From 89ea16a43f06590330e564b295315773b024dc18 Mon Sep 17 00:00:00 2001 From: alexz Date: Thu, 18 Dec 2025 08:29:09 +0000 Subject: [PATCH] Add build files with SSH client, VPN scripts, and auto-setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Includes: - Dockerfile extending dockurr/windows with openssh-client - SSH key for Windows VM access - Startup script for network setup and script deployment - VPN automation scripts (vpn-login.js, socks5.js, vpn.bat) - Windows setup scripts (install-nodejs.ps1, setup-autologin-sshd.ps1, setup-ssh-keys.ps1) - Technical README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/rego-tunnel/build/Dockerfile | 29 ++ apps/rego-tunnel/build/id_ed25519-lenovo | 7 + apps/rego-tunnel/build/rego-startup.sh | 66 +++ apps/rego-tunnel/build/scripts/README.md | 188 +++++++ .../build/scripts/install-nodejs.ps1 | 30 ++ .../build/scripts/setup-autologin-sshd.ps1 | 44 ++ .../build/scripts/setup-ssh-keys.ps1 | 32 ++ apps/rego-tunnel/build/scripts/socks5.js | 69 +++ apps/rego-tunnel/build/scripts/vpn-login.js | 459 ++++++++++++++++++ apps/rego-tunnel/build/scripts/vpn.bat | 4 + 10 files changed, 928 insertions(+) create mode 100644 apps/rego-tunnel/build/Dockerfile create mode 100644 apps/rego-tunnel/build/id_ed25519-lenovo create mode 100755 apps/rego-tunnel/build/rego-startup.sh create mode 100644 apps/rego-tunnel/build/scripts/README.md create mode 100644 apps/rego-tunnel/build/scripts/install-nodejs.ps1 create mode 100644 apps/rego-tunnel/build/scripts/setup-autologin-sshd.ps1 create mode 100644 apps/rego-tunnel/build/scripts/setup-ssh-keys.ps1 create mode 100644 apps/rego-tunnel/build/scripts/socks5.js create mode 100644 apps/rego-tunnel/build/scripts/vpn-login.js create mode 100644 apps/rego-tunnel/build/scripts/vpn.bat diff --git a/apps/rego-tunnel/build/Dockerfile b/apps/rego-tunnel/build/Dockerfile new file mode 100644 index 0000000..d720285 --- /dev/null +++ b/apps/rego-tunnel/build/Dockerfile @@ -0,0 +1,29 @@ +FROM dockurr/windows:latest + +# Install SSH client and utilities +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssh-client \ + sshpass \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Create SSH directory +RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh + +# Copy SSH key for Windows VM access +COPY id_ed25519-lenovo /root/.ssh/id_ed25519-lenovo +RUN chmod 600 /root/.ssh/id_ed25519-lenovo + +# Create directory for VPN scripts +RUN mkdir -p /opt/rego-scripts + +# Copy all VPN/setup scripts +COPY scripts/ /opt/rego-scripts/ +RUN chmod +x /opt/rego-scripts/*.js /opt/rego-scripts/*.bat 2>/dev/null || true + +# Copy custom startup hook +COPY rego-startup.sh /etc/cont-init.d/99-rego-startup +RUN chmod +x /etc/cont-init.d/99-rego-startup + +LABEL maintainer="alexz" +LABEL description="dockurr/windows with SSH client, VPN scripts, and rego-tunnel customizations" diff --git a/apps/rego-tunnel/build/id_ed25519-lenovo b/apps/rego-tunnel/build/id_ed25519-lenovo new file mode 100644 index 0000000..f7873e8 --- /dev/null +++ b/apps/rego-tunnel/build/id_ed25519-lenovo @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBh1EJ8NFn3hUUMe+lJSJl14bt8Xjh+NT5g0YnfKVitQwAAAJhfESWEXxEl +hAAAAAtzc2gtZWQyNTUxOQAAACBh1EJ8NFn3hUUMe+lJSJl14bt8Xjh+NT5g0YnfKVitQw +AAAEDgYZN7HwPbKY++p612bnhGC10P3GUHQdJlprPEFbODgWHUQnw0WfeFRQx76UlImXXh +u3xeOH41PmDRid8pWK1DAAAAEWFsZXh6QEFaYXdQQy1QMTZTAQIDBA== +-----END OPENSSH PRIVATE KEY----- diff --git a/apps/rego-tunnel/build/rego-startup.sh b/apps/rego-tunnel/build/rego-startup.sh new file mode 100755 index 0000000..9fe05de --- /dev/null +++ b/apps/rego-tunnel/build/rego-startup.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Rego-tunnel custom startup script +# Runs when container starts + +set -e + +echo "[rego] Initializing rego-tunnel customizations..." + +# Copy VPN scripts to shared folder if it exists +if [ -d "/shared" ]; then + echo "[rego] Copying VPN scripts to shared folder..." + mkdir -p /shared/vpn_scripts + cp -rn /opt/rego-scripts/* /shared/vpn_scripts/ 2>/dev/null || true + chmod -R 755 /shared/vpn_scripts/ + echo "[rego] Scripts available at \\\\TSCLIENT\\shared\\vpn_scripts\\" +fi + +# Background task: Wait for Windows and setup networking +( + WINDOWS_IP="" + MAX_WAIT=300 # 5 minutes max wait + + echo "[rego] Waiting for Windows VM to get IP..." + + for i in $(seq 1 $MAX_WAIT); do + WINDOWS_IP=$(cat /run/shm/qemu.ip 2>/dev/null || ip neigh show dev docker 2>/dev/null | grep -oE '172\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) + if [ -n "$WINDOWS_IP" ]; then + echo "[rego] Windows VM IP: $WINDOWS_IP" + break + fi + sleep 1 + done + + if [ -z "$WINDOWS_IP" ]; then + echo "[rego] Warning: Could not detect Windows IP after ${MAX_WAIT}s" + exit 0 + fi + + # Wait for SSH to be available + echo "[rego] Waiting for SSH on Windows..." + for i in $(seq 1 120); do + if nc -z "$WINDOWS_IP" 22 2>/dev/null; then + echo "[rego] SSH is available on Windows" + break + fi + sleep 2 + done + + # Setup iptables rules + echo "[rego] Setting up iptables rules..." + + # MASQUERADE for outbound traffic + iptables -t nat -C POSTROUTING -o docker -j MASQUERADE 2>/dev/null || \ + iptables -t nat -A POSTROUTING -o docker -j MASQUERADE + + # Route to IBM i network via Windows VPN + ip route add 10.35.33.0/24 via $WINDOWS_IP dev docker 2>/dev/null || true + + # Allow forwarding + iptables -C FORWARD -d $WINDOWS_IP -j ACCEPT 2>/dev/null || \ + iptables -A FORWARD -d $WINDOWS_IP -j ACCEPT + + echo "[rego] Network setup complete" +) & + +echo "[rego] Startup script initialized" diff --git a/apps/rego-tunnel/build/scripts/README.md b/apps/rego-tunnel/build/scripts/README.md new file mode 100644 index 0000000..5f16b78 --- /dev/null +++ b/apps/rego-tunnel/build/scripts/README.md @@ -0,0 +1,188 @@ +# Rego VPN Automation - Technical Setup Guide + +## Overview + +Cisco Secure Client VPN running in Windows VM (dockurr/windows) inside Docker container, with SOCKS5 proxy for transparent routing to IBM i systems. + +## Architecture + +``` +Clients → Host (iptables/redsocks) → Container (socat) → Windows VM (SOCKS5) → VPN → 10.35.33.x +``` + +## Components + +### 1. Windows VM (inside container) +- **Container**: `rego-tunnel_runtipi-rego-tunnel-1` +- **Windows VM IP**: `172.30.0.16` or `172.30.0.17` (internal to container) +- **VPN**: Cisco Secure Client with SAML auth (email + password + TOTP) +- **Files on Windows** (`C:\Users\alexz\vpn_scripts`): + - `vpn.bat` - Startup batch file + - `vpn-login.js` - Node.js script that automates SAML login via Chrome DevTools Protocol + - `socks5.js` - Simple SOCKS5 proxy server + - `node_modules/` - ws, otplib packages + +### 2. Container +- **External IPs**: `10.128.16.2` or similar +- **Internal bridge**: `172.30.0.1/24` (Windows VM at .16 or .17) +- **socat**: Forwards port 1080 from container to Windows VM SOCKS5 +- **start.sh**: Mounted at `/run/start.sh` - sets up iptables DNAT rules + +### 3. Host +- **redsocks**: Transparent SOCKS5 redirector (optional) +- **iptables**: Redirects traffic to VPN network through container + +## VPN Credentials + +Located in `vpn-login.js`: +```javascript +const CONFIG = { + email: "c-azaw@regoproducts.com", + password: "Fuckyou4suhail", + totpSecret: "RZQTQSKDWKHZ6ZYR", + devtoolsPort: 9222, + vpnTestIp: "10.35.33.230" +}; +``` + +## Windows Setup Steps + +### 1. Install Node.js +Run PowerShell as Administrator: +```powershell +# Option A: Run the install script +.\install-nodejs.ps1 + +# Option B: Manual download from https://nodejs.org/ +``` + +### 2. Install Cisco Secure Client +- Download from company VPN portal or Cisco +- Install with default options +- Path: `C:\Program Files (x86)\Cisco\Cisco Secure Client\` + +### 3. Setup VPN Scripts +```cmd +mkdir C:\Users\alexz\vpn_scripts +copy \\TSCLIENT\shared\vpn-scripts\*.js C:\Users\alexz\vpn_scripts\ +copy \\TSCLIENT\shared\vpn-scripts\vpn.bat C:\Users\alexz\vpn_scripts\ + +cd C:\Users\alexz\vpn_scripts +npm install ws otplib +``` + +### 4. Add to Windows Startup +```cmd +# Create shortcut to vpn.bat in: +shell:startup +# Or: C:\Users\alexz\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup +``` + +### 5. Enable Remote Debugging for Cisco UI +The vpn-login.js script sets this environment variable before launching Cisco: +``` +WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --remote-allow-origins=* +``` + +## Container Configuration + +### docker-compose.yml (user-config) +```yaml +services: + rego-tunnel: + environment: + USER: alexz + PASS: Az@83278327$$@@ + VERSION: win10 + entrypoint: ["/bin/bash", "-c", "source /run/start.sh; exec /usr/bin/tini -s /run/entry.sh"] +``` + +### start.sh (Container Startup Script) +Located at: `/etc/runtipi/user-config/runtipi/rego-tunnel/scripts/start.sh` + +Sets up: +- iptables MASQUERADE for docker bridge +- Route to IBM i network via Windows VM +- DNAT rules for port forwarding (SSH, IBM i ports) + +## Key Ports + +| Port | Service | +|------|---------| +| 22 | SSH | +| 23 | Telnet (IBM i) | +| 446, 448, 449 | IBM i services | +| 1080 | SOCKS5 proxy | +| 8006 | noVNC web console | +| 8470-8476 | IBM i data ports | +| 9222 | Chrome DevTools (for automation) | + +## Manual Commands + +### Start VPN from host: +```bash +docker exec rego-tunnel_runtipi-rego-tunnel-1 ssh docker@172.30.0.16 'C:\Users\alexz\vpn_scripts\vpn.bat' +``` + +### Start socat in container: +```bash +docker exec -d rego-tunnel_runtipi-rego-tunnel-1 socat TCP-LISTEN:1080,fork,reuseaddr TCP:172.30.0.16:1080 +``` + +### Test SOCKS5 connectivity: +```bash +nc -zv 10.128.16.2 1080 +``` + +### Check VPN status in Windows: +```cmd +ipconfig | findstr 10\. +``` + +## Troubleshooting + +### VPN not connecting +1. Check time sync: `w32tm /resync /force` +2. Verify Cisco agent: `net start "Cisco Secure Client Agent"` +3. Check DevTools: `http://172.30.0.16:9222/json` + +### SOCKS5 not working +1. Verify VPN connected first (ping 10.35.33.230) +2. Check socks5.js running: `tasklist | findstr node` +3. Test locally: `nc -zv 127.0.0.1 1080` + +### Container issues +1. Check logs: `docker logs rego-tunnel_runtipi-rego-tunnel-1` +2. Verify start.sh: `docker exec rego-tunnel_runtipi-rego-tunnel-1 cat /run/start.sh` +3. Check Windows VM IP: `docker exec rego-tunnel_runtipi-rego-tunnel-1 cat /run/qemu.pid` + +## File Locations + +### Host +- `/etc/runtipi/user-config/runtipi/rego-tunnel/docker-compose.yml` - User overrides +- `/etc/runtipi/user-config/runtipi/rego-tunnel/scripts/start.sh` - Container startup +- `/etc/runtipi/repos/runtipi/apps/rego-tunnel/docker-compose.yml` - Base config +- `/etc/runtipi/app-data/runtipi/rego-tunnel/data/storage/` - Windows disk image +- `/etc/runtipi/app-data/runtipi/rego-tunnel/data/shared/` - Shared folder with Windows + +### Windows VM +- `C:\Users\alexz\vpn_scripts\vpn-login.js` - Main automation script +- `C:\Users\alexz\vpn_scripts\socks5.js` - SOCKS5 proxy +- `C:\Users\alexz\vpn_scripts\vpn.bat` - Startup batch file +- `C:\Program Files (x86)\Cisco\Cisco Secure Client\` - Cisco installation + +## Watchdog Mode + +The vpn-login.js script includes a watchdog that: +- Monitors VPN connectivity every 2 minutes +- Auto-reconnects after 2 consecutive failures +- Restarts SOCKS5 proxy after reconnection +- Logs memory usage every hour + +## Notes + +- Windows VM takes ~2-3 minutes to boot +- VPN login takes ~30 seconds +- TOTP requires accurate system time (script syncs automatically) +- The container uses VERSION=win10 for dockurr/windows compatibility +- noVNC password: `Az@83278327$@@` diff --git a/apps/rego-tunnel/build/scripts/install-nodejs.ps1 b/apps/rego-tunnel/build/scripts/install-nodejs.ps1 new file mode 100644 index 0000000..c37f1c2 --- /dev/null +++ b/apps/rego-tunnel/build/scripts/install-nodejs.ps1 @@ -0,0 +1,30 @@ +# Install Node.js on Windows +# Run as Administrator in PowerShell + +$nodeVersion = "22.9.0" +$nodeUrl = "https://nodejs.org/dist/v$nodeVersion/node-v$nodeVersion-x64.msi" +$installerPath = "$env:TEMP\node-installer.msi" + +Write-Host "Downloading Node.js v$nodeVersion..." -ForegroundColor Cyan +Invoke-WebRequest -Uri $nodeUrl -OutFile $installerPath + +Write-Host "Installing Node.js..." -ForegroundColor Cyan +Start-Process msiexec.exe -Wait -ArgumentList "/i `"$installerPath`" /quiet /norestart" + +# Refresh PATH +$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + +Write-Host "Verifying installation..." -ForegroundColor Cyan +node --version +npm --version + +Write-Host "Node.js installed successfully!" -ForegroundColor Green + +# Cleanup +Remove-Item $installerPath -Force + +Write-Host "" +Write-Host "Next steps:" -ForegroundColor Yellow +Write-Host "1. Copy vpn-login.js, socks5.js, vpn.bat to C:\Users\alexz\vpn_scripts\" +Write-Host "2. Open CMD in C:\Users\alexz\vpn_scripts\ and run: npm install ws otplib" +Write-Host "3. Add vpn.bat shortcut to shell:startup folder" diff --git a/apps/rego-tunnel/build/scripts/setup-autologin-sshd.ps1 b/apps/rego-tunnel/build/scripts/setup-autologin-sshd.ps1 new file mode 100644 index 0000000..feb6eaa --- /dev/null +++ b/apps/rego-tunnel/build/scripts/setup-autologin-sshd.ps1 @@ -0,0 +1,44 @@ +# Setup OpenSSH Server and Auto-Login for alexz +# Run as Administrator in PowerShell + +$Username = "alexz" +$Password = "Az@83278327$$@@" + +Write-Host "=== Setting up OpenSSH Server ===" -ForegroundColor Cyan + +# Install OpenSSH Server +$sshCapability = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*' +if ($sshCapability.State -ne 'Installed') { + Write-Host "Installing OpenSSH Server..." -ForegroundColor Yellow + Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 +} else { + Write-Host "OpenSSH Server already installed" -ForegroundColor Green +} + +# Start and enable SSH service +Write-Host "Starting SSH service..." -ForegroundColor Yellow +Start-Service sshd +Set-Service -Name sshd -StartupType 'Automatic' + +# Configure firewall +$fwRule = Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue +if (-not $fwRule) { + Write-Host "Adding firewall rule..." -ForegroundColor Yellow + New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 +} + +Write-Host "OpenSSH Server configured!" -ForegroundColor Green + +Write-Host "" +Write-Host "=== Setting up Auto-Login ===" -ForegroundColor Cyan + +$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" +Set-ItemProperty -Path $RegPath -Name "AutoAdminLogon" -Value "1" +Set-ItemProperty -Path $RegPath -Name "DefaultUserName" -Value $Username +Set-ItemProperty -Path $RegPath -Name "DefaultPassword" -Value $Password +Remove-ItemProperty -Path $RegPath -Name "DefaultDomainName" -ErrorAction SilentlyContinue + +Write-Host "Auto-login configured for '$Username'!" -ForegroundColor Green +Write-Host "" +Write-Host "Setup complete! SSH is now available and auto-login is enabled." -ForegroundColor Green +Write-Host "Reboot to test auto-login." -ForegroundColor Yellow diff --git a/apps/rego-tunnel/build/scripts/setup-ssh-keys.ps1 b/apps/rego-tunnel/build/scripts/setup-ssh-keys.ps1 new file mode 100644 index 0000000..66634b3 --- /dev/null +++ b/apps/rego-tunnel/build/scripts/setup-ssh-keys.ps1 @@ -0,0 +1,32 @@ +# Setup SSH Keys for alexz user +# Run as Administrator in PowerShell + +$PublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGHUQnw0WfeFRQx76UlImXXhu3xeOH41PmDRid8pWK1D alexz@AZawPC-P16S" + +Write-Host "=== Setting up SSH Keys ===" -ForegroundColor Cyan + +# User authorized_keys +$userSshDir = "C:\Users\alexz\.ssh" +$userAuthKeys = "$userSshDir\authorized_keys" + +Write-Host "Creating user .ssh directory..." -ForegroundColor Yellow +New-Item -ItemType Directory -Path $userSshDir -Force | Out-Null + +Write-Host "Adding key to user authorized_keys..." -ForegroundColor Yellow +Add-Content -Path $userAuthKeys -Value $PublicKey -Force + +# Fix permissions for user file +icacls $userAuthKeys /inheritance:r /grant "alexz:F" /grant "SYSTEM:F" + +# Administrator authorized_keys (for admin users) +$adminAuthKeys = "C:\ProgramData\ssh\administrators_authorized_keys" + +Write-Host "Adding key to administrators_authorized_keys..." -ForegroundColor Yellow +Add-Content -Path $adminAuthKeys -Value $PublicKey -Force + +# Fix permissions for admin file (required by OpenSSH) +icacls $adminAuthKeys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" + +Write-Host "" +Write-Host "SSH keys configured!" -ForegroundColor Green +Write-Host "You can now SSH in with the id_ed25519-lenovo key." -ForegroundColor Yellow diff --git a/apps/rego-tunnel/build/scripts/socks5.js b/apps/rego-tunnel/build/scripts/socks5.js new file mode 100644 index 0000000..ea9438b --- /dev/null +++ b/apps/rego-tunnel/build/scripts/socks5.js @@ -0,0 +1,69 @@ +const net = require('net'); + +const PORT = 1080; +const HOST = '0.0.0.0'; + +const server = net.createServer((client) => { + client.once('data', (data) => { + // SOCKS5 handshake + if (data[0] !== 0x05) { + client.end(); + return; + } + + // No auth required + client.write(Buffer.from([0x05, 0x00])); + + client.once('data', (data) => { + if (data[0] !== 0x05 || data[1] !== 0x01) { + client.write(Buffer.from([0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); + client.end(); + return; + } + + let host, port; + const atyp = data[3]; + + if (atyp === 0x01) { + // IPv4 + host = `${data[4]}.${data[5]}.${data[6]}.${data[7]}`; + port = data.readUInt16BE(8); + } else if (atyp === 0x03) { + // Domain + const len = data[4]; + host = data.slice(5, 5 + len).toString(); + port = data.readUInt16BE(5 + len); + } else if (atyp === 0x04) { + // IPv6 + host = ''; + for (let i = 0; i < 16; i += 2) { + host += data.slice(4 + i, 6 + i).toString('hex'); + if (i < 14) host += ':'; + } + port = data.readUInt16BE(20); + } else { + client.write(Buffer.from([0x05, 0x08, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); + client.end(); + return; + } + + const remote = net.createConnection(port, host, () => { + const response = Buffer.from([0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]); + client.write(response); + client.pipe(remote); + remote.pipe(client); + }); + + remote.on('error', () => { + client.write(Buffer.from([0x05, 0x05, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); + client.end(); + }); + }); + }); + + client.on('error', () => {}); +}); + +server.listen(PORT, HOST, () => { + console.log(`SOCKS5 proxy running on ${HOST}:${PORT}`); +}); diff --git a/apps/rego-tunnel/build/scripts/vpn-login.js b/apps/rego-tunnel/build/scripts/vpn-login.js new file mode 100644 index 0000000..3f02151 --- /dev/null +++ b/apps/rego-tunnel/build/scripts/vpn-login.js @@ -0,0 +1,459 @@ +const WebSocket = require("ws"); +const { authenticator } = require("otplib"); +const { execSync, spawn } = require("child_process"); + +const CONFIG = { + email: "c-azaw@regoproducts.com", + password: "Fuckyou4suhail", + totpSecret: "RZQTQSKDWKHZ6ZYR", + devtoolsPort: 9222, + vpnTestIp: "10.35.33.230" +}; + +let ws; +let msgId = 1; + +function log(msg) { + console.log("[" + new Date().toLocaleTimeString() + "] " + msg); +} + +function run(cmd) { + try { execSync(cmd, { stdio: "ignore", timeout: 10000 }); } catch (e) {} +} + +function runOutput(cmd) { + try { + return execSync(cmd, { encoding: "utf8", timeout: 10000 }); + } catch (e) { + return ""; + } +} + +async function getPages() { + const res = await fetch("http://localhost:" + CONFIG.devtoolsPort + "/json"); + return res.json(); +} + +function send(method, params = {}) { + return new Promise((resolve, reject) => { + const id = msgId++; + const timeout = setTimeout(() => reject(new Error("Timeout: " + method)), 15000); + const handler = (data) => { + const msg = JSON.parse(data); + if (msg.id === id) { + clearTimeout(timeout); + ws.off("message", handler); + resolve(msg.result); + } + }; + ws.on("message", handler); + ws.send(JSON.stringify({ id, method, params })); + }); +} + +async function waitForSelector(selector, timeout = 30000) { + const start = Date.now(); + while (Date.now() - start < timeout) { + try { + const result = await send("Runtime.evaluate", { + expression: "document.querySelector('" + selector + "') !== null", + returnByValue: true + }); + if (result.result.value === true) return true; + } catch (e) {} + await sleep(500); + } + return false; +} + +async function typeText(selector, text) { + await send("Runtime.evaluate", { + expression: "var el = document.querySelector('" + selector + "'); el.focus(); el.value = '';" + }); + await sleep(100); + for (const char of text) { + await send("Input.dispatchKeyEvent", { type: "char", text: char }); + await sleep(30); + } +} + +async function click(selector) { + await send("Runtime.evaluate", { + expression: "document.querySelector('" + selector + "').click()" + }); +} + +async function clickSubmit() { + const methods = [ + "document.querySelector('#submitButton') && document.querySelector('#submitButton').click()", + "typeof Login !== 'undefined' && Login.submitLoginRequest && Login.submitLoginRequest()", + "document.querySelector('input[type=\"submit\"]') && document.querySelector('input[type=\"submit\"]').click()", + "document.querySelector('button[type=\"submit\"]') && document.querySelector('button[type=\"submit\"]').click()", + "document.querySelector('#idSIButton9') && document.querySelector('#idSIButton9').click()" + ]; + for (const expr of methods) { + try { await send("Runtime.evaluate", { expression: expr }); } catch (e) {} + } +} + +async function sleep(ms) { + return new Promise(r => setTimeout(r, ms)); +} + +async function waitForDevtools(maxWait = 120000) { + const start = Date.now(); + while (Date.now() - start < maxWait) { + try { + const pages = await getPages(); + const page = pages.find(p => p.type === "page"); + if (page) return page; + } catch (e) {} + log("Waiting for WebView..."); + await sleep(2000); + } + return null; +} + +// VPN adapter check skipped - IP range is unpredictable +// We rely solely on connectivity test to target IP + +// Test connectivity via ping (check for actual reply, not TTL expired) +function testVpnConnectivity(ip) { + try { + const output = execSync(`ping -n 1 -w 3000 ${ip}`, { encoding: "utf8", timeout: 5000 }); + // Must have "Reply from " - not "TTL expired" or "Request timed out" + return output.includes(`Reply from ${ip}`); + } catch (e) { + return false; + } +} + +// Verify VPN is connected with retries (connectivity test only) +async function verifyVpnConnection(maxRetries = 10, retryDelay = 5000) { + log("--- VPN VERIFICATION ---"); + + for (let i = 1; i <= maxRetries; i++) { + log(`Attempt ${i}/${maxRetries}: Pinging ${CONFIG.vpnTestIp}...`); + const connected = testVpnConnectivity(CONFIG.vpnTestIp); + + if (connected) { + log("VPN connectivity confirmed!"); + return true; + } + + log("Not reachable yet, waiting..."); + if (i < maxRetries) { + await sleep(retryDelay); + } + } + + log("VPN verification failed after " + maxRetries + " attempts"); + return false; +} + +async function main() { + console.log(""); + console.log("========================================"); + console.log(" CISCO VPN AUTO-LOGIN"); + console.log("========================================"); + console.log(""); + + // Sync time first (TOTP requires accurate time) + log("Syncing system time..."); + run("sc config w32time start= auto"); + run("net start w32time"); + run("w32tm /config /manualpeerlist:pool.ntp.org /syncfromflags:manual /update"); + run("w32tm /resync /force"); + await sleep(2000); + + // Kill everything + log("Killing Cisco processes..."); + run("taskkill /F /IM csc_ui.exe"); + run("taskkill /F /IM vpnui.exe"); + run("taskkill /F /IM vpnagent.exe"); + run('net stop csc_vpnagent'); + await sleep(2000); + + // Start agent + log("Starting Cisco agent..."); + run('net start csc_vpnagent'); + await sleep(3000); + + // Start UI + log("Starting Cisco UI..."); + process.env.WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS = "--remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --remote-allow-origins=*"; + spawn("C:\\Program Files (x86)\\Cisco\\Cisco Secure Client\\UI\\csc_ui.exe", [], { + detached: true, + stdio: "ignore" + }).unref(); + + await sleep(5000); + + // Wait for WebView + const page = await waitForDevtools(); + if (!page) { + log("ERROR: WebView not found - rebooting..."); + run("shutdown /r /t 1"); + process.exit(1); + } + + log("WebView: " + page.title); + ws = new WebSocket(page.webSocketDebuggerUrl); + await new Promise((resolve, reject) => { + ws.on("open", resolve); + ws.on("error", reject); + }); + + await send("DOM.enable"); + await send("Runtime.enable"); + await send("Input.enable"); + log("Connected to DevTools"); + + const url = page.url || ""; + const isADFS = url.includes("adfs"); + log("Login type: " + (isADFS ? "ADFS" : "Microsoft")); + + if (isADFS) { + log("--- ADFS LOGIN ---"); + if (!await waitForSelector("#passwordInput", 15000)) { + log("Password field not found"); + process.exit(1); + } + log("Entering password..."); + await typeText("#passwordInput", CONFIG.password); + await sleep(500); + log("Clicking Sign In..."); + await clickSubmit(); + await sleep(3000); + } else { + log("--- EMAIL ---"); + if (await waitForSelector('input[type="email"]', 5000)) { + log("Entering email..."); + await typeText('input[type="email"]', CONFIG.email); + await sleep(500); + log("Clicking Next..."); + await clickSubmit(); + await sleep(3000); + } + + log("--- PASSWORD ---"); + if (!await waitForSelector('input[type="password"]', 15000)) { + log("Password field not found"); + process.exit(1); + } + log("Entering password..."); + await typeText('input[type="password"]', CONFIG.password); + await sleep(500); + log("Clicking Sign In..."); + await clickSubmit(); + await sleep(3000); + } + + // TOTP + log("--- TOTP ---"); + if (await waitForSelector('input[name="otc"]', 15000)) { + await sleep(500); + const totp = authenticator.generate(CONFIG.totpSecret); + log("TOTP: " + totp); + await typeText('input[name="otc"]', totp); + await sleep(500); + log("Submitting..."); + await clickSubmit(); + await sleep(3000); + } else { + log("No TOTP field"); + } + + // Stay signed in + log("--- STAY SIGNED IN ---"); + if (await waitForSelector("#idBtn_Back", 5000)) { + log("Clicking No..."); + await click("#idBtn_Back"); + } + + await sleep(2000); + ws.close(); + + // Verify VPN connection before starting SOCKS5 + const vpnConnected = await verifyVpnConnection(); + + if (!vpnConnected) { + log("ERROR: VPN connection could not be verified"); + log("SOCKS5 proxy NOT started"); + process.exit(1); + } + + // Start SOCKS5 proxy only after VPN verified + log("Starting SOCKS5 proxy on port 1080..."); + run("taskkill /F /IM node.exe /FI \"WINDOWTITLE eq socks5\""); + spawn("node", ["C:\\Users\\alexz\\vpn_scripts\\socks5.js"], { + detached: true, + stdio: "ignore", + windowsHide: true + }).unref(); + + console.log(""); + console.log("========================================"); + console.log(" CONNECTED!"); + console.log(" SOCKS5 proxy: 172.30.0.16:1080"); + console.log(" Entering watchdog mode..."); + console.log("========================================"); + console.log(""); + + // Enter watchdog mode - monitor and reconnect if needed + await watchdogLoop(); +} + +async function watchdogLoop() { + const checkInterval = 2 * 60 * 1000; // 2 minutes + let consecutiveFailures = 0; + let checkCount = 0; + + log("Watchdog: Monitoring every 2 minutes..."); + + while (true) { + await sleep(checkInterval); + checkCount++; + + // Force garbage collection every 10 checks (~20 min) + if (checkCount % 10 === 0 && global.gc) { + global.gc(); + } + + // Log memory every 30 checks (~1 hour) + if (checkCount % 30 === 0) { + const mem = Math.round(process.memoryUsage().heapUsed / 1024 / 1024); + log(`Watchdog: Memory ${mem}MB, checks ${checkCount}`); + } + + const connected = testVpnConnectivity(CONFIG.vpnTestIp); + + if (connected) { + if (consecutiveFailures > 0) { + log("Watchdog: Connection restored"); + } + consecutiveFailures = 0; + } else { + consecutiveFailures++; + log(`Watchdog: Connection FAILED (${consecutiveFailures})`); + + if (consecutiveFailures >= 2) { + log("Watchdog: Reconnecting..."); + await reconnectVpn(); + consecutiveFailures = 0; + } + } + } +} + +async function reconnectVpn() { + // Sync time first (TOTP requires accurate time) + log("Syncing system time..."); + run("sc config w32time start= auto"); + run("net start w32time"); + run("w32tm /resync /force"); + await sleep(1000); + + // Kill and restart VPN + log("Killing Cisco processes..."); + run("taskkill /F /IM csc_ui.exe"); + run("taskkill /F /IM vpnui.exe"); + run("taskkill /F /IM vpnagent.exe"); + run('net stop "Cisco Secure Client Agent"'); + await sleep(2000); + + log("Starting Cisco agent..."); + run('net start "Cisco Secure Client Agent"'); + await sleep(3000); + + log("Starting Cisco UI..."); + spawn("C:\\Program Files (x86)\\Cisco\\Cisco Secure Client\\UI\\csc_ui.exe", [], { + detached: true, + stdio: "ignore" + }).unref(); + + await sleep(5000); + + const page = await waitForDevtools(); + if (!page) { + log("ERROR: WebView not found - rebooting..."); + run("shutdown /r /t 1"); + return false; + } + + ws = new WebSocket(page.webSocketDebuggerUrl); + await new Promise((resolve, reject) => { + ws.on("open", resolve); + ws.on("error", reject); + }); + + await send("DOM.enable"); + await send("Runtime.enable"); + await send("Input.enable"); + + const url = page.url || ""; + const isADFS = url.includes("adfs"); + + if (isADFS) { + if (await waitForSelector("#passwordInput", 15000)) { + await typeText("#passwordInput", CONFIG.password); + await sleep(500); + await clickSubmit(); + await sleep(3000); + } + } else { + if (await waitForSelector('input[type="email"]', 5000)) { + await typeText('input[type="email"]', CONFIG.email); + await sleep(500); + await clickSubmit(); + await sleep(3000); + } + if (await waitForSelector('input[type="password"]', 15000)) { + await typeText('input[type="password"]', CONFIG.password); + await sleep(500); + await clickSubmit(); + await sleep(3000); + } + } + + // TOTP + if (await waitForSelector('input[name="otc"]', 15000)) { + await sleep(500); + const totp = authenticator.generate(CONFIG.totpSecret); + log("TOTP: " + totp); + await typeText('input[name="otc"]', totp); + await sleep(500); + await clickSubmit(); + await sleep(3000); + } + + // Stay signed in - No + if (await waitForSelector("#idBtn_Back", 5000)) { + await click("#idBtn_Back"); + } + + await sleep(2000); + ws.close(); + + // Verify reconnection + const verified = await verifyVpnConnection(); + if (verified) { + log("Reconnection successful!"); + // Restart socks5 + run("taskkill /F /IM node.exe /FI \"WINDOWTITLE eq socks5\""); + spawn("node", ["C:\\Users\\alexz\\vpn_scripts\\socks5.js"], { + detached: true, + stdio: "ignore", + windowsHide: true + }).unref(); + return true; + } + + log("Reconnection failed"); + return false; +} + +main().catch(err => { + log("ERROR: " + err.message); + process.exit(1); +}); diff --git a/apps/rego-tunnel/build/scripts/vpn.bat b/apps/rego-tunnel/build/scripts/vpn.bat new file mode 100644 index 0000000..a9ed48f --- /dev/null +++ b/apps/rego-tunnel/build/scripts/vpn.bat @@ -0,0 +1,4 @@ +@echo off +cd /d C:\Users\alexz\vpn_scripts\ +node vpn-login.js +pause