From ccbbf67e9704193b5f06e0268380772a25d7797b Mon Sep 17 00:00:00 2001 From: Davide Principi Date: Tue, 6 Aug 2024 14:58:31 +0200 Subject: [PATCH 1/2] Clean up destmap redundant entry The key of the local node (self_id) is skipped with this commit because there is nothing to do with it: neither wg config, nor ip routing table. --- core/imageroot/usr/local/sbin/apply-vpn-routes | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/imageroot/usr/local/sbin/apply-vpn-routes b/core/imageroot/usr/local/sbin/apply-vpn-routes index 5ec2a4d1b..fd71d3088 100755 --- a/core/imageroot/usr/local/sbin/apply-vpn-routes +++ b/core/imageroot/usr/local/sbin/apply-vpn-routes @@ -35,19 +35,17 @@ agent.assert_exp(cluster_network) # Convert Redis VPN records to our VPN data model destmap = {} for node_id in peers: - if self_id == leader_id: + if self_id == node_id: + # Skip the destinations for the local node: they use default + # kernel routing rules. + pass + elif self_id == leader_id: # The local node is the Leader and the VPN hub. One node # corresponds to a VPN peer, and its destinations are the node # destinations with its IP address destmap.setdefault(node_id, set()) destmap[node_id].update(peers[node_id]['destinations'].split()) destmap[node_id].add(peers[node_id]['ip_address']) - - elif self_id == node_id: - # Skip the destinations for the local node: they use default - # kernel routing rules. - pass - else: # Default VPN routing rule: send all traffic to the Leader destmap.setdefault(leader_id, set()) From a5b582f5ce1f769ab3bdbfc8c1a39ce0e6a677fa Mon Sep 17 00:00:00 2001 From: Davide Principi Date: Tue, 6 Aug 2024 15:15:49 +0200 Subject: [PATCH 2/2] Fix VPN routing error Never push an endpoint IP inside the VPN itself. If two nodes are in distinct private LAN networks, avoid bad VPN routing configuration. --- .../imageroot/usr/local/sbin/apply-vpn-routes | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/core/imageroot/usr/local/sbin/apply-vpn-routes b/core/imageroot/usr/local/sbin/apply-vpn-routes index fd71d3088..5adde46d9 100755 --- a/core/imageroot/usr/local/sbin/apply-vpn-routes +++ b/core/imageroot/usr/local/sbin/apply-vpn-routes @@ -52,21 +52,20 @@ for node_id in peers: destmap[leader_id].update(peers[node_id]['destinations'].split()) destmap[leader_id].add(peers[node_id]['ip_address']) -def get_wgset_endpoint_clause(endpoint): +def resolve_endpoint(endpoint): """Resolve the endpoint IP address and return the endpoint arguments for the `wg set` subcommand""" if not endpoint: - return [] - + return (None, None) address, port = endpoint.rsplit(':') - try: addrinfo = socket.getaddrinfo(address, port) - # Get the IP address (the last 0) of the first entry (first 0) from - # sockaddr item (index 4) - return ['endpoint', ':'.join([addrinfo[0][4][0], port])] - except: - return [] + except Exception as ex: + print(agent.SD_ERR+f"Endpoint {address} resolution failed", ex, file=sys.stderr) + return (None, port) + # Get the IP address (the last 0) of the first entry (first 0) from + # sockaddr item (index 4) + return (addrinfo[0][4][0] , str(port)) # Find the networks of the local interfaces. Addresses of these networks # are not routed through the VPN. @@ -83,11 +82,13 @@ for iface in ipaddr_reply: errors = 0 valid_destinations = [] for node_id in destmap: + peer_host, peer_port = resolve_endpoint(peers[node_id].get('endpoint', '')) + allowed_ips = destmap[node_id] - {peer_host} # Apply immediately the new configuration to the WireGuard wg0 interface... wset_proc = agent.run_helper('wg', 'set', 'wg0', 'peer', peers[node_id]["public_key"], 'persistent-keepalive', '25', - 'allowed-ips', ','.join(destmap[node_id]), - *get_wgset_endpoint_clause(peers[node_id].get('endpoint', '')), + 'allowed-ips', ','.join(allowed_ips), + *(['endpoint', f"{peer_host}:{peer_port}"] if peer_host else []), # optional arguments log_command=True, ) if wset_proc.returncode != 0: @@ -95,7 +96,7 @@ for node_id in destmap: print(agent.SD_ERR + f'Runtime change of allowed-ips has failed for peer node/{node_id}', file=sys.stderr) # ...and to the system routing table: - for xdest in destmap[node_id]: + for xdest in allowed_ips: push_route = False odest = ipm.ip_address(xdest) # object form of xdest for network calculations