[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Freeipa-devel] [PATCH] 3 Add ability to specify netmask with IP addresses during installation



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 <jcholast redhat com>
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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]