mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Make the IPA installer IPv6 friendly
Notable changes include: * parse AAAA records in dnsclient * also ask for AAAA records when verifying FQDN * do not use functions that are not IPv6 aware - notably socket.gethostbyname() The complete list of functions was taken from http://www.akkadia.org/drepper/userapi-ipv6.html section "Interface Checklist"
This commit is contained in:
committed by
Rob Crittenden
parent
b328d845de
commit
ee4d2739f1
@@ -62,16 +62,19 @@ def parse_options():
|
||||
def resolve_host(host_name):
|
||||
ip = None
|
||||
try:
|
||||
ip = socket.gethostbyname(host_name)
|
||||
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
|
||||
print "Please change your /etc/hosts file so that the hostname"
|
||||
print "resolves to the ip address of your network interface."
|
||||
print ""
|
||||
print "Please fix your /etc/hosts file and restart the setup program"
|
||||
return None
|
||||
addrinfos = socket.getaddrinfo(host_name, None,
|
||||
socket.AF_UNSPEC, socket.SOCK_DGRAM)
|
||||
for ai in addrinfos:
|
||||
ip = ai[4][0]
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
|
||||
print "Please change your /etc/hosts file so that the hostname"
|
||||
print "resolves to the ip address of your network interface."
|
||||
print ""
|
||||
print "Please fix your /etc/hosts file and restart the setup program"
|
||||
return None
|
||||
|
||||
ip = addrinfos[0][4][0]
|
||||
except:
|
||||
print "Unable to lookup the IP address of the provided host"
|
||||
return ip
|
||||
|
||||
@@ -127,12 +127,17 @@ def get_host_name(no_host_dns):
|
||||
return hostname
|
||||
|
||||
def resolve_host(host_name):
|
||||
ip = socket.gethostbyname(host_name)
|
||||
try:
|
||||
addrinfos = socket.getaddrinfo(host_name, None,
|
||||
socket.AF_UNSPEC, socket.SOCK_STREAM)
|
||||
for ai in addrinfos:
|
||||
ip = ai[4][0]
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
raise HostnameLocalhost
|
||||
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
raise HostnameLocalhost
|
||||
|
||||
return ip
|
||||
return addrinfos[0][4][0]
|
||||
except:
|
||||
return None
|
||||
|
||||
def set_owner(config, dir):
|
||||
pw = pwd.getpwnam(config.ds_user)
|
||||
@@ -240,6 +245,8 @@ def install_bind(config, options):
|
||||
forwarders = ()
|
||||
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")
|
||||
create_reverse = bindinstance.create_reverse(options.unattended)
|
||||
bind.setup(config.host_name, ip_address, config.realm_name,
|
||||
config.domain_name, forwarders, options.conf_ntp, create_reverse)
|
||||
|
||||
@@ -279,19 +279,22 @@ def read_host_name(host_default,no_host_dns=False):
|
||||
return host_name
|
||||
|
||||
def resolve_host(host_name):
|
||||
ip = ""
|
||||
ip = None
|
||||
try:
|
||||
ip = socket.gethostbyname(host_name)
|
||||
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
|
||||
print "Please change your /etc/hosts file so that the hostname"
|
||||
print "resolves to the ip address of your network interface."
|
||||
print "The KDC service does not listen on localhost"
|
||||
print ""
|
||||
print "Please fix your /etc/hosts file and restart the setup program"
|
||||
return None
|
||||
addrinfos = socket.getaddrinfo(host_name, None,
|
||||
socket.AF_UNSPEC, socket.SOCK_DGRAM)
|
||||
for ai in addrinfos:
|
||||
ip = ai[4][0]
|
||||
if ip == "127.0.0.1" or ip == "::1":
|
||||
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
|
||||
print "Please change your /etc/hosts file so that the hostname"
|
||||
print "resolves to the ip address of your network interface."
|
||||
print "The KDC service does not listen on localhost"
|
||||
print ""
|
||||
print "Please fix your /etc/hosts file and restart the setup program"
|
||||
return None
|
||||
|
||||
ip = addrinfos[0][4][0]
|
||||
except:
|
||||
print "Unable to lookup the IP address of the provided host"
|
||||
return ip
|
||||
@@ -549,7 +552,7 @@ def main():
|
||||
sys.exit("Aborting installation")
|
||||
|
||||
# check the hostname is correctly configured, it must be as the kldap
|
||||
# utilities just use the hostname as returned by gethostbyname to set
|
||||
# utilities just use the hostname as returned by getaddrinfo to set
|
||||
# up some of the standard entries
|
||||
|
||||
host_default = ""
|
||||
|
||||
@@ -37,6 +37,7 @@ DNS_T_PTR = 12
|
||||
DNS_T_HINFO = 13
|
||||
DNS_T_MX = 15
|
||||
DNS_T_TXT = 16
|
||||
DNS_T_AAAA = 28
|
||||
DNS_T_SRV = 33
|
||||
DNS_T_ANY = 255
|
||||
|
||||
@@ -127,6 +128,10 @@ class DNSRData:
|
||||
# u_int32_t address;
|
||||
#} dns_rr_a_t;
|
||||
#
|
||||
#typedef struct dns_rr_aaaa {
|
||||
# unsigned char address[16];
|
||||
#} dns_rr_aaaa_t;
|
||||
#
|
||||
#typedef struct dns_rr_cname {
|
||||
# const char *cname;
|
||||
#} dns_rr_cname_t;
|
||||
@@ -239,6 +244,18 @@ def dnsParseA(data, base):
|
||||
print "A = %d.%d.%d.%d." % (ord(data[0]), ord(data[1]), ord(data[2]), ord(data[3]))
|
||||
return rdata
|
||||
|
||||
def dnsParseAAAA(data, base):
|
||||
rdata = DNSRData()
|
||||
if len(data) < 16:
|
||||
rdata.address = 0
|
||||
return None
|
||||
|
||||
rdata.address = list(struct.unpack('!16B', data))
|
||||
if DEBUG_DNSCLIENT:
|
||||
print socket.inet_ntop(socket.AF_INET6,
|
||||
struct.pack('!16B', *rdata.address))
|
||||
return rdata
|
||||
|
||||
def dnsParseText(data):
|
||||
if len(data) < 1:
|
||||
return ("", None)
|
||||
@@ -413,7 +430,7 @@ def dnsParseResults(results):
|
||||
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_SRV: dnsParseSRV}
|
||||
DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
|
||||
|
||||
if not rr.dns_type in fmap:
|
||||
if DEBUG_DNSCLIENT:
|
||||
|
||||
@@ -27,6 +27,7 @@ import fileinput
|
||||
import sys
|
||||
import struct
|
||||
import fcntl
|
||||
import netaddr
|
||||
|
||||
from ipapython import ipautil
|
||||
from ipapython import dnsclient
|
||||
@@ -42,8 +43,65 @@ def get_fqdn():
|
||||
fqdn = ""
|
||||
return fqdn
|
||||
|
||||
def verify_fqdn(host_name,no_host_dns=False):
|
||||
def verify_dns_records(host_name, responses, resaddr, family):
|
||||
familykw = { 'ipv4' : {
|
||||
'dns_type' : dnsclient.DNS_T_A,
|
||||
'socket_family' : socket.AF_INET,
|
||||
},
|
||||
'ipv6' : {
|
||||
'dns_type' : dnsclient.DNS_T_AAAA,
|
||||
'socket_family' : socket.AF_INET6,
|
||||
},
|
||||
}
|
||||
|
||||
family = family.lower()
|
||||
if family not in familykw.keys():
|
||||
raise RuntimeError("Unknown faimily %s\n" % family)
|
||||
|
||||
if len(responses) == 0:
|
||||
raise IOError(errno.ENOENT,
|
||||
"Warning: Hostname (%s) not found with NSS calls" % host_name)
|
||||
|
||||
rec = None
|
||||
for rsn in responses:
|
||||
if rsn.dns_type == familykw[family]['dns_type']:
|
||||
rec = rsn
|
||||
break
|
||||
|
||||
if rec == None:
|
||||
raise IOError(errno.ENOENT,
|
||||
"Warning: Hostname (%s) not found in DNS" % host_name)
|
||||
|
||||
if family == 'ipv4':
|
||||
familykw[family]['address'] = socket.inet_ntop(socket.AF_INET,
|
||||
struct.pack('!L',rec.rdata.address))
|
||||
else:
|
||||
familykw[family]['address'] = socket.inet_ntop(socket.AF_INET6,
|
||||
struct.pack('!16B', *rec.rdata.address))
|
||||
|
||||
# Check that DNS address is the same is address returned via standard glibc calls
|
||||
dns_addr = netaddr.IPAddress(familykw[family]['address'])
|
||||
if dns_addr.format() != resaddr:
|
||||
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))
|
||||
|
||||
rs = dnsclient.query(dns_addr.reverse_dns, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
|
||||
if len(rs) == 0:
|
||||
raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
|
||||
|
||||
rev = None
|
||||
for rsn in rs:
|
||||
if rsn.dns_type == dnsclient.DNS_T_PTR:
|
||||
rev = rsn
|
||||
break
|
||||
|
||||
if rev == None:
|
||||
raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
|
||||
|
||||
if rec.dns_name != rev.rdata.ptrdname:
|
||||
raise RuntimeError("The DNS forward record %s does not match the reverse address %s" % (rec.dns_name, rev.rdata.ptrdname))
|
||||
|
||||
|
||||
def verify_fqdn(host_name,no_host_dns=False):
|
||||
if len(host_name.split(".")) < 2 or host_name == "localhost.localdomain":
|
||||
raise RuntimeError("Invalid hostname: " + host_name)
|
||||
|
||||
@@ -59,7 +117,7 @@ def verify_fqdn(host_name,no_host_dns=False):
|
||||
if a[4][0] == '127.0.0.1' or a[4][0] == '::1':
|
||||
raise RuntimeError("The IPA Server hostname cannot resolve to localhost (%s). A routable IP address must be used. Check /etc/hosts to see if %s is an alias for %s" % (a[4][0], host_name, a[4][0]))
|
||||
try:
|
||||
revaddr = a[4][0]
|
||||
resaddr = a[4][0]
|
||||
revname = socket.gethostbyaddr(a[4][0])[0]
|
||||
except:
|
||||
raise RuntimeError("Unable to resolve the reverse ip address, check /etc/hosts or DNS name resolution")
|
||||
@@ -77,48 +135,15 @@ def verify_fqdn(host_name,no_host_dns=False):
|
||||
if rsn.dns_type == dnsclient.DNS_T_CNAME:
|
||||
raise RuntimeError("The IPA Server Hostname cannot be a CNAME, only A names are allowed.")
|
||||
|
||||
# Verify that it is a DNS A record
|
||||
# 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:
|
||||
print "Warning: Hostname (%s) not found in DNS" % host_name
|
||||
return
|
||||
try:
|
||||
verify_dns_records(host_name, rs, resaddr, 'ipv4')
|
||||
except IOError, e:
|
||||
if e.errno == errno.ENOENT: # retry IPv6
|
||||
rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA)
|
||||
verify_dns_records(host_name, rs, resaddr, 'ipv6')
|
||||
|
||||
rec = None
|
||||
for rsn in rs:
|
||||
if rsn.dns_type == dnsclient.DNS_T_A:
|
||||
rec = rsn
|
||||
break
|
||||
|
||||
if rec == None:
|
||||
print "Warning: Hostname (%s) not found in DNS" % host_name
|
||||
return
|
||||
|
||||
# Compare the forward and reverse
|
||||
forward = rec.dns_name
|
||||
|
||||
addr = socket.inet_ntoa(struct.pack('<L',rec.rdata.address))
|
||||
ipaddr = socket.inet_ntoa(struct.pack('!L',rec.rdata.address))
|
||||
if revaddr != ipaddr:
|
||||
raise RuntimeError("The network address %s does not match the reverse lookup %s. Check /etc/hosts and ensure that %s is the IP address for %s" % (ipaddr, revaddr, ipaddr, host_name))
|
||||
|
||||
addr = addr + ".in-addr.arpa."
|
||||
rs = dnsclient.query(addr, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
|
||||
if len(rs) == 0:
|
||||
raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
|
||||
|
||||
rev = None
|
||||
for rsn in rs:
|
||||
if rsn.dns_type == dnsclient.DNS_T_PTR:
|
||||
rev = rsn
|
||||
break
|
||||
|
||||
if rev == None:
|
||||
raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
|
||||
|
||||
reverse = rev.rdata.ptrdname
|
||||
|
||||
if forward != reverse:
|
||||
raise RuntimeError("The DNS forward record %s does not match the reverse address %s" % (forward, reverse))
|
||||
|
||||
def verify_ip_address(ip):
|
||||
is_ok = True
|
||||
|
||||
@@ -135,7 +135,7 @@ class KrbInstance(service.Service):
|
||||
self.fqdn = host_name
|
||||
self.realm = realm_name.upper()
|
||||
self.host = host_name.split(".")[0]
|
||||
self.ip = socket.gethostbyname(host_name)
|
||||
self.ip = socket.getaddrinfo(host_name, None, socket.AF_UNSPEC, socket.SOCK_STREAM)[0][4][0]
|
||||
self.domain = domain_name
|
||||
self.suffix = util.realm_to_suffix(self.realm)
|
||||
self.kdc_password = ipautil.ipa_generate_password()
|
||||
|
||||
Reference in New Issue
Block a user