summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib/plugins')
-rw-r--r--ipalib/plugins/dns.py111
1 files changed, 93 insertions, 18 deletions
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index febd4d17c..e7ac58d23 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -31,7 +31,7 @@ from ipalib import Command
from ipalib.parameters import Flag, Bool, Int, Decimal, Str, StrEnum, Any
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
-from ipalib.util import (validate_zonemgr, normalize_zonemgr,
+from ipalib.util import (validate_zonemgr, normalize_zonemgr, normalize_zone,
validate_hostname, validate_dns_label, validate_domain_name,
get_dns_forward_zone_update_policy, get_dns_reverse_zone_update_policy,
get_reverse_zone_default, zone_is_reverse, REVERSE_DNS_ZONES)
@@ -72,8 +72,9 @@ ipa dnsrecord-mod --mx-rec="0 mx.example.com." --mx-preference=1
EXAMPLES:
Add new zone:
- ipa dnszone-add example.com --name-server=nameserver.example.com \\
- --admin-email=admin@example.com
+ ipa dnszone-add example.com --name-server=ns \\
+ --admin-email=admin@example.com \\
+ --ip-address=10.0.0.1
Add system permission that can be used for per-zone privilege delegation:
ipa dnszone-add-permission example.com
@@ -90,7 +91,7 @@ EXAMPLES:
Add new reverse zone specified by network IP address:
ipa dnszone-add --name-from-ip=80.142.15.0/24 \\
- --name-server=nameserver.example.com
+ --name-server=ns.example.com.
Add second nameserver for example.com:
ipa dnsrecord-add example.com @ --ns-rec=nameserver2.example.com
@@ -357,6 +358,8 @@ def _normalize_bind_aci(bind_acis):
return acis
def _bind_hostname_validator(ugettext, value):
+ if value == _dns_zone_record:
+ return
try:
# Allow domain name which is not fully qualified. These are supported
# in bind and then translated as <non-fqdn-name>.<domain>.
@@ -1500,7 +1503,9 @@ _dns_supported_record_types = tuple(record.rrtype for record in _dns_records \
if record.supported)
def check_ns_rec_resolvable(zone, name):
- if not name.endswith('.'):
+ if name == _dns_zone_record:
+ name = normalize_zone(zone)
+ elif not name.endswith('.'):
# this is a DNS name relative to the zone
zone = dns.name.from_text(zone)
name = unicode(dns.name.from_text(name, origin=zone))
@@ -1567,6 +1572,7 @@ class dnszone(LDAPObject):
cli_name='name_server',
label=_('Authoritative nameserver'),
doc=_('Authoritative nameserver domain name'),
+ normalizer=lambda value: value.lower(),
),
Str('idnssoarname',
_rname_validator,
@@ -1716,6 +1722,38 @@ class dnszone(LDAPObject):
def permission_name(self, zone):
return u"Manage DNS zone %s" % zone
+ def get_name_in_zone(self, zone, hostname):
+ """
+ Get name of a record that is to be added to a new zone. I.e. when
+ we want to add record "ipa.lab.example.com" in a zone "example.com",
+ this function should return "ipa.lab". Returns None when record cannot
+ be added to a zone
+ """
+ if hostname == _dns_zone_record:
+ # special case: @ --> zone name
+ return hostname
+
+ if hostname.endswith(u'.'):
+ hostname = hostname[:-1]
+ if zone.endswith(u'.'):
+ zone = zone[:-1]
+
+ hostname_parts = hostname.split(u'.')
+ zone_parts = zone.split(u'.')
+
+ dns_name = list(hostname_parts)
+ for host_part, zone_part in zip(reversed(hostname_parts),
+ reversed(zone_parts)):
+ if host_part != zone_part:
+ return None
+ dns_name.pop()
+
+ if not dns_name:
+ # hostname is directly in zone itself
+ return _dns_zone_record
+
+ return u'.'.join(dns_name)
+
api.register(dnszone)
@@ -1726,10 +1764,10 @@ class dnszone_add(LDAPCreate):
takes_options = LDAPCreate.takes_options + (
Flag('force',
label=_('Force'),
- doc=_('Force DNS zone creation even if nameserver not in DNS.'),
+ doc=_('Force DNS zone creation even if nameserver is not resolvable.'),
),
Str('ip_address?', _validate_ipaddr,
- doc=_('Add the nameserver to DNS with this IP address'),
+ doc=_('Add forward record for nameserver located in the created zone'),
),
)
@@ -1746,13 +1784,32 @@ class dnszone_add(LDAPCreate):
# NS record must contain domain name
if valid_ip(nameserver):
raise errors.ValidationError(name='name-server',
- error=unicode(_("Nameserver address is not a fully qualified domain name")))
+ error=_("Nameserver address is not a domain name"))
- if nameserver[-1] != '.':
- nameserver += '.'
+ nameserver_ip_address = options.get('ip_address')
+ normalized_zone = normalize_zone(keys[-1])
- if not 'ip_address' in options and not options['force']:
- check_ns_rec_resolvable(keys[0], nameserver)
+ if nameserver.endswith('.'):
+ record_in_zone = self.obj.get_name_in_zone(keys[-1], nameserver)
+ else:
+ record_in_zone = nameserver
+
+ if zone_is_reverse(normalized_zone):
+ if not nameserver.endswith('.'):
+ raise errors.ValidationError(name='name-server',
+ error=_("Nameserver for reverse zone cannot be "
+ "a relative DNS name"))
+ elif nameserver_ip_address:
+ raise errors.ValidationError(name='ip_address',
+ error=_("Nameserver DNS record is created for "
+ "for forward zones only"))
+ elif nameserver_ip_address and nameserver.endswith('.') and not record_in_zone:
+ raise errors.ValidationError(name='ip_address',
+ error=_("Nameserver DNS record is created only for "
+ "nameservers in current zone"))
+
+ if not nameserver_ip_address and not options['force']:
+ check_ns_rec_resolvable(keys[0], nameserver)
entry_attrs['nsrecord'] = nameserver
entry_attrs['idnssoamname'] = nameserver
@@ -1760,12 +1817,16 @@ class dnszone_add(LDAPCreate):
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
- if 'ip_address' in options:
- nameserver = entry_attrs['idnssoamname'][0][:-1] # ends with a dot
- nsparts = nameserver.split('.')
- add_forward_record('.'.join(nsparts[1:]),
- nsparts[0],
- options['ip_address'])
+ nameserver_ip_address = options.get('ip_address')
+ if nameserver_ip_address:
+ nameserver = entry_attrs['idnssoamname'][0]
+ if nameserver.endswith('.'):
+ dns_record = self.obj.get_name_in_zone(keys[-1], nameserver)
+ else:
+ dns_record = nameserver
+ add_forward_record(keys[-1],
+ dns_record,
+ nameserver_ip_address)
return dn
@@ -1789,8 +1850,22 @@ api.register(dnszone_del)
class dnszone_mod(LDAPUpdate):
__doc__ = _('Modify DNS zone (SOA record).')
+ takes_options = LDAPUpdate.takes_options + (
+ Flag('force',
+ label=_('Force'),
+ doc=_('Force nameserver change even if nameserver not in DNS'),
+ ),
+ )
+
has_output_params = LDAPUpdate.has_output_params + dnszone_output_params
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ nameserver = entry_attrs.get('idnssoamname')
+ if nameserver and nameserver != _dns_zone_record and not options['force']:
+ check_ns_rec_resolvable(keys[0], nameserver)
+
+ return dn
+
api.register(dnszone_mod)