From a04defc43419906675107e483f0f2f3153685c8d Mon Sep 17 00:00:00 2001 From: Martin Basti Date: Wed, 31 May 2017 15:50:05 +0200 Subject: [PATCH] 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 --- ipaclient/install/client.py | 4 +- ipalib/install/hostname.py | 2 +- ipalib/util.py | 14 +++++++ ipapython/ipautil.py | 62 ++++++++++++++++-------------- ipaserver/install/dns.py | 1 + ipaserver/install/installutils.py | 4 +- ipaserver/install/server/install.py | 2 + ipaserver/install/server/replicainstall.py | 2 + 8 files changed, 59 insertions(+), 32 deletions(-) diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index 6f10f5258747881b9af8c6b70b499f9ff7d577ff..41dae3004d1f4836e79c2048ae0a12f722595ca0 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -41,6 +41,7 @@ from ipalib.util import ( broadcast_ip_address_warning, network_ip_address_warning, normalize_hostname, + no_matching_interface_for_ip_address_warning, verify_host_resolvable, ) from ipaplatform import services @@ -1300,6 +1301,7 @@ def update_dns(server, hostname, options): network_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 += ipautil.template_str(DELETE_TEMPLATE_A, @@ -1445,7 +1447,7 @@ def check_ip_addresses(options): if options.ip_addresses: for ip in options.ip_addresses: try: - ipautil.CheckedIPAddress(ip, match_local=True) + ipautil.CheckedIPAddress(ip) except ValueError as e: root_logger.error(e) return False diff --git a/ipalib/install/hostname.py b/ipalib/install/hostname.py index 74c569d972df9975d677762b5769b2bf84dfddf0..5422ba6390ce13aa40f34938ed777d8821e8231b 100644 --- a/ipalib/install/hostname.py +++ b/ipalib/install/hostname.py @@ -34,7 +34,7 @@ class HostNameInstallInterface(service.ServiceInstallInterface): def ip_addresses(self, values): for value in values: try: - CheckedIPAddress(value, match_local=True) + CheckedIPAddress(value) except Exception as e: raise ValueError("invalid IP address {0}: {1}".format( value, e)) diff --git a/ipalib/util.py b/ipalib/util.py index 713fc107e9374eefe7805bc4e1abc40b6d150c32..1bd8495a49b010e7a3ac926dad516ab5f8219b39 100644 --- a/ipalib/util.py +++ b/ipalib/util.py @@ -1128,3 +1128,17 @@ def broadcast_ip_address_warning(addr_list): # print print("WARNING: IP address {} might be broadcast address".format( 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 + ) diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index 317fc225b722ad3ce2f4b9d92822b4f19d49adb9..a277ed87473f3c591f34fcc00e1159f3bbfe3e9b 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -161,34 +161,7 @@ class CheckedIPAddress(UnsafeIPAddress): raise ValueError("cannot use multicast IP address {}".format(addr)) if match_local: - 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 - - if iface is None: + if not self.get_matching_interface(): raise ValueError('no network interface matches the IP address ' 'and netmask {}'.format(addr)) @@ -218,6 +191,39 @@ class CheckedIPAddress(UnsafeIPAddress): def is_broadcast_addr(self): 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): return netaddr.valid_ipv4(addr) or netaddr.valid_ipv6(addr) diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py index 0dddf2a6427f3f939171d755bfe2b1f05cfafa67..090b79493652566a433da248fa7fd9e33dd2cb72 100644 --- a/ipaserver/install/dns.py +++ b/ipaserver/install/dns.py @@ -266,6 +266,7 @@ def install_check(standalone, api, replica, options, hostname): util.network_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: # user did not specify policy, derive it: default is 'first' but diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index d2283af20485fd5d66bfd3cc49059d08d1802575..3521d555914714351160213df60ed9167ac6e370 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -276,7 +276,7 @@ def read_ip_addresses(): if not ip: break try: - ip_parsed = ipautil.CheckedIPAddress(ip, match_local=True) + ip_parsed = ipautil.CheckedIPAddress(ip) except Exception as e: print("Error: Invalid IP Address %s: %s" % (ip, e)) continue @@ -585,7 +585,7 @@ def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses): if len(hostaddr): for ha in hostaddr: try: - ips.append(ipautil.CheckedIPAddress(ha, match_local=True)) + ips.append(ipautil.CheckedIPAddress(ha, match_local=False)) except ValueError as e: root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e)) diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 9dcf903f4582740f007c049fae3ec247ddf52aef..7eb291e07c00e0407ce534c3d4088e6f6378260f 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -29,6 +29,7 @@ from ipalib.util import ( validate_domain_name, network_ip_address_warning, broadcast_ip_address_warning, + no_matching_interface_for_ip_address_warning, ) import ipaclient.install.ntpconf from ipaserver.install import ( @@ -617,6 +618,7 @@ def install_check(installer): # check addresses here, dns module is doing own check network_ip_address_warning(ip_addresses) broadcast_ip_address_warning(ip_addresses) + no_matching_interface_for_ip_address_warning(ip_addresses) if options.setup_adtrust: adtrust.install_check(False, options, api) diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index 20eaf98397101b49c751c325afc0591e0babcc18..6620f0222f9d38112ce0d0fd72381e5673921cba 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -35,6 +35,7 @@ from ipalib.config import Env from ipalib.util import ( network_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 ipaserver.install import ( @@ -1285,6 +1286,7 @@ def promote_check(installer): # check addresses here, dns module is doing own check network_ip_address_warning(config.ips) broadcast_ip_address_warning(config.ips) + no_matching_interface_for_ip_address_warning(config.ips) if options.setup_adtrust: adtrust.install_check(False, options, remote_api) -- 2.9.4