mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-11 00:31:56 -06:00
Fix ipa-server-install for dual NICs
A server may have 2 or more NICs and its hostname may thus resolve to 2 and more forward addresses. IP address checks in install scripts does not expect this setup and may fail or crash. This script adds a support for multiple forward addresses for a hostname. The install scripts do not crash now. When one IP address is needed, user is asked to choose from all detected server IP addresses. https://fedorahosted.org/freeipa/ticket/2154
This commit is contained in:
parent
f7b4eb6a09
commit
6141919fba
@ -147,7 +147,26 @@ def main():
|
|||||||
else:
|
else:
|
||||||
hostaddr = resolve_host(api.env.host)
|
hostaddr = resolve_host(api.env.host)
|
||||||
try:
|
try:
|
||||||
ip = hostaddr and ipautil.CheckedIPAddress(hostaddr, match_local=True)
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
if options.unattended:
|
||||||
|
print >> sys.stderr, "Please use --ip-address option to specify the address"
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
ip = read_ip_address(api.env.host, fstore)
|
||||||
|
else:
|
||||||
|
ip = hostaddr and ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Error: Invalid IP Address %s: %s" % (ip, e)
|
print "Error: Invalid IP Address %s: %s" % (ip, e)
|
||||||
ip = None
|
ip = None
|
||||||
|
@ -237,7 +237,7 @@ class PortResponder(threading.Thread):
|
|||||||
def port_check(host, port_list):
|
def port_check(host, port_list):
|
||||||
ip = installutils.resolve_host(host)
|
ip = installutils.resolve_host(host)
|
||||||
|
|
||||||
if ip is None:
|
if not ip:
|
||||||
raise RuntimeError("Port check failed! Unable to resolve host name '%s'" % host)
|
raise RuntimeError("Port check failed! Unable to resolve host name '%s'" % host)
|
||||||
|
|
||||||
failed_ports = []
|
failed_ports = []
|
||||||
|
@ -200,27 +200,22 @@ def install_bind(config, options):
|
|||||||
else:
|
else:
|
||||||
forwarders = ()
|
forwarders = ()
|
||||||
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
||||||
ip_address = resolve_host(config.host_name)
|
|
||||||
if not ip_address:
|
|
||||||
sys.exit("Unable to resolve IP address for host name")
|
|
||||||
ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
|
|
||||||
ip_address = str(ip)
|
|
||||||
|
|
||||||
if options.reverse_zone:
|
if options.reverse_zone:
|
||||||
if not bindinstance.verify_reverse_zone(options.reverse_zone, ip):
|
if not bindinstance.verify_reverse_zone(options.reverse_zone, config.ip):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
reverse_zone = bindinstance.normalize_zone(options.reverse_zone)
|
reverse_zone = bindinstance.normalize_zone(options.reverse_zone)
|
||||||
else:
|
else:
|
||||||
reverse_zone = bindinstance.find_reverse_zone(ip)
|
reverse_zone = bindinstance.find_reverse_zone(config.ip)
|
||||||
if reverse_zone is None and not options.no_reverse:
|
if reverse_zone is None and not options.no_reverse:
|
||||||
reverse_zone = bindinstance.get_reverse_zone_default(ip)
|
reverse_zone = bindinstance.get_reverse_zone_default(config.ip)
|
||||||
if not options.unattended and bindinstance.create_reverse():
|
if not options.unattended and bindinstance.create_reverse():
|
||||||
reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip)
|
reverse_zone = bindinstance.read_reverse_zone(reverse_zone, config.ip)
|
||||||
|
|
||||||
if reverse_zone is not None:
|
if reverse_zone is not None:
|
||||||
print "Using reverse zone %s" % reverse_zone
|
print "Using reverse zone %s" % reverse_zone
|
||||||
|
|
||||||
bind.setup(config.host_name, ip_address, config.realm_name,
|
bind.setup(config.host_name, config.ip_address, config.realm_name,
|
||||||
config.domain_name, forwarders, options.conf_ntp, reverse_zone)
|
config.domain_name, forwarders, options.conf_ntp, reverse_zone)
|
||||||
bind.create_instance()
|
bind.create_instance()
|
||||||
|
|
||||||
@ -240,14 +235,9 @@ def install_dns_records(config, options):
|
|||||||
bind_pw=config.dirman_password,
|
bind_pw=config.dirman_password,
|
||||||
tls_cacertfile=CACERT)
|
tls_cacertfile=CACERT)
|
||||||
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
||||||
ip_address = resolve_host(config.host_name)
|
reverse_zone = bindinstance.find_reverse_zone(config.ip)
|
||||||
if not ip_address:
|
|
||||||
sys.exit("Unable to resolve IP address for host name")
|
|
||||||
ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
|
|
||||||
ip_address = str(ip)
|
|
||||||
reverse_zone = bindinstance.find_reverse_zone(ip)
|
|
||||||
|
|
||||||
bind.add_master_dns_records(config.host_name, ip_address,
|
bind.add_master_dns_records(config.host_name, config.ip_address,
|
||||||
config.realm_name, config.domain_name,
|
config.realm_name, config.domain_name,
|
||||||
reverse_zone, options.conf_ntp)
|
reverse_zone, options.conf_ntp)
|
||||||
|
|
||||||
@ -341,7 +331,8 @@ def main():
|
|||||||
replica_conn_check(config.master_host_name, config.host_name, config.realm_name, options.setup_ca, options.admin_password)
|
replica_conn_check(config.master_host_name, config.host_name, config.realm_name, options.setup_ca, options.admin_password)
|
||||||
|
|
||||||
# check replica host IP resolution
|
# check replica host IP resolution
|
||||||
ip = installutils.get_server_ip_address(config.host_name, fstore, True, options)
|
config.ip = installutils.get_server_ip_address(config.host_name, fstore, True, options)
|
||||||
|
config.ip_address = str(config.ip)
|
||||||
|
|
||||||
# Create the management framework config file
|
# Create the management framework config file
|
||||||
# Note: We must do this before bootstraping and finalizing ipalib.api
|
# Note: We must do this before bootstraping and finalizing ipalib.api
|
||||||
|
@ -40,6 +40,11 @@ DNS_T_AAAA = 28
|
|||||||
DNS_T_SRV = 33
|
DNS_T_SRV = 33
|
||||||
DNS_T_ANY = 255
|
DNS_T_ANY = 255
|
||||||
|
|
||||||
|
DNS_S_QUERY = 1
|
||||||
|
DNS_S_ANSWER = 2
|
||||||
|
DNS_S_AUTHORITY = 3
|
||||||
|
DNS_S_ADDITIONAL = 4
|
||||||
|
|
||||||
DEBUG_DNSCLIENT = False
|
DEBUG_DNSCLIENT = False
|
||||||
|
|
||||||
class DNSQueryHeader:
|
class DNSQueryHeader:
|
||||||
@ -105,6 +110,7 @@ class DNSResult:
|
|||||||
self.dns_ttl = 0
|
self.dns_ttl = 0
|
||||||
self.dns_rlength = 0
|
self.dns_rlength = 0
|
||||||
self.rdata = None
|
self.rdata = None
|
||||||
|
self.section = None
|
||||||
|
|
||||||
def unpack(self, data):
|
def unpack(self, data):
|
||||||
(self.dns_type, self.dns_class, self.dns_ttl,
|
(self.dns_type, self.dns_class, self.dns_ttl,
|
||||||
@ -398,47 +404,51 @@ def dnsParseResults(results):
|
|||||||
print "Queried for '%s', class = %d, type = %d." % (label,
|
print "Queried for '%s', class = %d, type = %d." % (label,
|
||||||
qq.dns_class, qq.dns_type)
|
qq.dns_class, qq.dns_type)
|
||||||
|
|
||||||
for i in xrange(header.dns_ancount + header.dns_nscount + header.dns_arcount):
|
for (rec_count, section_id) in ((header.dns_ancount, DNS_S_ANSWER),
|
||||||
(rest, label) = dnsParseLabel(rest, results)
|
(header.dns_nscount, DNS_S_AUTHORITY),
|
||||||
if label is None:
|
(header.dns_arcount, DNS_S_ADDITIONAL)):
|
||||||
return []
|
for i in xrange(rec_count):
|
||||||
|
(rest, label) = dnsParseLabel(rest, results)
|
||||||
|
if label is None:
|
||||||
|
return []
|
||||||
|
|
||||||
rr = DNSResult()
|
rr = DNSResult()
|
||||||
|
|
||||||
rr.dns_name = label
|
rr.dns_name = label
|
||||||
|
rr.section = section_id
|
||||||
|
|
||||||
if len(rest) < rr.size():
|
if len(rest) < rr.size():
|
||||||
return []
|
return []
|
||||||
|
|
||||||
rr.unpack(rest)
|
rr.unpack(rest)
|
||||||
|
|
||||||
rest = rest[rr.size():]
|
|
||||||
|
|
||||||
if DEBUG_DNSCLIENT:
|
rest = rest[rr.size():]
|
||||||
print "Answer %d for '%s', class = %d, type = %d, ttl = %d." % (i,
|
|
||||||
rr.dns_name, rr.dns_class, rr.dns_type,
|
|
||||||
rr.dns_ttl)
|
|
||||||
|
|
||||||
if len(rest) < rr.dns_rlength:
|
|
||||||
if DEBUG_DNSCLIENT:
|
if DEBUG_DNSCLIENT:
|
||||||
print "Answer too short."
|
print "Answer %d for '%s', class = %d, type = %d, ttl = %d." % (i,
|
||||||
return []
|
rr.dns_name, rr.dns_class, rr.dns_type,
|
||||||
|
rr.dns_ttl)
|
||||||
fmap = { DNS_T_A: dnsParseA, DNS_T_NS: dnsParseNS,
|
|
||||||
DNS_T_CNAME: dnsParseCNAME, DNS_T_SOA: dnsParseSOA,
|
|
||||||
DNS_T_NULL: dnsParseNULL, DNS_T_WKS: dnsParseWKS,
|
|
||||||
DNS_T_PTR: dnsParsePTR, DNS_T_HINFO: dnsParseHINFO,
|
|
||||||
DNS_T_MX: dnsParseMX, DNS_T_TXT: dnsParseTXT,
|
|
||||||
DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
|
|
||||||
|
|
||||||
if not rr.dns_type in fmap:
|
if len(rest) < rr.dns_rlength:
|
||||||
if DEBUG_DNSCLIENT:
|
if DEBUG_DNSCLIENT:
|
||||||
print "Don't know how to parse RR type %d!" % rr.dns_type
|
print "Answer too short."
|
||||||
else:
|
return []
|
||||||
rr.rdata = fmap[rr.dns_type](rest[:rr.dns_rlength], results)
|
|
||||||
|
|
||||||
rest = rest[rr.dns_rlength:]
|
fmap = { DNS_T_A: dnsParseA, DNS_T_NS: dnsParseNS,
|
||||||
rrlist += [rr]
|
DNS_T_CNAME: dnsParseCNAME, DNS_T_SOA: dnsParseSOA,
|
||||||
|
DNS_T_NULL: dnsParseNULL, DNS_T_WKS: dnsParseWKS,
|
||||||
|
DNS_T_PTR: dnsParsePTR, DNS_T_HINFO: dnsParseHINFO,
|
||||||
|
DNS_T_MX: dnsParseMX, DNS_T_TXT: dnsParseTXT,
|
||||||
|
DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
|
||||||
|
|
||||||
|
if not rr.dns_type in fmap:
|
||||||
|
if DEBUG_DNSCLIENT:
|
||||||
|
print "Don't know how to parse RR type %d!" % rr.dns_type
|
||||||
|
else:
|
||||||
|
rr.rdata = fmap[rr.dns_type](rest[:rr.dns_rlength], results)
|
||||||
|
|
||||||
|
rest = rest[rr.dns_rlength:]
|
||||||
|
rrlist += [rr]
|
||||||
|
|
||||||
return rrlist
|
return rrlist
|
||||||
|
|
||||||
|
@ -197,7 +197,13 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
|
|||||||
raise errors.NotFound("No IPA server with DNS support found!")
|
raise errors.NotFound("No IPA server with DNS support found!")
|
||||||
ns_main = dns_masters.pop(0)
|
ns_main = dns_masters.pop(0)
|
||||||
ns_replicas = dns_masters
|
ns_replicas = dns_masters
|
||||||
ns_ip_address = resolve_host(ns_main)
|
addresses = resolve_host(ns_main)
|
||||||
|
|
||||||
|
if len(addresses) > 0:
|
||||||
|
# use the first address
|
||||||
|
ns_ip_address = addresses[0]
|
||||||
|
else:
|
||||||
|
ns_ip_address = None
|
||||||
else:
|
else:
|
||||||
ns_main = ns_hostname
|
ns_main = ns_hostname
|
||||||
ns_replicas = []
|
ns_replicas = []
|
||||||
@ -230,7 +236,13 @@ def add_reverse_zone(zone, ns_hostname=None, ns_ip_address=None,
|
|||||||
raise errors.NotFound("No IPA server with DNS support found!")
|
raise errors.NotFound("No IPA server with DNS support found!")
|
||||||
ns_main = dns_masters.pop(0)
|
ns_main = dns_masters.pop(0)
|
||||||
ns_replicas = dns_masters
|
ns_replicas = dns_masters
|
||||||
ns_ip_address = resolve_host(ns_main)
|
addresses = resolve_host(ns_main)
|
||||||
|
|
||||||
|
if len(addresses) > 0:
|
||||||
|
# use the first address
|
||||||
|
ns_ip_address = addresses[0]
|
||||||
|
else:
|
||||||
|
ns_ip_address = None
|
||||||
else:
|
else:
|
||||||
ns_main = ns_hostname
|
ns_main = ns_hostname
|
||||||
ns_replicas = []
|
ns_replicas = []
|
||||||
|
@ -90,27 +90,35 @@ def verify_dns_records(host_name, responses, resaddr, family):
|
|||||||
if family not in familykw.keys():
|
if family not in familykw.keys():
|
||||||
raise RuntimeError("Unknown faimily %s\n" % family)
|
raise RuntimeError("Unknown faimily %s\n" % family)
|
||||||
|
|
||||||
rec = None
|
rec_list = []
|
||||||
for rsn in responses:
|
for rsn in responses:
|
||||||
if rsn.dns_type == familykw[family]['dns_type']:
|
if rsn.section == dnsclient.DNS_S_ANSWER and \
|
||||||
rec = rsn
|
rsn.dns_type == familykw[family]['dns_type']:
|
||||||
break
|
rec_list.append(rsn)
|
||||||
|
|
||||||
if rec == None:
|
if not rec_list:
|
||||||
raise IOError(errno.ENOENT,
|
raise IOError(errno.ENOENT,
|
||||||
"Warning: Hostname (%s) not found in DNS" % host_name)
|
"Warning: Hostname (%s) not found in DNS" % host_name)
|
||||||
|
|
||||||
if family == 'ipv4':
|
if family == 'ipv4':
|
||||||
familykw[family]['address'] = socket.inet_ntop(socket.AF_INET,
|
familykw[family]['address'] = [socket.inet_ntop(socket.AF_INET,
|
||||||
struct.pack('!L',rec.rdata.address))
|
struct.pack('!L',rec.rdata.address)) \
|
||||||
|
for rec in rec_list]
|
||||||
else:
|
else:
|
||||||
familykw[family]['address'] = socket.inet_ntop(socket.AF_INET6,
|
familykw[family]['address'] = [socket.inet_ntop(socket.AF_INET6,
|
||||||
struct.pack('!16B', *rec.rdata.address))
|
struct.pack('!16B', *rec.rdata.address)) \
|
||||||
|
for rec in rec_list]
|
||||||
|
|
||||||
# Check that DNS address is the same is address returned via standard glibc calls
|
# Check that DNS address is the same is address returned via standard glibc calls
|
||||||
dns_addr = netaddr.IPAddress(familykw[family]['address'])
|
dns_addrs = [netaddr.IPAddress(addr) for addr in familykw[family]['address']]
|
||||||
if dns_addr.format() != resaddr:
|
dns_addr = None
|
||||||
raise RuntimeError("The network address %s does not match the DNS lookup %s. Check /etc/hosts and ensure that %s is the IP address for %s" % (dns_addr.format(), resaddr, dns_addr.format(), host_name))
|
for addr in dns_addrs:
|
||||||
|
if addr.format() == resaddr:
|
||||||
|
dns_addr = addr
|
||||||
|
break
|
||||||
|
|
||||||
|
if dns_addr is None:
|
||||||
|
raise RuntimeError("Host address %s does not match any address in DNS lookup." % resaddr)
|
||||||
|
|
||||||
rs = dnsclient.query(dns_addr.reverse_dns, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
|
rs = dnsclient.query(dns_addr.reverse_dns, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
|
||||||
if len(rs) == 0:
|
if len(rs) == 0:
|
||||||
@ -498,14 +506,19 @@ def resolve_host(host_name):
|
|||||||
try:
|
try:
|
||||||
addrinfos = socket.getaddrinfo(host_name, None,
|
addrinfos = socket.getaddrinfo(host_name, None,
|
||||||
socket.AF_UNSPEC, socket.SOCK_STREAM)
|
socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
ip_list = []
|
||||||
|
|
||||||
for ai in addrinfos:
|
for ai in addrinfos:
|
||||||
ip = ai[4][0]
|
ip = ai[4][0]
|
||||||
if ip == "127.0.0.1" or ip == "::1":
|
if ip == "127.0.0.1" or ip == "::1":
|
||||||
raise HostnameLocalhost("The hostname resolves to the localhost address")
|
raise HostnameLocalhost("The hostname resolves to the localhost address")
|
||||||
|
|
||||||
return addrinfos[0][4][0]
|
ip_list.append(ip)
|
||||||
except:
|
|
||||||
return None
|
return ip_list
|
||||||
|
except socket.error:
|
||||||
|
return []
|
||||||
|
|
||||||
def get_host_name(no_host_dns):
|
def get_host_name(no_host_dns):
|
||||||
"""
|
"""
|
||||||
@ -534,8 +547,27 @@ def get_server_ip_address(host_name, fstore, unattended, options):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
ip_add_to_hosts = False
|
ip_add_to_hosts = False
|
||||||
if hostaddr is not None:
|
|
||||||
ip = ipautil.CheckedIPAddress(hostaddr, match_local=True)
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
if unattended:
|
||||||
|
print >> sys.stderr, "Please use --ip-address option to specify the address"
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
ip = read_ip_address(host_name, fstore)
|
||||||
|
elif len(hostaddr) == 1:
|
||||||
|
ip = ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
|
||||||
else:
|
else:
|
||||||
# hostname is not resolvable
|
# hostname is not resolvable
|
||||||
ip = options.ip_address
|
ip = options.ip_address
|
||||||
|
Loading…
Reference in New Issue
Block a user