Only warn when specified server IP addresses don't match intf

In containers local addresses differ from public addresses and we need
a way to provide only public address to installers.

https://pagure.io/freeipa/issue/2715
https://pagure.io/freeipa/issue/4317

Reviewed-By: Tomas Krizek <tkrizek@redhat.com>
This commit is contained in:
Martin Basti 2017-05-31 15:50:05 +02:00
parent 566361e63d
commit 6637980af6
8 changed files with 59 additions and 32 deletions

View File

@ -41,6 +41,7 @@ from ipalib.util import (
broadcast_ip_address_warning, broadcast_ip_address_warning,
network_ip_address_warning, network_ip_address_warning,
normalize_hostname, normalize_hostname,
no_matching_interface_for_ip_address_warning,
verify_host_resolvable, verify_host_resolvable,
) )
from ipaplatform import services from ipaplatform import services
@ -1300,6 +1301,7 @@ def update_dns(server, hostname, options):
network_ip_address_warning(update_ips) network_ip_address_warning(update_ips)
broadcast_ip_address_warning(update_ips) broadcast_ip_address_warning(update_ips)
no_matching_interface_for_ip_address_warning(update_ips)
update_txt = "debug\n" update_txt = "debug\n"
update_txt += ipautil.template_str(DELETE_TEMPLATE_A, update_txt += ipautil.template_str(DELETE_TEMPLATE_A,
@ -1445,7 +1447,7 @@ def check_ip_addresses(options):
if options.ip_addresses: if options.ip_addresses:
for ip in options.ip_addresses: for ip in options.ip_addresses:
try: try:
ipautil.CheckedIPAddress(ip, match_local=True) ipautil.CheckedIPAddress(ip)
except ValueError as e: except ValueError as e:
root_logger.error(e) root_logger.error(e)
return False return False

View File

@ -34,7 +34,7 @@ class HostNameInstallInterface(service.ServiceInstallInterface):
def ip_addresses(self, values): def ip_addresses(self, values):
for value in values: for value in values:
try: try:
CheckedIPAddress(value, match_local=True) CheckedIPAddress(value)
except Exception as e: except Exception as e:
raise ValueError("invalid IP address {0}: {1}".format( raise ValueError("invalid IP address {0}: {1}".format(
value, e)) value, e))

View File

@ -1128,3 +1128,17 @@ def broadcast_ip_address_warning(addr_list):
# print # print
print("WARNING: IP address {} might be broadcast address".format( print("WARNING: IP address {} might be broadcast address".format(
ip), file=sys.stderr) ip), file=sys.stderr)
def no_matching_interface_for_ip_address_warning(addr_list):
for ip in addr_list:
if not ip.get_matching_interface():
root_logger.warning(
"No network interface matches the IP address %s", ip)
# fixme: once when loggers will be fixed, we can remove this
# print
print(
"WARNING: No network interface matches the IP address "
"{}".format(ip),
file=sys.stderr
)

View File

@ -161,34 +161,7 @@ class CheckedIPAddress(UnsafeIPAddress):
raise ValueError("cannot use multicast IP address {}".format(addr)) raise ValueError("cannot use multicast IP address {}".format(addr))
if match_local: if match_local:
if self.version == 4: if not self.get_matching_interface():
family = netifaces.AF_INET
elif self.version == 6:
family = netifaces.AF_INET6
else:
raise ValueError(
"Unsupported address family ({})".format(self.version)
)
iface = None
for interface in netifaces.interfaces():
for ifdata in netifaces.ifaddresses(interface).get(family, []):
# link-local addresses contain '%suffix' that causes parse
# errors in IPNetwork
ifaddr = ifdata['addr'].split(u'%', 1)[0]
ifnet = netaddr.IPNetwork('{addr}/{netmask}'.format(
addr=ifaddr,
netmask=ifdata['netmask']
))
if ifnet == self._net or (
self._net is None and ifnet.ip == self):
self._net = ifnet
iface = interface
break
if iface is None:
raise ValueError('no network interface matches the IP address ' raise ValueError('no network interface matches the IP address '
'and netmask {}'.format(addr)) 'and netmask {}'.format(addr))
@ -218,6 +191,39 @@ class CheckedIPAddress(UnsafeIPAddress):
def is_broadcast_addr(self): def is_broadcast_addr(self):
return self.version == 4 and self == self._net.broadcast return self.version == 4 and self == self._net.broadcast
def get_matching_interface(self):
"""Find matching local interface for address
:return: Interface name or None if no interface has this address
"""
if self.version == 4:
family = netifaces.AF_INET
elif self.version == 6:
family = netifaces.AF_INET6
else:
raise ValueError(
"Unsupported address family ({})".format(self.version)
)
iface = None
for interface in netifaces.interfaces():
for ifdata in netifaces.ifaddresses(interface).get(family, []):
# link-local addresses contain '%suffix' that causes parse
# errors in IPNetwork
ifaddr = ifdata['addr'].split(u'%', 1)[0]
ifnet = netaddr.IPNetwork('{addr}/{netmask}'.format(
addr=ifaddr,
netmask=ifdata['netmask']
))
if ifnet == self._net or (
self._net is None and ifnet.ip == self):
self._net = ifnet
iface = interface
break
return iface
def valid_ip(addr): def valid_ip(addr):
return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr) return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr)

View File

@ -266,6 +266,7 @@ def install_check(standalone, api, replica, options, hostname):
util.network_ip_address_warning(ip_addresses) util.network_ip_address_warning(ip_addresses)
util.broadcast_ip_address_warning(ip_addresses) util.broadcast_ip_address_warning(ip_addresses)
util.no_matching_interface_for_ip_address_warning(ip_addresses)
if not options.forward_policy: if not options.forward_policy:
# user did not specify policy, derive it: default is 'first' but # user did not specify policy, derive it: default is 'first' but

View File

@ -281,7 +281,7 @@ def read_ip_addresses():
if not ip: if not ip:
break break
try: try:
ip_parsed = ipautil.CheckedIPAddress(ip, match_local=True) ip_parsed = ipautil.CheckedIPAddress(ip)
except Exception as e: except Exception as e:
print("Error: Invalid IP Address %s: %s" % (ip, e)) print("Error: Invalid IP Address %s: %s" % (ip, e))
continue continue
@ -590,7 +590,7 @@ def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses):
if len(hostaddr): if len(hostaddr):
for ha in hostaddr: for ha in hostaddr:
try: try:
ips.append(ipautil.CheckedIPAddress(ha, match_local=True)) ips.append(ipautil.CheckedIPAddress(ha, match_local=False))
except ValueError as e: except ValueError as e:
root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e)) root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e))

View File

@ -29,6 +29,7 @@ from ipalib.util import (
validate_domain_name, validate_domain_name,
network_ip_address_warning, network_ip_address_warning,
broadcast_ip_address_warning, broadcast_ip_address_warning,
no_matching_interface_for_ip_address_warning,
) )
import ipaclient.install.ntpconf import ipaclient.install.ntpconf
from ipaserver.install import ( from ipaserver.install import (
@ -617,6 +618,7 @@ def install_check(installer):
# check addresses here, dns module is doing own check # check addresses here, dns module is doing own check
network_ip_address_warning(ip_addresses) network_ip_address_warning(ip_addresses)
broadcast_ip_address_warning(ip_addresses) broadcast_ip_address_warning(ip_addresses)
no_matching_interface_for_ip_address_warning(ip_addresses)
if options.setup_adtrust: if options.setup_adtrust:
adtrust.install_check(False, options, api) adtrust.install_check(False, options, api)

View File

@ -35,6 +35,7 @@ from ipalib.config import Env
from ipalib.util import ( from ipalib.util import (
network_ip_address_warning, network_ip_address_warning,
broadcast_ip_address_warning, broadcast_ip_address_warning,
no_matching_interface_for_ip_address_warning,
) )
from ipaclient.install.client import configure_krb5_conf, purge_host_keytab from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
from ipaserver.install import ( from ipaserver.install import (
@ -1285,6 +1286,7 @@ def promote_check(installer):
# check addresses here, dns module is doing own check # check addresses here, dns module is doing own check
network_ip_address_warning(config.ips) network_ip_address_warning(config.ips)
broadcast_ip_address_warning(config.ips) broadcast_ip_address_warning(config.ips)
no_matching_interface_for_ip_address_warning(config.ips)
if options.setup_adtrust: if options.setup_adtrust:
adtrust.install_check(False, options, remote_api) adtrust.install_check(False, options, remote_api)