This patch enables the user to specify netmask/prefix length with IP
addresses (see
http://packages.python.org/netaddr/netaddr.ip.IPNetwork-class.html)
during installation for proper DNS reverse zone setup.
https://fedorahosted.org/freeipa/ticket/910
--
Jan Cholasta
>From 800eaf3c5caa3c7fcde363124373978916671820 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <[email protected]>
Date: Mon, 28 Mar 2011 12:34:15 +0200
Subject: [PATCH] Add ability to specify netmask/prefix length with IP addresses during install for proper DNS reverse zone setup.
ticket 910
---
install/tools/ipa-dns-install | 8 +++--
install/tools/ipa-replica-install | 4 ++-
install/tools/ipa-replica-prepare | 15 +++++---
install/tools/ipa-server-install | 32 ++++++++-----------
ipaserver/install/bindinstance.py | 49 +++++++++++++++++++----------
ipaserver/install/installutils.py | 62 ++++++++++++++++++++++++++++--------
6 files changed, 111 insertions(+), 59 deletions(-)
diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index aac85bf..69b05bc 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -131,11 +131,13 @@ def main():
ip_address = options.ip_address
else:
ip_address = resolve_host(api.env.host)
- if not ip_address or not verify_ip_address(ip_address):
+ ip = parse_ip_address(ip_address)
+ if not verify_ip_address(ip):
if options.unattended:
sys.exit("Unable to resolve IP address for host name")
else:
- ip_address = read_ip_address(api.env.host, fstore)
+ ip = read_ip_address(api.env.host, fstore)
+ ip_address = str(ip)
logging.debug("will use ip_address: %s\n", ip_address)
if options.no_forwarders:
@@ -180,7 +182,7 @@ def main():
create_reverse = not options.no_reverse
elif not options.no_reverse:
create_reverse = bindinstance.create_reverse()
- bind.setup(api.env.host, ip_address, api.env.realm, api.env.domain, dns_forwarders, conf_ntp, create_reverse, zonemgr=options.zonemgr)
+ bind.setup(api.env.host, ip_address, ip.prefixlen, api.env.realm, api.env.domain, dns_forwarders, conf_ntp, create_reverse, zonemgr=options.zonemgr)
if bind.dm_password:
api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", bind_pw=bind.dm_password)
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 2bc9a17..1693da9 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -284,6 +284,8 @@ def install_bind(config, options):
ip_address = resolve_host(config.host_name)
if not ip_address:
sys.exit("Unable to resolve IP address for host name")
+ ip = installutils.parse_ip_address(ip_address)
+ ip_address = str(ip)
create_reverse = True
if options.unattended:
@@ -293,7 +295,7 @@ def install_bind(config, options):
# In interactive mode, if the flag was not explicitly specified, ask the user
create_reverse = bindinstance.create_reverse()
- bind.setup(config.host_name, ip_address, config.realm_name,
+ bind.setup(config.host_name, ip_address, ip.prefixlen, config.realm_name,
config.domain_name, forwarders, options.conf_ntp, create_reverse)
bind.create_instance()
diff --git a/install/tools/ipa-replica-prepare b/install/tools/ipa-replica-prepare
index e912235..63dc4ca 100755
--- a/install/tools/ipa-replica-prepare
+++ b/install/tools/ipa-replica-prepare
@@ -28,7 +28,7 @@ from optparse import OptionParser
from ipapython import ipautil
from ipaserver.install import bindinstance, dsinstance, installutils, certs
-from ipaserver.install.bindinstance import add_zone, add_reverse_zone, add_rr, add_ptr_rr
+from ipaserver.install.bindinstance import add_zone, add_reverse_zone, add_fwd_rr, add_ptr_rr
from ipaserver.install.replication import check_replication_plugin, enable_replication_version_checking
from ipaserver.plugins.ldap2 import ldap2
from ipapython import version
@@ -50,7 +50,7 @@ def parse_options():
help="PIN for the Apache Server PKCS#12 file")
parser.add_option("--pkinit_pin", dest="pkinit_pin",
help="PIN for the KDC pkinit PKCS#12 file")
- parser.add_option("-p", "--password", dest="password",
+ parser.add_option("-p", "--password", dest="password",
help="Directory Manager (existing master) password")
parser.add_option("--ip-address", dest="ip_address",
help="Add A and PTR records of the future replica")
@@ -425,10 +425,13 @@ def main():
name = domain.pop(0)
domain = ".".join(domain)
- zone = add_zone(domain, nsaddr=options.ip_address)
- add_rr(zone, name, "A", options.ip_address)
- add_reverse_zone(options.ip_address)
- add_ptr_rr(options.ip_address, replica_fqdn)
+ ip = installutils.parse_ip_address(options.ip_address)
+ ip_address = str(ip)
+
+ zone = add_zone(domain, nsaddr=ip_address)
+ add_fwd_rr(zone, name, ip_address)
+ add_reverse_zone(ip_address, ip.prefixlen)
+ add_ptr_rr(ip_address, ip.prefixlen, replica_fqdn)
try:
if not os.geteuid()==0:
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index f3a01e8..854c9a4 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -603,37 +603,33 @@ def main():
domain_name = domain_name.lower()
# Check we have a public IP that is associated with the hostname
- ip = resolve_host(host_name)
- if ip is None:
- if options.ip_address:
- ip = options.ip_address
- if ip is None and options.unattended:
+ ip = parse_ip_address(resolve_host(host_name))
+ ip_opt = parse_ip_address(options.ip_address)
+ if not ip and ip_opt:
+ ip = ip_opt
+ if not ip and options.unattended:
sys.exit("Unable to resolve IP address for host name")
if not verify_ip_address(ip):
- ip = ""
+ ip = parse_ip_address(None)
if options.unattended:
sys.exit(1)
- if options.ip_address and options.ip_address != ip:
- if options.setup_dns:
- if not verify_ip_address(options.ip_address):
- return 1
- ip = options.ip_address
- else:
+ if ip_opt:
+ if ip_opt != ip and not options.setup_dns:
print >>sys.stderr, "Error: the hostname resolves to an IP address that is different"
print >>sys.stderr, "from the one provided on the command line. Please fix your DNS"
print >>sys.stderr, "or /etc/hosts file and restart the installation."
return 1
- if options.unattended:
- if not ip:
- sys.exit("Unable to resolve IP address")
+ ip = ip_opt
+ if not verify_ip_address(ip):
+ return 1
if not ip:
ip = read_ip_address(host_name, fstore)
- logging.debug("read ip_address: %s\n" % ip)
- ip_address = ip
+ logging.debug("read ip_address: %s\n" % str(ip))
+ ip_address = str(ip)
print "The IPA Master Server will be configured with"
print "Hostname: " + host_name
@@ -900,7 +896,7 @@ def main():
# In interactive mode, if the flag was not explicitly specified, ask the user
create_reverse = bindinstance.create_reverse()
- bind.setup(host_name, ip_address, realm_name, domain_name, dns_forwarders, options.conf_ntp, create_reverse, zonemgr=options.zonemgr)
+ bind.setup(host_name, ip_address, ip.prefixlen, realm_name, domain_name, dns_forwarders, options.conf_ntp, create_reverse, zonemgr=options.zonemgr)
if options.setup_dns:
api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", bind_pw=dm_password)
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index e005653..5d556e6 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -98,16 +98,20 @@ def dns_container_exists(fqdn, suffix):
return ret
-def get_reverse_zone(ip_address_str):
+def get_reverse_zone(ip_address_str, ip_prefix_len):
ip = netaddr.IPAddress(ip_address_str)
+ items = ip.reverse_dns.split('.')
+
if ip.version == 4:
- name, dot, zone = ip.reverse_dns.partition('.')
+ pos = 4 - ip_prefix_len / 8
elif ip.version == 6:
- name = '.'.join(ip.reverse_dns.split('.')[:8])
- zone = '.'.join(ip.reverse_dns.split('.')[8:])
+ pos = 32 - ip_prefix_len / 4
else:
raise ValueError('Bad address format?')
+ name = '.'.join(items[:pos])
+ zone = '.'.join(items[pos:])
+
return unicode(zone), unicode(name)
def dns_zone_exists(name):
@@ -138,8 +142,8 @@ def add_zone(name, zonemgr=None, dns_backup=None, nsaddr=None, update_policy=Non
add_rr(name, "@", "NS", api.env.host+'.', dns_backup, force=True)
return name
-def add_reverse_zone(ip_address, update_policy=None, dns_backup=None):
- zone, name = get_reverse_zone(ip_address)
+def add_reverse_zone(ip_address, ip_prefix_len, update_policy=None, dns_backup=None):
+ zone, name = get_reverse_zone(ip_address, ip_prefix_len)
if not update_policy:
update_policy = "grant %s krb5-subdomain %s. PTR;" % (api.env.realm, zone)
try:
@@ -172,8 +176,8 @@ def add_fwd_rr(zone, host, ip_address):
elif addr.version == 6:
add_rr(zone, host, "AAAA", ip_address)
-def add_ptr_rr(ip_address, fqdn, dns_backup=None):
- zone, name = get_reverse_zone(ip_address)
+def add_ptr_rr(ip_address, ip_prefix_len, fqdn, dns_backup=None):
+ zone, name = get_reverse_zone(ip_address, ip_prefix_len)
add_rr(zone, name, "PTR", fqdn+".", dns_backup)
def del_rr(zone, name, type, rdata):
@@ -249,6 +253,7 @@ class BindInstance(service.Service):
self.domain = None
self.host = None
self.ip_address = None
+ self.ip_prefix_len = None
self.realm = None
self.forwarders = None
self.sub_dict = None
@@ -259,10 +264,11 @@ class BindInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
- def setup(self, fqdn, ip_address, realm_name, domain_name, forwarders, ntp, create_reverse, named_user="named", zonemgr=None):
+ def setup(self, fqdn, ip_address, ip_prefix_len, realm_name, domain_name, forwarders, ntp, create_reverse, named_user="named", zonemgr=None):
self.named_user = named_user
self.fqdn = fqdn
self.ip_address = ip_address
+ self.ip_prefix_len = ip_prefix_len
self.realm = realm_name
self.domain = domain_name
self.forwarders = forwarders
@@ -386,11 +392,11 @@ class BindInstance(service.Service):
# Add forward and reverse records to self
add_fwd_rr(zone, self.host, self.ip_address)
- if dns_zone_exists(get_reverse_zone(self.ip_address)[0]):
- add_ptr_rr(self.ip_address, self.fqdn)
+ if dns_zone_exists(get_reverse_zone(self.ip_address, self.ip_prefix_len)[0]):
+ add_ptr_rr(self.ip_address, self.ip_prefix_len, self.fqdn)
def __setup_reverse_zone(self):
- add_reverse_zone(self.ip_address, dns_backup=self.dns_backup)
+ add_reverse_zone(self.ip_address, self.ip_prefix_len, dns_backup=self.dns_backup)
def __setup_principal(self):
dns_principal = "DNS/" + self.fqdn + "@" + self.realm
@@ -477,14 +483,23 @@ class BindInstance(service.Service):
for (record, type, rdata) in resource_records:
del_rr(zone, record, type, rdata)
- areclist = get_rr(zone, host, "A")
- if len(areclist) != 0:
- for rdata in areclist:
- del_rr(zone, host, "A", rdata)
+ areclist = [("A", x) for x in get_rr(zone, host, "A")] + [("AAAA", x) for x in get_rr(zone, host, "AAAA")]
+ for (type, rdata) in areclist:
+ del_rr(zone, host, type, rdata)
- rzone, record = get_reverse_zone(rdata)
+ ip = netaddr.IPAddress(rdata)
+ if ip.version == 4:
+ prefixrange = reversed(range(0, 32, 8))
+ elif ip.version == 6:
+ prefixrange = reversed(range(0, 128, 4))
+ else:
+ continue
+
+ for prefix in prefixrange:
+ rzone, record = get_reverse_zone(rdata, prefix)
if dns_zone_exists(rzone):
del_rr(rzone, record, "PTR", fqdn+".")
+ break
def uninstall(self):
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 61d53a2..0e5692b 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -148,17 +148,45 @@ def verify_fqdn(host_name,no_host_dns=False):
else:
print "Warning: Hostname (%s) not found in DNS" % host_name
-def verify_ip_address(ip):
- is_ok = True
- try:
- socket.inet_pton(socket.AF_INET, ip)
- except:
+class _IPAddressWithPrefix(netaddr.IPAddress):
+ def __init__(self, addr='0/0'):
+ if isinstance(addr, _IPAddressWithPrefix):
+ super(_IPAddressWithPrefix, self).__init__(addr)
+ self.prefixlen = addr.prefixlen
+ return
+
try:
- socket.inet_pton(socket.AF_INET6, ip)
+ ip = netaddr.IPAddress(addr)
+ if ip.version == 4:
+ net = netaddr.IPNetwork(netaddr.cidr_abbrev_to_verbose(str(ip)))
+ self.prefixlen = net.prefixlen
+ elif ip.version == 6:
+ self.prefixlen = 64
+ addr = ip
except:
- print "Unable to verify IP address"
- is_ok = False
- return is_ok
+ net = netaddr.IPNetwork(addr)
+ self.prefixlen = net.prefixlen
+ addr = net.ip
+
+ super(_IPAddressWithPrefix, self).__init__(addr)
+
+def parse_ip_address(ip):
+ try:
+ ipaddr = _IPAddressWithPrefix(ip)
+ except:
+ return _IPAddressWithPrefix()
+
+ if ipaddr.version not in (4, 6):
+ return _IPAddressWithPrefix()
+
+ return ipaddr
+
+def verify_ip_address(ip):
+ if parse_ip_address(ip):
+ return True
+
+ print "Unable to verify IP address"
+ return False
def record_in_hosts(ip, host_name, file="/etc/hosts"):
hosts = open(file, 'r').readlines()
@@ -191,19 +219,21 @@ def add_record_to_hosts(ip, host_name, file="/etc/hosts"):
def read_ip_address(host_name, fstore):
while True:
ip = ipautil.user_input("Please provide the IP address to be used for this host name", allow_empty = False)
+ ip_parsed = parse_ip_address(ip)
- if ip == "127.0.0.1" or ip == "::1":
+ if ip_parsed.is_loopback():
print "The IPA Server can't use localhost as a valid IP"
continue
- if verify_ip_address(ip):
+ if verify_ip_address(ip_parsed):
break
+ ip = str(ip_parsed)
print "Adding ["+ip+" "+host_name+"] to your /etc/hosts file"
fstore.backup_file("/etc/hosts")
add_record_to_hosts(ip, host_name)
- return ip
+ return ip_parsed
def read_dns_forwarders():
addrs = []
@@ -215,10 +245,14 @@ def read_dns_forwarders():
allow_empty=True)
if not ip:
break
- if ip == "127.0.0.1" or ip == "::1":
+
+ ip_parsed = parse_ip_address(ip)
+ ip = str(ip_parsed)
+
+ if ip_parsed.is_loopback():
print "You cannot use localhost as a DNS forwarder"
continue
- if not verify_ip_address(ip):
+ if not verify_ip_address(ip_parsed):
print "DNS forwarder %s not added" % ip
continue
--
1.7.4
_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel