def create(self, **kwargs): # Create WireGuard Interface self.ifname = kwargs.get('ifname') self.ip = kwargs.get('ip') with IPDB() as ip: dev = ip.create(kind='wireguard', ifname=self.ifname) dev.add_ip(self.ip) dev.up() dev.commit() wg = WireGuard() wg.set(self.ifname, private_key=kwargs.get('privatekey'), listen_port=kwargs.get('listenport'))
def wg_set(**kwargs): wg = WireGuard() interface_kwargs = ['private_key', 'fwmark', 'listen_port'] peer_kwargs = [ 'public_key', 'remove', 'preshared_key', 'endpoint_addr', 'endpoint_port', 'persistent_keepalive', 'allowed_ips' ] interface = {} peer = {} for arg, value in kwargs.items(): if arg in interface_kwargs: interface[arg] = value elif arg in peer_kwargs: peer[arg] = value if 'endpoint_addr' in peer and 'endpoint_port' not in peer: peer['endpoint_port'] = 51820 if peer: wg.set(config.IFNAME, **interface, peer=peer) else: wg.set(config.IFNAME, **interface)
def addpeer(self, **kwargs): # Create WireGuard object wg = WireGuard() # build peer dict peer = {} for key in kwargs.keys(): if key == 'publickey': peer = {**peer, **{'public_key': kwargs.get('publickey')}} if key == 'endpointaddr': peer = { **peer, **{ 'endpoint_addr': kwargs.get('endpointaddr') } } if key == 'endpointport': peer = { **peer, **{ 'endpoint_port': kwargs.get('endpointport') } } if key == 'keepalive': peer = { **peer, **{ 'persistent_keepalive': kwargs.get('keepalive') } } if key == 'allowedips': peer = {**peer, **{'allowed_ips': kwargs.get('allowedips')}} print(f"peer: {peer}") # add peer wg.set(self.ifname, peer=peer)
class TestBasic(object): @skip_if_not_supported def setup(self): require_user('root') self.ipnets = [] self.wg = WireGuard() self.log_id = uifname() self.ndb = NDB(log='../ndb-%s-%s.log' % (os.getpid(), self.log_id), rtnl_debug=True) self.netif = uifname() self.wg0if = uifname() self.wg1if = uifname() self.ifnames = [self.netif, self.wg0if, self.wg1if] self.ipnets.append(allocate_network()) self.ipnets.append(allocate_network()) self.ipranges = [[str(x) for x in net] for net in self.ipnets] # create the "network" interface (self.ndb.interfaces.create(ifname=self.netif, kind='dummy').set( 'state', 'up').add_ip('%s/24' % (self.ipranges[0][1])).commit()) # create one peer (self.ndb.interfaces.create(ifname=self.wg0if, kind='wireguard').set( 'state', 'up').add_ip('%s/24' % (self.ipranges[1][1])).commit()) # create another peer (self.ndb.interfaces.create(ifname=self.wg1if, kind='wireguard').set( 'state', 'up').add_ip('%s/24' % (self.ipranges[1][2])).commit()) def teardown(self): with NDB() as ndb: for i in self.ifnames: (ndb.interfaces[i].remove().commit()) for net in self.ipnets: free_network(net) def test_set_peer(self): # host A # # private key aPrZzfjeiNuy/oolBFkX4ClU3UjYzVemhK49TfZvMmU= # public key orXRcGaN/vxYm0fupIq/Q0ePQyDviyDRtAxAPNJMrA0= peerB = { 'public_key': 'waDwFcRFnPzGJqgaYoV3P7V3NOji5QNPNjUGuBrwwTI=', 'endpoint_addr': self.ipranges[1][1], 'endpoint_port': 12453, 'persistent_keepalive': 15, 'allowed_ips': ['%s/24' % self.ipranges[0][0], '%s/24' % self.ipranges[1][0]] } self.wg.set(self.wg0if, private_key='aPrZzfjeiNuy/oolBFkX4ClU3UjYzVemhK49TfZvMmU=', listen_port=12452, peer=peerB) # host B # # private key eHqiUofUM6A41mDVSTbBwFyfkDsVW7uEhv9A8romH2A= # public key waDwFcRFnPzGJqgaYoV3P7V3NOji5QNPNjUGuBrwwTI= peerA = { 'public_key': 'orXRcGaN/vxYm0fupIq/Q0ePQyDviyDRtAxAPNJMrA0=', 'endpoint_addr': self.ipranges[1][1], 'endpoint_port': 12452, 'persistent_keepalive': 15, 'allowed_ips': ['%s/24' % self.ipranges[0][0], '%s/24' % self.ipranges[1][0]] } self.wg.set(self.wg1if, private_key='eHqiUofUM6A41mDVSTbBwFyfkDsVW7uEhv9A8romH2A=', listen_port=12453, peer=peerA) assert grep('wg show %s' % self.wg0if, pattern='peer: %s' % peerB['public_key']) assert grep('wg show %s' % self.wg0if, pattern='endpoint: %s:%s' % (peerB['endpoint_addr'], peerB['endpoint_port'])) assert grep('wg show %s' % self.wg0if, pattern='transfer:')
class WgConf(): def __init__(self, client=None): self.wg_kernel = module_loaded('wireguard') self.wg = WireGuard() if self.wg_kernel else WireguardGo() self.ipdb = IPDB() self.routes = Routes() self.client = client def create_syntropy_interfaces(self, ifaces): result = [] for ifname in ifaces.keys(): int_data = self.create_interface(ifname, ifaces[ifname].get('ip')) if int_data.get('public_key') != ifaces[ifname].get('public_key'): result.append({"fn": "create_interface", "data": int_data}) @staticmethod def get_wg_interfaces(): with IPDB() as ipdb: current_interfaces = [ k for k, v in ipdb.by_name.items() if re.match(WG_NAME_PATTERN, k) ] return current_interfaces def clear_interfaces(self, dump): remote_interfaces = [ d['args']['ifname'] for d in dump if d['fn'] == 'create_interface' ] current_interfaces = self.get_wg_interfaces() remove_interfaces = set(current_interfaces) - set(remote_interfaces) logger.debug( f"Clearing interfaces REMOTE - {remote_interfaces}, CURRENT - {current_interfaces} REMOVE={remove_interfaces}" ) for interface in remove_interfaces: self.remove_interface(interface) def clear_unused_routes(self, dump): remote_peers = [d['args'] for d in dump if d['fn'] == 'add_peer'] remote_interfaces = [ d['args']['ifname'] for d in dump if d['fn'] == 'create_interface' ] for ifname in remote_interfaces: allowed_ips = [] remote_peers = [ allowed_ips.extend(peer['allowed_ips']) for peer in remote_peers if peer and peer['ifname'] == ifname ] self.routes.clear_unused_routes(ifname, allowed_ips) def clear_peers(self, dump): remote_peers = [ d['args']['public_key'] for d in dump if d['fn'] == 'add_peer' ] current_interfaces = self.get_wg_interfaces() for iface in current_interfaces: peers = get_peer_info(iface, self.wg) for peer in peers: if peer not in remote_peers: self.remove_peer(iface, peer) def get_wg_keys(self, ifname): private_key_path = f"/etc/syntropy-agent/privatekey-{ifname}" public_key_path = f"/etc/syntropy-agent/publickey-{ifname}" private_key = Path(private_key_path) public_key = Path(public_key_path) if not private_key.is_file() or not public_key.is_file(): privKey = PrivateKey.generate() pubKey = base64.b64encode(bytes(privKey.public_key)) privKey = base64.b64encode(bytes(privKey)) base64_privKey = privKey.decode('ascii') base64_pubKey = pubKey.decode('ascii') private_key.write_text(base64_privKey) public_key.write_text(base64_pubKey) private_key.chmod(0o600) public_key.chmod(0o600) if self.wg_kernel: return public_key.read_text().strip(), private_key.read_text( ).strip() else: return public_key.read_text().strip(), private_key_path def next_free_port(self, port=1024, max_port=65535): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) while port <= max_port: try: sock.bind(('', port)) sock.close() return port except OSError: port += 1 raise IOError('no free ports') def create_interface(self, ifname, internal_ip, listen_port=None, **kwargs): public_key, private_key = self.get_wg_keys(ifname) peer_metadata = {'metadata': get_peer_metadata(public_key=public_key)} logger.info( f"[WG_CONF] - Creating interface {ifname}, {internal_ip} - wg_kernel={self.wg_kernel}", extra={'metadata': peer_metadata}) if self.wg_kernel: create_interface(ifname) else: self.wg.create_interface(ifname) set_interface_up(ifname) set_interface_ip(ifname, internal_ip) self.routes.clear_unused_iface_addrs(ifname, internal_ip.split('/')[0]) try: self.wg.set(ifname, private_key=private_key, listen_port=listen_port) except NetlinkError as error: if error.code != 98: raise else: # if port was taken before creating. self.wg.set( ifname, private_key=private_key, ) listen_port = self.get_listening_port(ifname) if not listen_port: listen_port = find_free_port() self.wg.set(ifname, private_key=private_key, listen_port=listen_port) result = { "public_key": public_key, "listen_port": int(listen_port), "ifname": ifname } logger.debug(f"[WG_CONF] - interface_created {result}", extra={'metadata': peer_metadata}) return result def add_peer(self, ifname, public_key, allowed_ips, gw_ipv4, endpoint_ipv4=None, endpoint_port=None): peer_metadata = get_peer_metadata(public_key=public_key) if self.wg_kernel: try: peer_info = get_peer_info(ifname=ifname, wg=self.wg) except ValueError as e: raise WgConfException(str(e)) old_ips = set(peer_info.get(public_key, [])) - set(allowed_ips) self.routes.ip_route_del(ifname, old_ips) peer = { 'public_key': public_key, 'endpoint_addr': endpoint_ipv4, 'endpoint_port': endpoint_port, 'persistent_keepalive': 15, 'allowed_ips': allowed_ips } self.wg.set(ifname, peer=peer) statuses = self.routes.ip_route_add(ifname, allowed_ips, gw_ipv4) add_iptable_rules(allowed_ips) self.client.send_log( json.dumps({ 'id': "ID." + str(now()), 'executed_at': now(), "type": "WG_ROUTE_STATUS", "data": { "connection_id": peer_metadata.get('connection_id'), "public_key": public_key, "statuses": statuses, } })) def remove_peer(self, ifname, public_key, allowed_ips=None): if ifname not in self.get_wg_interfaces(): logger.debug(f'[WG_CONF] Remove peer - [{ifname}] does not exist') return peer = {'public_key': public_key, 'remove': True} try: self.wg.set(ifname, peer=peer) if allowed_ips: self.routes.ip_route_del(ifname, allowed_ips) delete_iptable_rule(allowed_ips) except pyroute2.netlink.exceptions.NetlinkError as error: if error.code != 19: raise return def remove_interface(self, ifname): logger.debug(f'[WG_CONF] Removing interfcae - [{ifname}]') delete_interface(ifname) logger.debug(f'[WG_CONF] Removed interfcae - [{ifname}]') return def get_listening_port(self, ifname): if self.wg_kernel: wg_info = dict(self.wg.info(ifname)[0]['attrs']) return wg_info['WGDEVICE_A_LISTEN_PORT'] else: wg_info = self.wg.info(ifname) return wg_info['listen_port']