Esempio n. 1
0
    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
Esempio n. 2
0
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)
Esempio n. 3
0
    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'))
Esempio n. 4
0
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)
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
 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
Esempio n. 8
0
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)
Esempio n. 9
0
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
Esempio n. 10
0
 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
Esempio n. 11
0
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)
Esempio n. 12
0
 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())
Esempio n. 13
0
    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)
Esempio n. 14
0
    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)
Esempio n. 15
0
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
Esempio n. 16
0
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)
Esempio n. 17
0
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)
Esempio n. 18
0
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)
Esempio n. 19
0
def wg_delete_peer(ifname, pubkey):
    peer = {}
    peer["public_key"] = pubkey
    peer["remove"] = True
    with WireGuard() as wg:
        wg.set(ifname, peer=peer)
Esempio n. 20
0
def wg_fwmark(ifname, fwmark):
    with WireGuard() as wg:
        wg.set(ifname, fwmark=fwmark)
Esempio n. 21
0
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)
Esempio n. 22
0
 def __init__(self, interval=10):
     self.interval = interval
     self.wg = WireGuard() if module_loaded(
         "wireguard") else WireGuardRead()
Esempio n. 23
0
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:')
Esempio n. 24
0
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']