Files
runtipi/apps/rego-tunnel/build/scripts/vpn-login.js
alexz 89ea16a43f
Some checks failed
Test / test (push) Has been cancelled
Add build files with SSH client, VPN scripts, and auto-setup
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 <noreply@anthropic.com>
2025-12-18 08:29:09 +00:00

460 lines
12 KiB
JavaScript

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 <ip>" - 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);
});