From f798239f90f7a0a2379244e2294a52078a0afaca Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 27 Feb 2020 16:36:16 -0500 Subject: [PATCH] Switch to using the standard confluent port for credserver Also add a check and only accept API arming requests from local ips --- confluent_server/confluent/credserver.py | 105 +++++++++++------------ confluent_server/confluent/netutil.py | 23 ++++- confluent_server/confluent/sockapi.py | 7 +- misc/clortho.c | 4 +- 4 files changed, 75 insertions(+), 64 deletions(-) diff --git a/confluent_server/confluent/credserver.py b/confluent_server/confluent/credserver.py index 3017fd61..ebbd5a2c 100644 --- a/confluent_server/confluent/credserver.py +++ b/confluent_server/confluent/credserver.py @@ -15,6 +15,7 @@ # limitations under the License. import confluent.config.configmanager as cfm +import confluent.netutil as netutil import datetime import eventlet import eventlet.green.socket as socket @@ -22,66 +23,56 @@ import eventlet.greenpool import os class CredServer(object): - def __init__(self, bindhost='::', bindport=301, ttl=1): - self.srv = socket.socket(socket.AF_INET6) - self.srv.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl) - self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.srv.bind((bindhost, bindport)) - self.srv.listen(32) - self.gpool = eventlet.greenpool.GreenPool(256) + def __init__(self): self.cfm = cfm.ConfigManager(None) - self.runtime = eventlet.spawn(self.listen) - def listen(self): - while True: - client, info = self.srv.accept() - if info[1] > 1023: - client.close() - continue - self.gpool.spawn_n(self.handle_client, client) - - def handle_client(self, client): - client.send('\xc2\xd1-\xa8\x80\xd8j\xba') - tlv = bytearray(client.recv(2)) - if tlv[0] != 1: - client.close() - return - nodename = client.recv(tlv[1]) - tlv = bytearray(client.recv(2)) - apiarmed = self.cfm.get_node_attributes(nodename, 'api.armed') - apiarmed = apiarmed.get(nodename, {}).get('api.armed', {}).get('value', None) - if not apiarmed: - client.close() - return - if apiarmed not in ('armed', 'continuous'): - now = datetime.datetime.utcnow() - expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ") - if now > expiry: - self.cfm.set_node_attributes({nodename: {'api.armed': ''}}) - client.close() - return - client.send(b'\x02\x20') - rttoken = os.urandom(32) - client.send(rttoken) - client.send('\x00\x00') - tlv = bytearray(client.recv(2)) - if tlv[0] != 3: - client.close() - return - echotoken = client.recv(tlv[1]) - if echotoken != rttoken: - client.close() - return - tlv = bytearray(client.recv(2)) - if tlv[0] != 4: - client.close() - return - echotoken = client.recv(tlv[1]) - if apiarmed != 'continuous': - self.cfm.set_node_attributes({nodename: {'api.key': echotoken, 'api.armed': ''}}) - client.recv(2) # drain end of message - client.send('\x05\x00') # report success + def handle_client(self, client, peer): + if not netutil.address_is_local(peer[0]): client.close() + return + client.send('\xc2\xd1-\xa8\x80\xd8j\xba') + tlv = bytearray(client.recv(2)) + if tlv[0] != 1: + client.close() + return + nodename = client.recv(tlv[1]) + tlv = bytearray(client.recv(2)) + apiarmed = self.cfm.get_node_attributes(nodename, 'api.armed') + apiarmed = apiarmed.get(nodename, {}).get('api.armed', {}).get('value', None) + if not apiarmed: + client.close() + return + if apiarmed not in ('once', 'continuous'): + now = datetime.datetime.utcnow() + expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ") + if now > expiry: + self.cfm.set_node_attributes({nodename: {'api.armed': ''}}) + client.close() + return + client.send(b'\x02\x20') + rttoken = os.urandom(32) + client.send(rttoken) + client.send('\x00\x00') + tlv = bytearray(client.recv(2)) + if tlv[0] != 3: + client.close() + return + echotoken = client.recv(tlv[1]) + if echotoken != rttoken: + client.close() + return + tlv = bytearray(client.recv(2)) + if tlv[0] != 4: + client.close() + return + echotoken = client.recv(tlv[1]) + cfgupdate = {nodename: {'api.key': echotoken, 'api.armed': ''}} + if apiarmed == 'continuous': + del cfgupdate[nodename]['api.armed'] + self.cfm.set_node_attributes(cfgupdate) + client.recv(2) # drain end of message + client.send('\x05\x00') # report success + client.close() if __name__ == '__main__': a = CredServer() diff --git a/confluent_server/confluent/netutil.py b/confluent_server/confluent/netutil.py index d268551f..23844929 100644 --- a/confluent_server/confluent/netutil.py +++ b/confluent_server/confluent/netutil.py @@ -18,6 +18,7 @@ import confluent.exceptions as exc import codecs +import netifaces import struct import eventlet.green.socket as socket import eventlet.support.greendns @@ -57,6 +58,20 @@ def ip_on_same_subnet(first, second, prefix): return ip & mask == oip & mask +def address_is_local(address): + for iface in netifaces.interfaces(): + for i4 in netifaces.ifaddresses(iface).get(2, []): + cidr = mask_to_cidr(i4['netmask']) + if ip_on_same_subnet(i4['addr'], address, cidr): + return True + for i6 in netifaces.ifaddresses(iface).get(10, []): + cidr = int(i6['netmask'].split('/')[1]) + laddr = i6['addr'].split('%')[0] + if ip_on_same_subnet(laddr, address, cidr): + return True + return False + + # TODO(jjohnson2): have a method to arbitrate setting methods, to aid # in correct matching of net.* based on parameters, mainly for pxe # The scheme for pxe: @@ -68,17 +83,17 @@ def ip_on_same_subnet(first, second, prefix): # if switch and port available, that should match. def get_nic_config(configmanager, node, ip=None, mac=None): """Fetch network configuration parameters for a nic - + For a given node and interface, find and retrieve the pertinent network configuration data. The desired configuration can be searched either by ip or by mac. - - :param configmanager: The relevant confluent.config.ConfigManager + + :param configmanager: The relevant confluent.config.ConfigManager instance. :param node: The name of the node :param ip: An IP address on the intended subnet :param mac: The mac address of the interface - + :returns: A dict of parameters, 'ipv4_gateway', .... """ # ip parameter *could* be the result of recvmsg with cmsg to tell diff --git a/confluent_server/confluent/sockapi.py b/confluent_server/confluent/sockapi.py index 6fe50770..b349afc8 100644 --- a/confluent_server/confluent/sockapi.py +++ b/confluent_server/confluent/sockapi.py @@ -37,6 +37,7 @@ import eventlet.green.ssl as ssl import eventlet import confluent.auth as auth +import confluent.credserver as credserver import confluent.tlvdata as tlvdata import confluent.consoleserver as consoleserver import confluent.config.configmanager as configmanager @@ -352,9 +353,13 @@ def _tlshandler(bind_host, bind_port): # Enable TCP_FASTOPEN plainsocket.setsockopt(socket.SOL_TCP, 23, 5) plainsocket.listen(5) + cs = credserver.CredServer() while (1): # TODO: exithook cnn, addr = plainsocket.accept() - eventlet.spawn_n(_tlsstartup, cnn) + if addr[1] < 1000: + eventlet.spawn_n(cs.handle_client, cnn, addr) + else: + eventlet.spawn_n(_tlsstartup, cnn) if ffi: diff --git a/misc/clortho.c b/misc/clortho.c index 73753fd2..16780a91 100644 --- a/misc/clortho.c +++ b/misc/clortho.c @@ -64,11 +64,11 @@ int main(int argc, char* argv[]) { strncpy(buffer + 3, cryptedpass, 8); free(cryptedpass); cryptedpass = crypt(passwd, buffer); - if (argc < 3) { + if (argc < 3) { fprintf(stderr, "Missing node name and manager\n"); exit(1); } - sock = getaddrinfo(argv[2], "301", &hints, &addrs); + sock = getaddrinfo(argv[2], "13001", &hints, &addrs); if (sock != 0) { fprintf(stderr, "Error trying to resolve %s\n", argv[2]); exit(1);