From ac3245b78f27c66a4c1254ef2be67bae85d1e8d4 Mon Sep 17 00:00:00 2001 From: tke Date: Mon, 27 Apr 2026 14:27:27 +0200 Subject: [PATCH] proxy-bridge: add keyring-based HTTP CONNECT proxy bridge --- scripts/proxy/bridge.js | 57 +++++++++++++++++++++++++++ scripts/proxy/install_proxy.sh | 70 ++++++++++++++++++++++++++++++++++ scripts/proxy/setup.js | 34 +++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 scripts/proxy/bridge.js create mode 100755 scripts/proxy/install_proxy.sh create mode 100644 scripts/proxy/setup.js diff --git a/scripts/proxy/bridge.js b/scripts/proxy/bridge.js new file mode 100644 index 0000000..92f63b2 --- /dev/null +++ b/scripts/proxy/bridge.js @@ -0,0 +1,57 @@ +const http = require('http'); +const net = require('net'); +const { execSync } = require('child_process'); +const fs = require('fs'); + +// 1. CONFIGURATION +const PROXY_HOST = 'PROXY_HOST_PLACEHOLDER'; +const PROXY_PORT = 8080; + +// 2. FETCH SECRETS FROM KEYRING +let USER, PASS; +try { + // Read the username we saved during setup + USER = JSON.parse(fs.readFileSync('/opt/proxy-bridge/user.json')).username; + // Query the Ubuntu Keyring for the password associated with this user/service + PASS = execSync(`secret-tool lookup service proxy-bridge account ${USER}`).toString().trim(); + + if (!PASS) throw new Error("Password returned empty."); +} catch (e) { + console.error("CRITICAL: Could not retrieve credentials from keyring. Did you run setup.js?"); + console.error(e.message); + process.exit(1); +} + +// 3. GENERATE AUTH +const AUTH_HEADER = 'Basic ' + Buffer.from(`${USER}:${PASS}`).toString('base64'); + +const server = http.createServer(); + +server.on('connect', (req, clientSocket, head) => { + console.log(`--> Connecting to ${req.url}`); + + const serverSocket = net.connect(PROXY_PORT, PROXY_HOST, () => { + serverSocket.write(`CONNECT ${req.url} HTTP/1.1\r\n` + + `Host: ${req.url}\r\n` + + `Proxy-Authorization: ${AUTH_HEADER}\r\n` + + `Proxy-Connection: Keep-Alive\r\n\r\n`); + serverSocket.write(head); + }); + + serverSocket.once('data', (data) => { + if (data.toString().includes('200')) { + clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); + serverSocket.pipe(clientSocket); + clientSocket.pipe(serverSocket); + } else { + clientSocket.write(data); + } + }); + + serverSocket.on('error', () => clientSocket.end()); + clientSocket.on('error', () => serverSocket.end()); +}); + +server.listen(8888, '127.0.0.1', () => { + console.log('Bridge active on http://127.0.0.1:8888 (Auth via Keyring)'); +}); \ No newline at end of file diff --git a/scripts/proxy/install_proxy.sh b/scripts/proxy/install_proxy.sh new file mode 100755 index 0000000..44898d7 --- /dev/null +++ b/scripts/proxy/install_proxy.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Ensure the script is NOT run as root, so the user-level systemd service configures correctly +if [ "$EUID" -eq 0 ]; then + echo "❌ Please run this script as your standard user, not as root." + echo "The script will prompt for sudo access automatically when needed." + exit 1 +fi + +echo "=== Proxy Bridge Installer ===" + +# 1. Verify files exist +if [ ! -f "bridge.js" ] || [ ! -f "setup.js" ]; then + echo "❌ Error: bridge.js and/or setup.js not found in the current directory." + echo "Please place this script in the same folder as your Node.js scripts." + exit 1 +fi + +# 2. Install dependencies (requires sudo) +echo "--> Installing required system packages (libsecret-tools)..." +sudo apt-get update +sudo apt-get install -y libsecret-tools + +# 3. Setup application directory (requires sudo) +echo "--> Creating /opt/proxy-bridge directory..." +sudo mkdir -p /opt/proxy-bridge +sudo chown -R $USER:$USER /opt/proxy-bridge + +# 4. Copy files +echo "--> Copying scripts to /opt/proxy-bridge..." +cp bridge.js /opt/proxy-bridge/ +cp setup.js /opt/proxy-bridge/ + +# 5. Setup User-Level systemd Service +echo "--> Configuring user-level systemd service..." +mkdir -p ~/.config/systemd/user/ + +cat < ~/.config/systemd/user/proxy-bridge.service +[Unit] +Description=Dumb Pipe Proxy Bridge (Keyring Auth) +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/node /opt/proxy-bridge/bridge.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=default.target +EOF + +# 6. Enable the service +echo "--> Reloading systemd and enabling service..." +systemctl --user daemon-reload +systemctl --user enable proxy-bridge.service + +echo "" +echo "✅ Installation Complete!" +echo "==================================================" +echo "Next Steps:" +echo "1. Run the interactive setup to store your proxy credentials:" +echo " node /opt/proxy-bridge/setup.js" +echo "" +echo "2. Start the background service:" +echo " systemctl --user start proxy-bridge.service" +echo "" +echo "3. Check the logs to ensure it's running smoothly:" +echo " journalctl --user -u proxy-bridge.service -f" +echo "==================================================" \ No newline at end of file diff --git a/scripts/proxy/setup.js b/scripts/proxy/setup.js new file mode 100644 index 0000000..d941461 --- /dev/null +++ b/scripts/proxy/setup.js @@ -0,0 +1,34 @@ +const readline = require('readline'); +const { execSync } = require('child_process'); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +console.log("=== Proxy Bridge Keyring Setup ==="); + +rl.question('Enter your corporate username: ', (user) => { + rl.question('Enter your corporate password: ', (pass) => { + try { + // Securely store the password in the Ubuntu Keyring using secret-tool + // We use standard input to pass the password so it doesn't appear in process lists + execSync(`secret-tool store --label="Proxy Bridge Credentials" service proxy-bridge account ${user}`, { + input: pass + }); + + // Store the username in a local config just so the bridge knows WHICH account to look up + require('fs').writeFileSync('/opt/proxy-bridge/user.json', JSON.stringify({ username: user })); + + console.log("\n✅ Credentials successfully stored in the system keyring."); + } catch (error) { + console.error("\n❌ Failed to store credentials in keyring:", error.message); + } + rl.close(); + }); + + // Hide typing for password (basic implementation) + rl._writeToOutput = function _writeToOutput(stringToWrite) { + if (rl.history.length === 0) rl.output.write("*"); + }; +}); \ No newline at end of file