mirror of https://github.com/wg-easy/wg-easy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.1 KiB
105 lines
3.1 KiB
'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); });
|
|
|