diff options
Diffstat (limited to 'ipaserver')
-rw-r--r-- | ipaserver/install/bindinstance.py | 110 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 124 | ||||
-rw-r--r-- | ipaserver/install/ipa_replica_prepare.py | 82 |
3 files changed, 188 insertions, 128 deletions
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index cece85ec6..2e8836ec6 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -22,6 +22,7 @@ import os import pwd import netaddr import re +import sys import ldap @@ -250,7 +251,6 @@ def verify_reverse_zone(zone, ip_address): try: get_reverse_record_name(zone, ip_address) except ValueError: - print "Invalid reverse zone %s" % zone return False return True @@ -276,6 +276,8 @@ def read_reverse_zone(default, ip_address): return None if verify_reverse_zone(zone, ip_address): break + else: + print "Invalid reverse zone %s for IP address %s" % (zone, ip_address) return normalize_zone(zone) @@ -378,6 +380,48 @@ def zonemgr_callback(option, opt_str, value, parser): parser.values.zonemgr = value +def check_reverse_zones(ip_addresses, reverse_zones, options, unattended, search_reverse_zones=False): + reverse_asked = False + + ret_reverse_zones = [] + # check that there is IP address in every reverse zone + if reverse_zones: + for rz in reverse_zones: + for ip in ip_addresses: + if verify_reverse_zone(rz, ip): + ret_reverse_zones.append(normalize_zone(rz)) + break + else: + # no ip matching reverse zone found + sys.exit("There is no IP address matching reverse zone %s." % rz) + if not options.no_reverse: + # check that there is reverse zone for every IP + for ip in ip_addresses: + if search_reverse_zones and find_reverse_zone(str(ip)): + # reverse zone is already in LDAP + continue + for rz in ret_reverse_zones: + if verify_reverse_zone(rz, ip): + # reverse zone was entered by user + break + else: + # no reverse zone for ip found + if not reverse_asked: + if not unattended and not reverse_zones: + # user did not specify reverse_zone nor no_reverse + options.no_reverse = not create_reverse() + if options.no_reverse: + # user decided not to create reverse zone + return [] + reverse_asked = True + rz = get_reverse_zone_default(str(ip)) + if not unattended: + rz = read_reverse_zone(rz, str(ip)) + ret_reverse_zones.append(rz) + + return ret_reverse_zones + + class DnsBackup(object): def __init__(self, service): self.service = service @@ -437,11 +481,11 @@ class BindInstance(service.Service): self.named_user = None self.domain = None self.host = None - self.ip_address = None + self.ip_addresses = [] self.realm = None self.forwarders = None self.sub_dict = None - self.reverse_zone = None + self.reverse_zones = [] self.dm_password = dm_password if fstore: @@ -451,19 +495,19 @@ class BindInstance(service.Service): suffix = ipautil.dn_attribute_property('_suffix') - def setup(self, fqdn, ip_address, realm_name, domain_name, forwarders, ntp, - reverse_zone, named_user="named", zonemgr=None, + def setup(self, fqdn, ip_addresses, realm_name, domain_name, forwarders, ntp, + reverse_zones, named_user="named", zonemgr=None, ca_configured=None): self.named_user = named_user self.fqdn = fqdn - self.ip_address = ip_address + self.ip_addresses = ip_addresses self.realm = realm_name self.domain = domain_name self.forwarders = forwarders self.host = fqdn.split(".")[0] self.suffix = ipautil.realm_to_suffix(self.realm) self.ntp = ntp - self.reverse_zone = reverse_zone + self.reverse_zones = reverse_zones self.ca_configured = ca_configured if not zonemgr: @@ -509,8 +553,9 @@ class BindInstance(service.Service): # get a connection to the DS self.ldap_connect() - if installutils.record_in_hosts(self.ip_address, self.fqdn) is None: - installutils.add_record_to_hosts(self.ip_address, self.fqdn) + for ip_address in self.ip_addresses: + if installutils.record_in_hosts(str(ip_address), self.fqdn) is None: + installutils.add_record_to_hosts(str(ip_address), self.fqdn) # Make sure generate-rndc-key.sh runs before named restart self.step("generating rndc key file", self.__generate_rndc_key) @@ -520,8 +565,7 @@ class BindInstance(service.Service): if not dns_zone_exists(self.domain): self.step("setting up our zone", self.__setup_zone) - - if self.reverse_zone is not None: + if self.reverse_zones: self.step("setting up reverse zone", self.__setup_reverse_zone) self.step("setting up our own record", self.__add_self) @@ -574,18 +618,17 @@ class BindInstance(service.Service): else: optional_ntp = "" - addr = netaddr.IPAddress(self.ip_address) - if addr.version in (4, 6): - ipa_ca = "%s\t\t\tIN %s\t\t\t%s\n" % ( - IPA_CA_RECORD, - "A" if addr.version == 4 else "AAAA", - self.ip_address) - else: - ipa_ca = "" + ipa_ca = "" + for addr in self.ip_addresses: + if addr.version in (4, 6): + ipa_ca += "%s\t\t\tIN %s\t\t\t%s\n" % ( + IPA_CA_RECORD, + "A" if addr.version == 4 else "AAAA", + str(addr)) self.sub_dict = dict( FQDN=self.fqdn, - IP=self.ip_address, + IP=[str(ip) for ip in self.ip_addresses], DOMAIN=self.domain, HOST=self.host, REALM=self.realm, @@ -618,7 +661,8 @@ class BindInstance(service.Service): def __setup_reverse_zone(self): # Always use force=True as named is not set up yet - add_zone(self.reverse_zone, self.zonemgr, ns_hostname=api.env.host, + for reverse_zone in self.reverse_zones: + add_zone(reverse_zone, self.zonemgr, ns_hostname=api.env.host, dns_backup=self.dns_backup, force=True) def __add_master_records(self, fqdn, addrs): @@ -665,7 +709,7 @@ class BindInstance(service.Service): add_ptr_rr(reverse_zone, addr, fqdn) def __add_self(self): - self.__add_master_records(self.fqdn, [self.ip_address]) + self.__add_master_records(self.fqdn, self.ip_addresses) def __add_others(self): entries = self.admin_conn.get_entries( @@ -710,7 +754,7 @@ class BindInstance(service.Service): pass def __add_ipa_ca_record(self): - self.__add_ipa_ca_records(self.fqdn, [self.ip_address], + self.__add_ipa_ca_records(self.fqdn, self.ip_addresses, self.ca_configured) if self.first_instance: @@ -798,7 +842,17 @@ class BindInstance(service.Service): def __setup_resolv_conf(self): self.fstore.backup_file(RESOLV_CONF) - resolv_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n" + resolv_txt = "search "+self.domain+"\n" + + for ip_address in self.ip_addresses: + if ip_address.version == 4: + resolv_txt += "nameserver 127.0.0.1\n" + break + + for ip_address in self.ip_addresses: + if ip_address.version == 6: + resolv_txt += "nameserver ::1\n" + break try: resolv_fd = open(RESOLV_CONF, 'w') resolv_fd.seek(0) @@ -812,16 +866,16 @@ class BindInstance(service.Service): installutils.check_entropy() ipautil.run(['/usr/libexec/generate-rndc-key.sh']) - def add_master_dns_records(self, fqdn, ip_address, realm_name, domain_name, - reverse_zone, ntp=False, ca_configured=None): + def add_master_dns_records(self, fqdn, ip_addresses, realm_name, domain_name, + reverse_zones, ntp=False, ca_configured=None): self.fqdn = fqdn - self.ip_address = ip_address + self.ip_addresses = ip_addresses self.realm = realm_name self.domain = domain_name self.host = fqdn.split(".")[0] self.suffix = ipautil.realm_to_suffix(self.realm) self.ntp = ntp - self.reverse_zone = reverse_zone + self.reverse_zones = reverse_zones self.ca_configured = ca_configured self.first_instance = False self.zonemgr = 'hostmaster.%s' % self.domain diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 34ae30624..c8e1a8de9 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -222,6 +222,7 @@ def add_record_to_hosts(ip, host_name, conf_file=paths.HOSTS): hosts_fd.write(ip+'\t'+host_name+' '+host_name.split('.')[0]+'\n') hosts_fd.close() +# TODO: Remove when removing usage from ipa-adtrust-install 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) @@ -235,6 +236,22 @@ def read_ip_address(host_name, fstore): return ip_parsed +def read_ip_addresses(host_name, fstore): + ips = [] + print "Enter the IP address to use, or press Enter to finish." + while True: + ip = ipautil.user_input("Please provide the IP address to be used for this host name", allow_empty = True) + if not ip: + break + try: + ip_parsed = ipautil.CheckedIPAddress(ip, match_local=True) + except Exception, e: + print "Error: Invalid IP Address %s: %s" % (ip, e) + continue + ips.append(ip) + + return ips + def read_dns_forwarders(): addrs = [] if ipautil.user_input("Do you want to configure DNS forwarders?", True): @@ -426,7 +443,7 @@ def get_host_name(no_host_dns): verify_fqdn(hostname, no_host_dns) return hostname -def get_server_ip_address(host_name, fstore, unattended, options): +def get_server_ip_address(host_name, fstore, unattended, setup_dns, ip_addresses): # Check we have a public IP that is associated with the hostname try: hostaddr = resolve_host(host_name) @@ -441,74 +458,59 @@ def get_server_ip_address(host_name, fstore, unattended, options): ip_add_to_hosts = False - if len(hostaddr) > 1: - print >> sys.stderr, "The server hostname resolves to more than one address:" - for addr in hostaddr: - print >> sys.stderr, " %s" % addr + ips = [] + if len(hostaddr): + for ha in hostaddr: + try: + ips.append(ipautil.CheckedIPAddress(ha, match_local=True)) + except ValueError, e: + root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e)) - if options.ip_address: - if str(options.ip_address) not in hostaddr: - print >> sys.stderr, "Address passed in --ip-address did not match any resolved" - print >> sys.stderr, "address!" - sys.exit(1) - print "Selected IP address:", str(options.ip_address) - ip = options.ip_address + if not ips and not ip_addresses: + if not unattended: + ip_addresses = read_ip_addresses(host_name, fstore) + + if ip_addresses: + if setup_dns: + ips = ip_addresses else: - if unattended: - print >> sys.stderr, "Please use --ip-address option to specify the address" - sys.exit(1) + # all specified addresses was resolved for this host + if set(ip_addresses) <= set(ips): + ips = ip_addresses else: - ip = read_ip_address(host_name, fstore) - elif len(hostaddr) == 1: - try: - ip = ipautil.CheckedIPAddress(hostaddr[0], match_local=True) - except ValueError, e: - sys.exit("Invalid IP Address %s for %s: %s" % (hostaddr[0], host_name, unicode(e))) - else: - # hostname is not resolvable - ip = options.ip_address + print >>sys.stderr, "Error: the hostname resolves to IP address(es) that are different" + print >>sys.stderr, "from those provided on the command line. Please fix your DNS" + print >>sys.stderr, "or /etc/hosts file and restart the installation." + print >>sys.stderr, "Provided but not resolved address(es): %s" % \ + ", ".join(str(ip) for ip in (set(ip_addresses) - set(ips))) + sys.exit(1) ip_add_to_hosts = True - if ip is None: - print "Unable to resolve IP address for host name" - if unattended: - sys.exit(1) - - if options.ip_address: - if options.ip_address != 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." - sys.exit(1) - - ip = options.ip_address - - if ip is None: - ip = read_ip_address(host_name, fstore) - root_logger.debug("read ip_address: %s\n" % str(ip)) + if not ips: + print >> sys.stderr, "No usable IP address provided nor resolved." + sys.exit(1) - ip_address = str(ip) + for ip_address in ips: + # check /etc/hosts sanity, add a record when needed + hosts_record = record_in_hosts(str(ip_address)) - # check /etc/hosts sanity, add a record when needed - hosts_record = record_in_hosts(ip_address) + if hosts_record is None: + if ip_add_to_hosts: + print "Adding ["+str(ip_address)+" "+host_name+"] to your /etc/hosts file" + fstore.backup_file(paths.HOSTS) + add_record_to_hosts(str(ip_address), host_name) + else: + primary_host = hosts_record[1][0] + if primary_host != host_name: + print >>sys.stderr, "Error: there is already a record in /etc/hosts for IP address %s:" \ + % ip_address + print >>sys.stderr, hosts_record[0], " ".join(hosts_record[1]) + print >>sys.stderr, "Chosen hostname %s does not match configured canonical hostname %s" \ + % (host_name, primary_host) + print >>sys.stderr, "Please fix your /etc/hosts file and restart the installation." + sys.exit(1) - if hosts_record is None: - if ip_add_to_hosts or options.setup_dns: - print "Adding ["+ip_address+" "+host_name+"] to your /etc/hosts file" - fstore.backup_file(paths.HOSTS) - add_record_to_hosts(ip_address, host_name) - else: - primary_host = hosts_record[1][0] - if primary_host != host_name: - print >>sys.stderr, "Error: there is already a record in /etc/hosts for IP address %s:" \ - % ip_address - print >>sys.stderr, hosts_record[0], " ".join(hosts_record[1]) - print >>sys.stderr, "Chosen hostname %s does not match configured canonical hostname %s" \ - % (host_name, primary_host) - print >>sys.stderr, "Please fix your /etc/hosts file and restart the installation." - sys.exit(1) - - return ip + return ips def expand_replica_info(filename, password): """ diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py index 7768fd311..e27eb6dd4 100644 --- a/ipaserver/install/ipa_replica_prepare.py +++ b/ipaserver/install/ipa_replica_prepare.py @@ -54,9 +54,11 @@ class ReplicaPrepare(admintool.AdminTool): parser.add_option("-p", "--password", dest="password", help="Directory Manager password (for the existing master)") - parser.add_option("--ip-address", dest="ip_address", type="ip", + parser.add_option("--ip-address", dest="ip_addresses", type="ip", + action="append", default=[], help="add A and PTR records of the future replica") - parser.add_option("--reverse-zone", dest="reverse_zone", + parser.add_option("--reverse-zone", dest="reverse_zones", + action="append", default=[], help="the reverse DNS zone to use") parser.add_option("--no-reverse", dest="no_reverse", action="store_true", default=False, @@ -95,14 +97,14 @@ class ReplicaPrepare(admintool.AdminTool): super(ReplicaPrepare, self).validate_options(needs_root=True) installutils.check_server_configuration() - if not options.ip_address: - if options.reverse_zone: + if not options.ip_addresses: + if options.reverse_zones: self.option_parser.error("You cannot specify a --reverse-zone " "option without the --ip-address option") if options.no_reverse: self.option_parser.error("You cannot specify a --no-reverse " "option without the --ip-address option") - elif options.reverse_zone and options.no_reverse: + elif options.reverse_zones and options.no_reverse: self.option_parser.error("You cannot specify a --reverse-zone " "option together with --no-reverse") @@ -192,7 +194,7 @@ class ReplicaPrepare(admintool.AdminTool): except installutils.BadHostError, e: msg = str(e) if isinstance(e, installutils.HostLookupError): - if options.ip_address is None: + if not options.ip_addresses: if dns_container_exists( api.env.host, api.env.basedn, dm_password=self.dirman_password, @@ -206,7 +208,7 @@ class ReplicaPrepare(admintool.AdminTool): else: raise - if options.ip_address: + if options.ip_addresses: if not dns_container_exists(api.env.host, api.env.basedn, dm_password=self.dirman_password, ldapi=True, realm=api.env.realm): @@ -215,9 +217,19 @@ class ReplicaPrepare(admintool.AdminTool): "because DNS is not managed by IPA. Please create DNS " "record manually and then omit --ip-address option.") raise admintool.ScriptError("Cannot add DNS record") - if options.reverse_zone and not bindinstance.verify_reverse_zone( - options.reverse_zone, options.ip_address): - raise admintool.ScriptError("Invalid reverse zone") + + disconnect = False + if not api.Backend.ldap2.isconnected(): + api.Backend.ldap2.connect( + bind_dn=DN(('cn', 'Directory Manager')), + bind_pw=self.dirman_password) + disconnect = True + + options.reverse_zones = bindinstance.check_reverse_zones( + options.ip_addresses, options.reverse_zones, options, False, + True) + if disconnect: + api.Backend.ldap2.disconnect() if options.http_pkcs12: if options.http_pin is None: @@ -293,7 +305,7 @@ class ReplicaPrepare(admintool.AdminTool): finally: shutil.rmtree(self.top_dir) - if options.ip_address: + if options.ip_addresses: self.add_dns_records() if options.wait_for_dns: @@ -420,46 +432,38 @@ class ReplicaPrepare(admintool.AdminTool): options = self.options self.log.info("Adding DNS records for %s", self.replica_fqdn) - api.Backend.ldap2.connect( - bind_dn=DN(('cn', 'Directory Manager')), - bind_pw=self.dirman_password) - name, domain = self.replica_fqdn.split(".", 1) - ip = options.ip_address - ip_address = str(ip) - - if options.reverse_zone: - reverse_zone = bindinstance.normalize_zone(options.reverse_zone) - else: - reverse_zone = bindinstance.find_reverse_zone(ip) - if reverse_zone is None and not options.no_reverse: - reverse_zone = bindinstance.get_reverse_zone_default(ip) - + if not api.Backend.ldap2.isconnected(): + api.Backend.ldap2.connect( + bind_dn=DN(('cn', 'Directory Manager')), + bind_pw=self.dirman_password) try: add_zone(domain) except errors.PublicError, e: raise admintool.ScriptError( "Could not create forward DNS zone for the replica: %s" % e) - try: - add_fwd_rr(domain, name, ip_address) - except errors.PublicError, e: - raise admintool.ScriptError( - "Could not add forward DNS record for the replica: %s" % e) + for reverse_zone in options.reverse_zones: + self.log.info("Adding reverse zone %s", reverse_zone) + add_zone(reverse_zone) - if reverse_zone is not None: - self.log.info("Using reverse zone %s", reverse_zone) + for ip in options.ip_addresses: + ip_address = str(ip) try: - add_zone(reverse_zone) + add_fwd_rr(domain, name, ip_address) except errors.PublicError, e: raise admintool.ScriptError( - "Could not create reverse DNS zone for replica: %s" % e) - try: - add_ptr_rr(reverse_zone, ip_address, self.replica_fqdn) - except errors.PublicError, e: - raise admintool.ScriptError( - "Could not add reverse DNS record for the replica: %s" % e) + "Could not add forward DNS record for the replica: %s" % e) + + if not options.no_reverse: + reverse_zone = bindinstance.find_reverse_zone(ip) + try: + add_ptr_rr(reverse_zone, ip_address, self.replica_fqdn) + except errors.PublicError, e: + raise admintool.ScriptError( + "Could not add reverse DNS record for the replica: %s" + % e) def check_dns(self, replica_fqdn): """Return true if the replica hostname is resolvable""" |