""" WireGuard VPN Manager - Server management, client CRUD, remote ADB Manage WireGuard VPN server, clients, and remote ADB connections over VPN tunnel. """ DESCRIPTION = "WireGuard VPN + Remote ADB manager" AUTHOR = "AUTARCH" VERSION = "1.0" CATEGORY = "defense" class WireGuardVPN: """Interactive WireGuard VPN menu.""" def __init__(self): from core.wireguard import get_wireguard_manager self.mgr = get_wireguard_manager() def show_menu(self): status = self.mgr.get_server_status() running = status.get('running', False) endpoint = status.get('endpoint', 'N/A') clients = self.mgr.get_all_clients() peer_status = self.mgr.get_peer_status() if running else {} # Count online peers online = 0 for c in clients: ps = peer_status.get(c.get('public_key', ''), {}) hs = ps.get('latest_handshake') if hs is not None and hs < 180: online += 1 print(f"\n{'='*55}") print(" WireGuard VPN Manager") print(f"{'='*55}") print(f" Interface: {status.get('interface', 'wg0')} | " f"Status: {'Running' if running else 'Stopped'}") print(f" Endpoint: {endpoint}") print(f" Clients: {len(clients)} ({online} online)") print() print(" -- Server --") print(" 1) Server Status") print(" 2) Start Interface") print(" 3) Stop Interface") print(" 4) Restart Interface") print() print(" -- Clients --") print(" 10) List All Clients") print(" 11) Create New Client") print(" 12) View Client Detail") print(" 13) Delete Client") print(" 14) Enable/Disable Client") print(" 15) Import Existing Peers") print() print(" -- Remote ADB --") print(" 20) ADB Connect (TCP/IP)") print(" 21) ADB Disconnect") print(" 22) Auto-Connect All Peers") print(" 23) List Remote ADB Devices") print() print(" -- USB/IP --") print(" 30) USB/IP Status") print(" 31) Load USB/IP Modules") print(" 32) List Remote USB Devices") print(" 33) Attach USB Device") print(" 34) Detach USB Device") print(" 35) List Attached Ports") print() print(" -- Config --") print(" 40) Generate Client Config") print(" 41) Show QR Code (terminal)") print(" 42) Refresh UPnP Mapping") print() print(" 0) Back") print() # ── Helpers ───────────────────────────────────────────────────── def _pick_client(self, prompt=" Select client #: "): """Select a client from the list.""" clients = self.mgr.get_all_clients() if not clients: print(" No clients configured.") return None print("\n Clients:") for i, c in enumerate(clients, 1): status = "ON " if c.get('enabled', True) else "OFF" print(f" {i}) [{status}] {c['name']} ({c['assigned_ip']})") try: choice = int(input(prompt).strip()) if 1 <= choice <= len(clients): return clients[choice - 1] except (ValueError, EOFError, KeyboardInterrupt): pass return None def _pick_client_ip(self, prompt=" Client IP (or # to select): "): """Get a client IP either directly or by selection.""" try: val = input(prompt).strip() except (EOFError, KeyboardInterrupt): return None if not val: return None # If numeric, treat as selection if val.isdigit(): clients = self.mgr.get_all_clients() idx = int(val) - 1 if 0 <= idx < len(clients): return clients[idx]['assigned_ip'] print(" Invalid selection.") return None return val # ── Server ───────────────────────────────────────────────────── def do_server_status(self): status = self.mgr.get_server_status() print(f"\n Server Status:") print(f" Interface: {status.get('interface', 'wg0')}") print(f" Running: {status.get('running', False)}") print(f" Public Key: {status.get('public_key', 'N/A')}") print(f" Endpoint: {status.get('endpoint', 'N/A')}") print(f" Listen Port: {status.get('listen_port', 'N/A')}") print(f" Peers: {status.get('peer_count', 0)}") if status.get('error'): print(f" Error: {status['error']}") def do_start(self): print(" Starting WireGuard interface...") result = self.mgr.start_interface() if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_stop(self): print(" Stopping WireGuard interface...") result = self.mgr.stop_interface() if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_restart(self): print(" Restarting WireGuard interface...") result = self.mgr.restart_interface() if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") # ── Clients ──────────────────────────────────────────────────── def do_list_clients(self): clients = self.mgr.get_all_clients() peer_status = self.mgr.get_peer_status() if not clients: print("\n No clients configured.") return print(f"\n {'Name':<20} {'IP':<16} {'Status':<8} {'Handshake':<20} {'RX/TX'}") print(f" {'-'*80}") for c in clients: ps = peer_status.get(c.get('public_key', ''), {}) hs = ps.get('latest_handshake') hs_str = ps.get('latest_handshake_str', 'never') if hs is not None and hs < 180: status = 'ONLINE' elif hs is not None: status = 'idle' else: status = 'offline' if not c.get('enabled', True): status = 'disabled' rx = ps.get('transfer_rx_str', '-') tx = ps.get('transfer_tx_str', '-') print(f" {c['name']:<20} {c['assigned_ip']:<16} {status:<8} " f"{hs_str:<20} {rx}/{tx}") def do_create_client(self): try: name = input(" Client name: ").strip() except (EOFError, KeyboardInterrupt): return if not name: print(" Name required.") return try: dns = input(f" DNS [{self.mgr._default_dns}]: ").strip() allowed = input(f" Allowed IPs [{self.mgr._default_allowed_ips}]: ").strip() except (EOFError, KeyboardInterrupt): return print(f" Creating client '{name}'...") result = self.mgr.create_client( name, dns=dns or None, allowed_ips=allowed or None) if result.get('ok'): client = result['client'] print(f" Created: {client['name']} ({client['assigned_ip']})") print(f" ID: {client['id']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_view_client(self): client = self._pick_client() if not client: return print(f"\n Client: {client['name']}") print(f" ID: {client['id']}") print(f" IP: {client['assigned_ip']}") print(f" Public Key: {client['public_key']}") print(f" PSK: {'Yes' if client.get('preshared_key') else 'No'}") print(f" DNS: {client.get('dns', 'default')}") print(f" Allowed IPs: {client.get('allowed_ips', 'default')}") print(f" Enabled: {client.get('enabled', True)}") print(f" Created: {client.get('created_at', 'N/A')}") # Show live status peer_status = self.mgr.get_peer_status() ps = peer_status.get(client['public_key'], {}) if ps: print(f" Handshake: {ps.get('latest_handshake_str', 'never')}") print(f" Endpoint: {ps.get('endpoint', 'N/A')}") print(f" RX: {ps.get('transfer_rx_str', '-')}") print(f" TX: {ps.get('transfer_tx_str', '-')}") def do_delete_client(self): client = self._pick_client() if not client: return try: confirm = input(f" Delete '{client['name']}'? (y/N): ").strip().lower() except (EOFError, KeyboardInterrupt): return if confirm != 'y': print(" Cancelled.") return result = self.mgr.delete_client(client['id']) if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_toggle_client(self): client = self._pick_client() if not client: return current = client.get('enabled', True) new_state = not current action = 'Enable' if new_state else 'Disable' try: confirm = input(f" {action} '{client['name']}'? (y/N): ").strip().lower() except (EOFError, KeyboardInterrupt): return if confirm != 'y': print(" Cancelled.") return result = self.mgr.toggle_client(client['id'], new_state) if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_import_peers(self): print(" Importing existing peers from wg0.conf...") result = self.mgr.import_existing_peers() if result.get('ok'): print(f" Imported {result['imported']} peers.") else: print(f" Error: {result.get('error', 'Failed')}") # ── Remote ADB ───────────────────────────────────────────────── def do_adb_connect(self): clients = self.mgr.get_all_clients() if clients: print("\n Available clients:") for i, c in enumerate(clients, 1): print(f" {i}) {c['name']} ({c['assigned_ip']})") ip = self._pick_client_ip() if not ip: return print(f" Connecting to {ip}:5555...") result = self.mgr.adb_connect(ip) if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_adb_disconnect(self): ip = self._pick_client_ip(" Client IP to disconnect: ") if not ip: return result = self.mgr.adb_disconnect(ip) print(f" {result.get('message', 'Done')}") def do_auto_connect(self): print(" Auto-connecting to all active WG peers...") result = self.mgr.auto_connect_peers() for r in result.get('results', []): status = "OK" if r['result'].get('ok') else "FAIL" print(f" [{status}] {r['name']} ({r['ip']}): " f"{r['result'].get('message', r['result'].get('error', ''))}") if not result.get('results'): print(" No active peers found.") def do_list_adb_devices(self): devices = self.mgr.get_adb_remote_devices() if not devices: print("\n No remote ADB devices connected via WireGuard.") return print(f"\n Remote ADB Devices:") for d in devices: print(f" {d['serial']} - {d['state']} " f"{'(' + d['model'] + ')' if d.get('model') else ''}") # ── USB/IP ───────────────────────────────────────────────────── def do_usbip_status(self): status = self.mgr.get_usbip_status() print(f"\n USB/IP Status:") print(f" Available: {status['available']}") print(f" Modules loaded: {status['modules_loaded']}") print(f" Active imports: {status['active_imports']}") if status.get('ports'): for p in status['ports']: print(f" Port {p['port']}: {p['status']}") def do_load_modules(self): result = self.mgr.load_usbip_modules() if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_list_remote_usb(self): ip = self._pick_client_ip() if not ip: return print(f" Listing USB devices on {ip}...") result = self.mgr.usbip_list_remote(ip) if not result.get('ok'): print(f" Error: {result.get('error', 'Failed')}") return devices = result.get('devices', []) if not devices: print(" No exportable USB devices found.") return for d in devices: print(f" [{d['busid']}] {d['description']}") def do_attach_usb(self): ip = self._pick_client_ip(" Remote host IP: ") if not ip: return # List devices first result = self.mgr.usbip_list_remote(ip) devices = result.get('devices', []) if not devices: print(" No exportable devices found.") return print("\n Available devices:") for i, d in enumerate(devices, 1): print(f" {i}) [{d['busid']}] {d['description']}") try: choice = input(" Attach #: ").strip() except (EOFError, KeyboardInterrupt): return if choice.isdigit(): idx = int(choice) - 1 if 0 <= idx < len(devices): busid = devices[idx]['busid'] else: print(" Invalid selection.") return else: busid = choice print(f" Attaching {busid} from {ip}...") result = self.mgr.usbip_attach(ip, busid) if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_detach_usb(self): # Show current ports ports = self.mgr.usbip_port_status() if not ports.get('ports'): print(" No attached USB/IP devices.") return print("\n Attached ports:") for p in ports['ports']: print(f" Port {p['port']}: {p['status']}") try: port = input(" Detach port #: ").strip() except (EOFError, KeyboardInterrupt): return if not port: return result = self.mgr.usbip_detach(port) if result.get('ok'): print(f" {result['message']}") else: print(f" Error: {result.get('error', 'Failed')}") def do_list_ports(self): result = self.mgr.usbip_port_status() if not result.get('ok'): print(f" Error: {result.get('error', 'Failed')}") return ports = result.get('ports', []) if not ports: print(" No attached USB/IP ports.") return for p in ports: detail = f" - {p['detail']}" if p.get('detail') else '' print(f" Port {p['port']}: {p['status']}{detail}") # ── Config ───────────────────────────────────────────────────── def do_gen_config(self): client = self._pick_client() if not client: return config = self.mgr.generate_client_config(client) print(f"\n Config for {client['name']}:\n") print(f" {'─'*40}") for line in config.split('\n'): print(f" {line}") print(f" {'─'*40}") def do_show_qr(self): client = self._pick_client() if not client: return config = self.mgr.generate_client_config(client) try: import qrcode qr = qrcode.QRCode(box_size=1, border=1) qr.add_data(config) qr.make(fit=True) qr.print_ascii(invert=True) except ImportError: print(" qrcode module not installed. Install: pip install qrcode") def do_refresh_upnp(self): print(" Refreshing UPnP mapping for WireGuard port...") result = self.mgr.refresh_upnp_mapping() if result.get('ok'): print(f" UPnP mapping refreshed.") else: print(f" Error: {result.get('error', 'Failed')}") # ── Main Loop ────────────────────────────────────────────────── def run_interactive(self): while True: self.show_menu() try: choice = input(" Select > ").strip() except (EOFError, KeyboardInterrupt): break if choice == '0': break actions = { '1': self.do_server_status, '2': self.do_start, '3': self.do_stop, '4': self.do_restart, '10': self.do_list_clients, '11': self.do_create_client, '12': self.do_view_client, '13': self.do_delete_client, '14': self.do_toggle_client, '15': self.do_import_peers, '20': self.do_adb_connect, '21': self.do_adb_disconnect, '22': self.do_auto_connect, '23': self.do_list_adb_devices, '30': self.do_usbip_status, '31': self.do_load_modules, '32': self.do_list_remote_usb, '33': self.do_attach_usb, '34': self.do_detach_usb, '35': self.do_list_ports, '40': self.do_gen_config, '41': self.do_show_qr, '42': self.do_refresh_upnp, } action = actions.get(choice) if action: action() else: print(" Invalid choice.") def run(): wg = WireGuardVPN() wg.run_interactive()