An easy way to check if master->replica UDP port check actually works is to simply configure few iptables rules to drop packets for tested UDP or TCP ports:
A INPUT -m udp -p udp --dport 88 -j DROP -A INPUT -m tcp -p tcp --dport 88 -j DROP ---- UDP port checks in ipa-replica-conncheck always returns OK even if they are closed by a firewall. They cannot be reliably checked in the same way as TCP ports as there is no session management as in TCP protocol. We cannot guarantee a response on the checked side without our own echo server bound to checked port. This patch removes UDP port checks in replica->master direction as we would have to implement (kerberos) protocol-wise check to make the other side actually respond. A list of skipped ports is printed for user. Direction master->replica was fixed and now it is able to report error when the port is blocked. https://fedorahosted.org/freeipa/ticket/2062
>From 415cae5d3b63e10471e515666224863036be0ece Mon Sep 17 00:00:00 2001 From: Martin Kosek <mko...@redhat.com> Date: Wed, 1 Feb 2012 17:12:17 +0100 Subject: [PATCH] Sanitize UDP checks in conncheck UDP port checks in ipa-replica-conncheck always returns OK even if they are closed by a firewall. They cannot be reliably checked in the same way as TCP ports as there is no session management as in TCP protocol. We cannot guarantee a response on the checked side without our own echo server bound to checked port. This patch removes UDP port checks in replica->master direction as we would have to implement (kerberos) protocol-wise check to make the other side actually respond. A list of skipped ports is printed for user. Direction master->replica was fixed and now it is able to report error when the port is blocked. https://fedorahosted.org/freeipa/ticket/2062 --- install/tools/ipa-replica-conncheck | 58 +++++++++++++++++++++++------------ ipapython/ipautil.py | 27 ++++++--------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck index 2622130e7c6f6ceabe6ff8a17e89412089897c5f..44b3caa45a20d3a72985c051a7982da1f9716147 100755 --- a/install/tools/ipa-replica-conncheck +++ b/install/tools/ipa-replica-conncheck @@ -34,6 +34,7 @@ import socket import time import threading import errno +from socket import SOCK_STREAM, SOCK_DGRAM CONNECT_TIMEOUT = 5 RESPONDERS = [ ] @@ -42,24 +43,24 @@ CCACHE_FILE = "/etc/ipa/.conncheck_ccache" KRB5_CONFIG = None class CheckedPort(object): - def __init__(self, port, stream, description): + def __init__(self, port, port_type, description): self.port = port - self.stream = stream + self.port_type = port_type self.description = description BASE_PORTS = [ - CheckedPort(389, True, "Directory Service: Unsecure port"), - CheckedPort(636, True, "Directory Service: Secure port"), - CheckedPort(88, True, "Kerberos KDC: TCP"), - CheckedPort(88, False, "Kerberos KDC: UDP"), - CheckedPort(464, True, "Kerberos Kpasswd: TCP"), - CheckedPort(464, False, "Kerberos Kpasswd: UDP"), - CheckedPort(80, True, "HTTP Server: Unsecure port"), - CheckedPort(443, True, "HTTP Server: Secure port"), + CheckedPort(389, SOCK_STREAM, "Directory Service: Unsecure port"), + CheckedPort(636, SOCK_STREAM, "Directory Service: Secure port"), + CheckedPort(88, SOCK_STREAM, "Kerberos KDC: TCP"), + CheckedPort(88, SOCK_DGRAM, "Kerberos KDC: UDP"), + CheckedPort(464, SOCK_STREAM, "Kerberos Kpasswd: TCP"), + CheckedPort(464, SOCK_DGRAM, "Kerberos Kpasswd: UDP"), + CheckedPort(80, SOCK_STREAM, "HTTP Server: Unsecure port"), + CheckedPort(443, SOCK_STREAM, "HTTP Server: Secure port"), ] CA_PORTS = [ - CheckedPort(7389, True, "PKI-CA: Directory Service port"), + CheckedPort(7389, SOCK_STREAM, "PKI-CA: Directory Service port"), ] def print_info(msg): @@ -211,18 +212,20 @@ def configure_krb5_conf(realm, kdc, filename): class PortResponder(threading.Thread): - def __init__(self, port, socket_stream = True, socket_timeout=1): + def __init__(self, port, port_type, socket_timeout=1): super(PortResponder, self).__init__() self.port = port - self.socket_stream = socket_stream + self.port_type = port_type self.socket_timeout = socket_timeout self._stop_request = False def run(self): while not self._stop_request: try: - ipautil.bind_port_responder(self.port, self.socket_stream, - self.socket_timeout, responder_data="FreeIPA") + ipautil.bind_port_responder(self.port, + self.port_type, + socket_timeout=self.socket_timeout, + responder_data="FreeIPA") except socket.timeout: pass except socket.error, e: @@ -242,7 +245,7 @@ def port_check(host, port_list): failed_ports = [] for port in port_list: - if ipautil.host_port_open(host, port.port, port.stream, CONNECT_TIMEOUT): + if ipautil.host_port_open(host, port.port, port.port_type, socket_timeout=CONNECT_TIMEOUT): result = "OK" else: failed_ports.append(port) @@ -250,8 +253,12 @@ def port_check(host, port_list): print_info(" %s (%d): %s" % (port.description, port.port, result)) if failed_ports: - msg_ports = ", ".join([str(port.port) for port in failed_ports]) - raise RuntimeError("Port check failed! Inaccessible port(s): %s" % msg_ports) + msg_ports = [] + for port in failed_ports: + port_type_text = "TCP" if port.port_type == SOCK_STREAM else "UDP" + msg_ports.append('%d (%s)' % (port.port, port_type_text)) + raise RuntimeError("Port check failed! Inaccessible port(s): %s" \ + % ", ".join(msg_ports)) def main(): safe_options, options = parse_options() @@ -276,15 +283,26 @@ def main(): if options.master: # check ports on master first print_info("Check connection from replica to remote master '%s':" % options.master) - port_check( options.master, required_ports) + tcp_ports = [ port for port in required_ports if port.port_type == SOCK_STREAM ] + udp_ports = [ port for port in required_ports if port.port_type == SOCK_DGRAM ] + port_check(options.master, tcp_ports) + + if udp_ports: + print_info("\nThe following list of ports use UDP protocol and would need to be") + print_info("checked manually:") + for port in udp_ports: + result = "SKIPPED" + print_info(" %s (%d): %s" % (port.description, port.port, result)) + print_info("\nConnection from replica to master is OK.") # create listeners global RESPONDERS print_info("Start listening on required ports for remote master check") + for port in required_ports: root_logger.debug("Start listening on port %d (%s)" % (port.port, port.description)) - responder = PortResponder(port.port, port.stream) + responder = PortResponder(port.port, port.port_type) responder.start() RESPONDERS.append(responder) diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index d9b0455e54949a75717553cb441ae42bfac56512..a0cf1998d4b9f5183f38fa480d6a0ba4145cf5ad 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -1101,15 +1101,10 @@ def get_gsserror(e): -def host_port_open(host, port, socket_stream=True, socket_timeout=None): +def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None): families = (socket.AF_INET, socket.AF_INET6) success = False - if socket_stream: - socket_type = socket.SOCK_STREAM - else: - socket_type = socket.SOCK_DGRAM - for family in families: try: try: @@ -1121,6 +1116,11 @@ def host_port_open(host, port, socket_stream=True, socket_timeout=None): s.settimeout(socket_timeout) s.connect((host, port)) + + if socket_type == socket.SOCK_DGRAM: + s.send('') + s.recv(512) + success = True except socket.error, e: pass @@ -1132,14 +1132,9 @@ def host_port_open(host, port, socket_stream=True, socket_timeout=None): return False -def bind_port_responder(port, socket_stream=True, socket_timeout=None, responder_data=None): +def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=None, responder_data=None): families = (socket.AF_INET, socket.AF_INET6) - if socket_stream: - socket_type = socket.SOCK_STREAM - else: - socket_type = socket.SOCK_DGRAM - host = '' # all available interfaces for family in families: @@ -1152,13 +1147,13 @@ def bind_port_responder(port, socket_stream=True, socket_timeout=None, responder if socket_timeout is not None: s.settimeout(socket_timeout) - if socket_stream: + if socket_type == socket.SOCK_STREAM: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((host, port)) - if socket_stream: + if socket_type == socket.SOCK_STREAM: s.listen(1) connection, client_address = s.accept() try: @@ -1166,8 +1161,8 @@ def bind_port_responder(port, socket_stream=True, socket_timeout=None, responder connection.sendall(responder_data) #pylint: disable=E1101 finally: connection.close() - else: - data, addr = s.recvfrom( 512 ) # buffer size is 1024 bytes + elif socket_type == socket.SOCK_DGRAM: + data, addr = s.recvfrom(1) if responder_data: s.sendto(responder_data, addr) -- 1.7.7.6
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel