mirror of https://github.com/wg-easy/wg-easy
committed by
GitHub
1 changed files with 105 additions and 0 deletions
@ -0,0 +1,105 @@ |
|||
'use strict'; |
|||
/** |
|||
* tls-proxy.js — HTTPS termination proxy for wg-easy-ip-tls |
|||
* |
|||
* Reads cert/key from env-provided paths, forwards HTTPS -> HTTP to Nitro. |
|||
* Supports hot-reload of certificates via server.setSecureContext(). |
|||
* Handles WebSocket upgrades (used by wg-easy UI). |
|||
* |
|||
* Env vars (required): |
|||
* CERT_FILE - path to fullchain.pem |
|||
* KEY_FILE - path to privkey.pem |
|||
* NITRO_PORT - internal HTTP port (Nitro) |
|||
* PORT - external HTTPS port to listen on |
|||
*/ |
|||
const https = require('https'); |
|||
const http = require('http'); |
|||
const net = require('net'); |
|||
const fs = require('fs'); |
|||
|
|||
const CERT_FILE = process.env.CERT_FILE; |
|||
const KEY_FILE = process.env.KEY_FILE; |
|||
const NITRO_PORT = parseInt(process.env.NITRO_PORT, 10); |
|||
const PORT = parseInt(process.env.PORT, 10); |
|||
|
|||
if (!CERT_FILE || !KEY_FILE || !NITRO_PORT || !PORT) { |
|||
console.error('[tls-proxy] Missing required env: CERT_FILE, KEY_FILE, NITRO_PORT, PORT'); |
|||
process.exit(1); |
|||
} |
|||
|
|||
function loadCreds() { |
|||
return { |
|||
cert: fs.readFileSync(CERT_FILE), |
|||
key: fs.readFileSync(KEY_FILE), |
|||
}; |
|||
} |
|||
|
|||
let creds; |
|||
try { |
|||
creds = loadCreds(); |
|||
} catch (e) { |
|||
console.error('[tls-proxy] Failed to load certificates:', e.message); |
|||
process.exit(1); |
|||
} |
|||
|
|||
const server = https.createServer(creds, (req, res) => { |
|||
const proxy = http.request( |
|||
{ |
|||
hostname: '127.0.0.1', |
|||
port: NITRO_PORT, |
|||
path: req.url, |
|||
method: req.method, |
|||
headers: req.headers, |
|||
}, |
|||
(proxyRes) => { |
|||
res.writeHead(proxyRes.statusCode, proxyRes.headers); |
|||
proxyRes.pipe(res); |
|||
} |
|||
); |
|||
proxy.on('error', (e) => { |
|||
console.error('[tls-proxy] upstream error:', e.message); |
|||
if (!res.headersSent) res.writeHead(502); |
|||
res.end('Bad Gateway'); |
|||
}); |
|||
req.pipe(proxy); |
|||
}); |
|||
|
|||
// WebSocket upgrade support (wg-easy uses WS for live updates)
|
|||
server.on('upgrade', (req, socket, head) => { |
|||
const conn = net.connect(NITRO_PORT, '127.0.0.1', () => { |
|||
const headers = Object.entries(req.headers) |
|||
.map(([k, v]) => `${k}: ${v}`) |
|||
.join('\r\n'); |
|||
conn.write(`GET ${req.url} HTTP/1.1\r\n${headers}\r\n\r\n`); |
|||
if (head && head.length) conn.write(head); |
|||
socket.pipe(conn); |
|||
conn.pipe(socket); |
|||
}); |
|||
conn.on('error', (e) => { |
|||
console.error('[tls-proxy] ws upstream error:', e.message); |
|||
socket.destroy(); |
|||
}); |
|||
socket.on('error', (e) => { |
|||
console.error('[tls-proxy] ws client error:', e.message); |
|||
conn.destroy(); |
|||
}); |
|||
}); |
|||
|
|||
// Hot-reload certificates without restart when lego-renew.sh updates them
|
|||
fs.watchFile(CERT_FILE, { interval: 60_000, persistent: true }, (curr, prev) => { |
|||
if (curr.mtime.getTime() === prev.mtime.getTime()) return; |
|||
try { |
|||
server.setSecureContext(loadCreds()); |
|||
console.log('[tls-proxy] Certificate hot-reloaded at', curr.mtime.toISOString()); |
|||
} catch (e) { |
|||
console.error('[tls-proxy] Failed to reload certificate:', e.message); |
|||
} |
|||
}); |
|||
|
|||
server.listen(PORT, '0.0.0.0', () => { |
|||
console.log(`[tls-proxy] HTTPS listening on 0.0.0.0:${PORT} -> http://127.0.0.1:${NITRO_PORT}`); |
|||
}); |
|||
|
|||
// Graceful shutdown
|
|||
process.on('SIGTERM', () => { server.close(); process.exit(0); }); |
|||
process.on('SIGINT', () => { server.close(); process.exit(0); }); |
|||
Loading…
Reference in new issue