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 switch_relay(): wg = WireGuard() old_server_pubkey = wg.info( config.IFNAME )[0].WGDEVICE_A_PEERS['value'][0].WGPEER_A_PUBLIC_KEY['value'].decode() wg_set(remove=True, public_key=old_server_pubkey) server_ip, server_pubkey = pick_relay() wg_set(endpoint_addr=server_ip, public_key=server_pubkey, allowed_ips=config.ALLOWED_IPS)
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 main(args=None): """ This is executed when run from the command line """ try: WireGuard() except: # Wireguard module load failed pass parser = argparse.ArgumentParser() # Required positional argument parser.add_argument("run", help="Required parameter to start agent") # Optional verbosity counter (eg. -v, -vv, -vvv, etc.) parser.add_argument("-v", "--verbose", action="count", default=0, help="Verbosity (-v, -vv, etc)") # Specify output of "--version" parser.add_argument( "--version", action="version", version="%(prog)s (version {version})".format(version=__version__)) args = parser.parse_args() agent(args)
def get_stats(format, interface, extended=True): clients = WireGuard().info(interface)[0].WGDEVICE_A_PEERS.value combined = {"interface": interface} peers = [] established_peers_count = 0 for client in clients: public_key = client.WGPEER_A_PUBLIC_KEY["value"].decode("utf-8") # ignore dummy key if public_key == (43 * "0" + "="): continue established = False latest_handshake = client.WGPEER_A_LAST_HANDSHAKE_TIME["tv_sec"] if datetime.now() - datetime.fromtimestamp(latest_handshake) < TIMEOUT: established = True established_peers_count += 1 if extended: peers.append({ "public_key": public_key, "latest_handshake": latest_handshake, "is_established": established, "rx_bytes": int(client.WGPEER_A_RX_BYTES['value']), "tx_bytes": int(client.WGPEER_A_TX_BYTES['value']), }) if extended: combined["peers"] = peers combined["established_peers_count"] = established_peers_count return combined
def _get_wg_mock(peer): info_mock = mock.Mock() info_mock.WGDEVICE_A_PEERS.value = [peer] wg_instance = WireGuard() wg_info_mock = wg_instance.__enter__.return_value wg_info_mock.set.return_value = {"WireGuard": "set"} wg_info_mock.info.return_value = [info_mock] return wg_info_mock
def __init__(self, client, interval=60): super().__init__() self.client = client self.interval = interval self.wg = WireGuard() if module_loaded( "wireguard") else WireGuardRead() self.stop_peer_watcher = threading.Event() self.daemon = True
def add_default(): # see https://www.wireguard.com/netns/#the-classic-solutions ip = IPRoute() wg = WireGuard() wg_set(fwmark=51820) add_route('0.0.0.0/0', table=51820) ip.rule('add', fwmark=51820, flags=2, table=51820) ip.rule('add', FRA_SUPPRESS_PREFIXLEN=0, table=254)
def _get_wg_mock(key_name, stale_time): pm = mock.Mock() pm.get_attr.side_effect = [{"tv_sec": stale_time}, key_name.encode()] peer_mock = mock.Mock() peer_mock.get_attr.side_effect = [[pm]] wg_instance = WireGuard() wg_info_mock = wg_instance.__enter__.return_value wg_info_mock.set.return_value = {"WireGuard": "set"} wg_info_mock.info.return_value = [peer_mock] return wg_info_mock
def __init__(self, client, interval=1): logger.debug(f"[REROUTING] Initializing") super().__init__() self.interval = interval self.client = client self.wg = WireGuard() if module_loaded( "wireguard") else WireGuardRead() self.routes = Routes() self.stop_rerouting = threading.Event() self.daemon = True
def wireguard_handler(client: WireGuardClient) -> Dict: with WireGuard() as wg: wg_peer = { "public_key": client.public_key, "persistent_keepalive": 15, "allowed_ips": [client.lladdr], "remove": client.remove, } return wg.set(client.wg_interface, peer=wg_peer)
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 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)
def pull_from_wireguard(self): with WireGuard() as wg: clients = wg.info(self.wg_interface)[0].WGDEVICE_A_PEERS.value for client in clients: latest_handshake = client.WGPEER_A_LAST_HANDSHAKE_TIME["tv_sec"] public_key = client.WGPEER_A_PUBLIC_KEY["value"].decode("utf-8") peer = self.find_by_public_key(public_key) if len(peer) < 1: peer = WireGuardPeer(public_key) self.all_peers.append(peer) else: peer = peer[0] peer.latest_handshake = datetime.fromtimestamp(latest_handshake)
def find_stale_wireguard_clients(wg_interface: str) -> List: with WireGuard() as wg: clients = wg.info(wg_interface)[0].WGDEVICE_A_PEERS.value three_hours_ago = (datetime.now() - timedelta(hours=3)).timestamp() stale_clients = [] for client in clients: latest_handshake = client.WGPEER_A_LAST_HANDSHAKE_TIME["tv_sec"] if latest_handshake < int(three_hours_ago): stale_clients.append( client.WGPEER_A_PUBLIC_KEY["value"].decode("utf-8") ) return stale_clients
def wg_peer(ifname, pubkey, **kwargs): """ **kwargs 这里都是可以选项: { 'remove': false, 'preshared_key': 'Pz8/V2FudFRvVHJ5TXlBZXJvR3Jvc3NlQmljaGU/Pz8=', 'endpoint_addr': '8.8.8.8', 'endpoint_port': 9999, 'persistent_keepalive': 25, 'allowed_ips': ['::/0'], } """ peer = {} peer['public_key'] = pubkey peer.update(**kwargs) with WireGuard() as wg: wg.set(ifname, peer=peer)
def wg_peer(ifname, pubkey, peer): """ **kwargs 这里都是可以选项: { 'remove': false, 'public_key': 'l5NCG5NmhSB4rbFVGZACPiKEL01+tQnjD6dRHCjXtkQ=', 'preshared_key': 'Pz8/V2FudFRvVHJ5TXlBZXJvR3Jvc3NlQmljaGU/Pz8=', 'endpoint_addr': '8.8.8.8', # 这里只能是IP, 不能是域名. 'endpoint_port': 9999, 'persistent_keepalive': 25, 'allowed_ips': ['::/0'], } """ # 如果endpoint_addr 是 域名,解析成ip # ipv6 也许会有问题 addr = peer.get("endpoint_addr") if addr is not None: try: ipaddress.ip_address(addr) ip = addr except ValueError: ip = socket.gethostbyname(addr) peer["endpoint_addr"] = ip # check allowed-ips 都是网络地址 peer["allowed_ips"] = peer.get("allowed_ips").split(",") #if allowed_ips is not None: # for network in allowed_ips: # try: # ipaddress.ip_network(network) # except ValueError: # #raise ValueError(f"allowed-ips: {network} 不是网络地址, 或网络地址不正确。") peer['public_key'] = pubkey with WireGuard() as wg: wg.set(ifname, peer=peer)
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 wg_delete_peer(ifname, pubkey): peer = {} peer["public_key"] = pubkey peer["remove"] = True with WireGuard() as wg: wg.set(ifname, peer=peer)
def wg_fwmark(ifname, fwmark): with WireGuard() as wg: wg.set(ifname, fwmark=fwmark)
def wg_set(ifname, private_key, listen_port=None, fwmark=0): with WireGuard() as wg: wg.set(ifname, private_key=private_key, listen_port=listen_port, fwmark=fwmark)
def __init__(self, interval=10): self.interval = interval self.wg = WireGuard() if module_loaded( "wireguard") else WireGuardRead()
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']