diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install index 6963cb343..a7f136b16 100755 --- a/install/tools/ipa-dns-install +++ b/install/tools/ipa-dns-install @@ -54,7 +54,7 @@ def parse_options(): help="Master Server IP Address. This option can be used " "multiple times") parser.add_option("--forwarder", dest="forwarders", action="append", - type="ip", help="Add a DNS forwarder. This option can be used multiple times") + type="ip_with_loopback", help="Add a DNS forwarder. This option can be used multiple times") parser.add_option("--no-forwarders", dest="no_forwarders", action="store_true", default=False, help="Do not add any DNS forwarders, use root servers instead") parser.add_option("--auto-forwarders", dest="auto_forwarders", diff --git a/ipapython/config.py b/ipapython/config.py index 8393e0d5d..aa4b3e48f 100644 --- a/ipapython/config.py +++ b/ipapython/config.py @@ -23,6 +23,7 @@ from optparse import ( # pylint: enable=deprecated-module from copy import copy import socket +import functools from dns import resolver, rdatatype from dns.exception import DNSException @@ -33,6 +34,7 @@ from six.moves.urllib.parse import urlsplit # pylint: enable=import-error from ipapython.dn import DN +from ipapython.ipautil import CheckedIPAddress, CheckedIPAddressLoopback try: # pylint: disable=ipa-forbidden-import @@ -65,13 +67,16 @@ class IPAFormatter(IndentedHelpFormatter): ret += "%s %s\n" % (spacing, line) return ret -def check_ip_option(option, opt, value): - from ipapython.ipautil import CheckedIPAddress +def check_ip_option(option, opt, value, allow_loopback=False): try: - return CheckedIPAddress(value) + if allow_loopback: + return CheckedIPAddressLoopback(value) + else: + return CheckedIPAddress(value) except Exception as e: - raise OptionValueError("option %s: invalid IP address %s: %s" % (opt, value, e)) + raise OptionValueError("option {}: invalid IP address {}: {}" + .format(opt, value, e)) def check_dn_option(option, opt, value): try: @@ -95,9 +100,11 @@ class IPAOption(Option): security-sensitive such as passwords. """ ATTRS = Option.ATTRS + ["sensitive", "constructor"] - TYPES = Option.TYPES + ("ip", "dn", "constructor") + TYPES = Option.TYPES + ("ip", "dn", "constructor", "ip_with_loopback") TYPE_CHECKER = copy(Option.TYPE_CHECKER) TYPE_CHECKER["ip"] = check_ip_option + TYPE_CHECKER["ip_with_loopback"] = functools.partial(check_ip_option, + allow_loopback=True) TYPE_CHECKER["dn"] = check_dn_option TYPE_CHECKER["constructor"] = check_constructor diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py index 1cac24d50..e8f67a3de 100644 --- a/ipapython/install/cli.py +++ b/ipapython/install/cli.py @@ -16,7 +16,8 @@ import six from ipapython import admintool from ipapython.ipa_log_manager import standard_logging_setup -from ipapython.ipautil import CheckedIPAddress, private_ccache +from ipapython.ipautil import (CheckedIPAddress, CheckedIPAddressLoopback, + private_ccache) from . import core, common @@ -166,6 +167,8 @@ class ConfigureTool(admintool.AdminTool): kwargs['type'] = 'int' elif knob_scalar_type is long: kwargs['type'] = 'long' + elif knob_scalar_type is CheckedIPAddressLoopback: + kwargs['type'] = 'ip_with_loopback' elif knob_scalar_type is CheckedIPAddress: kwargs['type'] = 'ip' elif issubclass(knob_scalar_type, enum.Enum): diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index 71ed4a174..c4149a184 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -244,6 +244,25 @@ class CheckedIPAddress(UnsafeIPAddress): self._net = ifnet +class CheckedIPAddressLoopback(CheckedIPAddress): + """IPv4 or IPv6 address with additional constraints with + possibility to use a loopback IP. + Reserved or link-local addresses are never accepted. + """ + def __init__(self, addr, parse_netmask=True, allow_multicast=False): + + super(CheckedIPAddressLoopback, self).__init__( + addr, parse_netmask=parse_netmask, + allow_multicast=allow_multicast, + allow_loopback=True) + + if self.is_loopback(): + # print is being used instead of a logger, because at this + # moment, in execution process, there is no logger configured + print("WARNING: You are using a loopback IP: {}".format(addr), + file=sys.stderr) + + 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 119aae136..e14b353e9 100644 --- a/ipaserver/install/dns.py +++ b/ipaserver/install/dns.py @@ -491,7 +491,7 @@ class DNSInstallInterface(hostname.HostNameInstallInterface): forwarders = knob( # pylint: disable=invalid-sequence-index - typing.List[ipautil.CheckedIPAddress], None, + typing.List[ipautil.CheckedIPAddressLoopback], None, description=("Add a DNS forwarder. This option can be used multiple " "times"), cli_names='--forwarder',