From bb56285f6b350fcdd7b22f6d6c4fb69da5ce438d Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 31 Jan 2011 15:30:43 +0100 Subject: IPv6 enhancements * Make host-add, host-del and reverse zone creation IPv6 aware * Make Bind listen on IPv6 interfaces, too https://fedorahosted.org/freeipa/ticket/398 --- install/share/bind.named.conf.template | 3 ++ ipalib/plugins/host.py | 61 ++++++++++++++++++++-------------- ipaserver/install/bindinstance.py | 33 +++++++++++------- ipaserver/install/installutils.py | 4 +-- 4 files changed, 62 insertions(+), 39 deletions(-) diff --git a/install/share/bind.named.conf.template b/install/share/bind.named.conf.template index 447c50ccb..71facbaf7 100644 --- a/install/share/bind.named.conf.template +++ b/install/share/bind.named.conf.template @@ -1,4 +1,7 @@ options { + // turns on IPv6 for port 53, IPv4 is on by default for all ifaces + listen-on-v6 {any;}; + // Put files that named is allowed to write in the data/ directory: directory "/var/named"; // the default dump-file "data/cache_dump.db"; diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index e3f38fc84..42ffe750c 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -85,6 +85,7 @@ from ipalib.plugins.service import set_certificate_attrs from ipalib.plugins.service import make_pem, check_writable_file from ipalib.plugins.service import write_certificate from ipalib.plugins.dns import dns_container_exists, _record_types +from ipalib.plugins.dns import add_forward_record from ipalib import _, ngettext from ipalib import x509 from ipapython.ipautil import ipa_generate_password @@ -103,6 +104,32 @@ def validate_host(ugettext, fqdn): return _('Fully-qualified hostname required') return None +def is_forward_record(zone, str_address): + addr = netaddr.IPAddress(str_address) + if addr.version == 4: + result = api.Command['dnsrecord_find'](zone, arecord=str_address) + elif addr.version == 6: + result = api.Command['dnsrecord_find'](zone, aaarecord=str_address) + else: + raise ValueError('Invalid address family') + + return result['count'] > 0 + +def remove_fwd_ptr(ipaddr, host, domain, recordtype): + api.log.debug('deleting ipaddr %s' % ipaddr) + revzone, revname = get_reverse_zone(ipaddr) + try: + delkw = { 'ptrrecord' : "%s.%s." % (host, domain) } + api.Command['dnsrecord_del'](revzone, revname, **delkw) + except errors.NotFound: + pass + + try: + delkw = { recordtype : ipaddr } + api.Command['dnsrecord_del'](domain, host, **delkw) + except errors.NotFound: + pass + host_output_params = ( Flag('has_keytab', label=_('Keytab'), @@ -309,8 +336,7 @@ class host_add(LDAPCreate): except errors.NotFound: pass else: - result = api.Command['dnsrecord_find'](domain, arecord=options['ip_address']) - if result['count'] > 0: + if is_forward_record(domain, options['ip_address']): raise errors.DuplicateEntry(message=u'This IP address is already assigned.') if not options.get('force', False) and not 'ip_address' in options: util.validate_host_dns(self.log, keys[-1]) @@ -347,15 +373,8 @@ class host_add(LDAPCreate): if 'ip_address' in options and dns_container_exists(ldap): parts = keys[-1].split('.') domain = unicode('.'.join(parts[1:])) - if ':' in options['ip_address']: - addkw = { 'aaaarecord' : options['ip_address'] } - else: - addkw = { 'arecord' : options['ip_address'] } - try: - api.Command['dnsrecord_add'](domain, parts[0], **addkw) - except errors.EmptyModlist: - # the entry already exists and matches - pass + + add_forward_record(domain, parts[0], options['ip_address']) if not options.get('no_reverse', False): revzone, revname = get_reverse_zone(options['ip_address']) @@ -444,24 +463,16 @@ class host_del(LDAPDelete): records = api.Command['dnsrecord_find'](domain, idnsname=parts[0])['result'] for record in records: if 'arecord' in record: - ipaddr = record['arecord'][0] - self.debug('deleting ipaddr %s' % ipaddr) - revzone, revname = get_reverse_zone(ipaddr) - try: - delkw = { 'ptrrecord' : fqdn+'.' } - api.Command['dnsrecord_del'](revzone, revname, **delkw) - except errors.NotFound: - pass - try: - delkw = { 'arecord' : ipaddr } - api.Command['dnsrecord_del'](domain, parts[0], **delkw) - except errors.NotFound: - pass + remove_fwd_ptr(record['arecord'][0], parts[0], + domain, 'arecord') + if 'aaaarecord' in record: + remove_fwd_ptr(record['aaaarecord'][0], parts[0], + domain, 'aaaarecord') else: # Try to delete all other record types too _attribute_types = [str('%srecord' % t.lower()) for t in _record_types] for attr in _attribute_types: - if attr != 'arecord' and attr in record: + if attr not in ['arecord', 'aaaarecord'] and attr in record: for i in xrange(len(record[attr])): if (record[attr][i].endswith(parts[0]) or record[attr][i].endswith(fqdn+'.')): diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index a2989e1de..1f35dc1ff 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -21,6 +21,7 @@ import tempfile import os import pwd import logging +import netaddr import installutils import ldap @@ -97,11 +98,15 @@ def dns_container_exists(fqdn, suffix): return ret -def get_reverse_zone(ip_address): - tmp = ip_address.split(".") - tmp.reverse() - name = tmp.pop(0) - zone = ".".join(tmp) + ".in-addr.arpa" +def get_reverse_zone(ip_address_str): + ip = netaddr.IPAddress(ip_address_str) + if ip.version == 4: + name, dot, zone = ip.reverse_dns.partition('.') + elif ip.version == 6: + name = '.'.join(ip.reverse_dns.split('.')[:8]) + zone = '.'.join(ip.reverse_dns.split('.')[8:]) + else: + raise ValueError('Bad address format?') return zone, name @@ -118,7 +123,7 @@ def dns_zone_exists(name): def add_zone(name, zonemgr=None, dns_backup=None, nsaddr=None, update_policy=None): if not update_policy: - update_policy = "grant %s krb5-self * A;" % api.env.realm + update_policy = "grant %(realm)s krb5-self * A; grant %(realm)s krb5-self * AAAA;" % dict(realm=api.env.realm) try: api.Command.dnszone_add(unicode(name), @@ -160,6 +165,13 @@ def add_rr(zone, name, type, rdata, dns_backup=None, **kwargs): if dns_backup: dns_backup.add(zone, type, name, rdata) +def add_fwd_rr(zone, host, ip_address): + addr = netaddr.IPAddress(ip_address) + if addr.version == 4: + add_rr(zone, host, "A", 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) add_rr(zone, name, "PTR", fqdn+".", dns_backup) @@ -264,11 +276,7 @@ class BindInstance(service.Service): else: self.zonemgr = 'root.%s.%s' % (self.host, self.domain) - tmp = ip_address.split(".") - tmp.reverse() - - self.reverse_host = tmp.pop(0) - self.reverse_subnet = ".".join(tmp) + self.reverse_subnet, self.reverse_host = get_reverse_zone(ip_address) self.__setup_sub_dict() @@ -357,7 +365,6 @@ class BindInstance(service.Service): def __add_self(self): zone = self.domain resource_records = ( - (self.host, "A", self.ip_address), ("_ldap._tcp", "SRV", "0 100 389 %s" % self.host), ("_kerberos", "TXT", self.realm), ("_kerberos._tcp", "SRV", "0 100 88 %s" % self.host), @@ -376,6 +383,8 @@ class BindInstance(service.Service): if self.ntp: add_rr(zone, "_ntp._udp", "SRV", "0 100 123 %s" % self.host) + # 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) diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 05d397eea..314c26c7f 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -133,12 +133,12 @@ def verify_fqdn(host_name,no_host_dns=False): # Verify that it is a DNS A or AAAA record rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_A) - if len(rs) > 0: + if len([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0: verify_dns_records(host_name, rs, resaddr, 'ipv4') return rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA) - if len(rs) > 0: + if len([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0: verify_dns_records(host_name, rs, resaddr, 'ipv6') return else: -- cgit