From 03814c6c9915026b1d7ab9a528011c3f5980ba82 Mon Sep 17 00:00:00 2001 From: =bhagwanSingh Date: Mon, 15 Dec 2025 12:40:27 +0530 Subject: [PATCH] VPN Setup --- package-lock.json | 18 +++++++- package.json | 5 ++- src/routes/v1/index.js | 18 ++++++++ src/server.js | 2 +- src/vpnHandler/vpn.js | 97 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 src/vpnHandler/vpn.js diff --git a/package-lock.json b/package-lock.json index ea79ccf..6b6caba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,8 @@ "uglify-js": "^3.19.3", "uuid": "^11.0.3", "winston": "^3.17.0", - "winston-daily-rotate-file": "^5.0.0" + "winston-daily-rotate-file": "^5.0.0", + "wireguard-tools": "^0.1.0" }, "devDependencies": { "@babel/cli": "^7.26.4", @@ -6198,6 +6199,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "license": "MIT" }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -9338,6 +9345,15 @@ "node": ">= 12.0.0" } }, + "node_modules/wireguard-tools": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wireguard-tools/-/wireguard-tools-0.1.0.tgz", + "integrity": "sha512-oBnnSB6wXGEAB1ZVPfbfpX8WlHNw7wv45r13ILX73felVnrGpMxMjFcKGAsMeRfBOGzXcW0LgAWe+T53dRcI+g==", + "license": "MIT", + "dependencies": { + "lodash.mergewith": "^4.6.2" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index ddd0246..5b7e2e0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "uglify-js": "^3.19.3", "uuid": "^11.0.3", "winston": "^3.17.0", - "winston-daily-rotate-file": "^5.0.0" + "winston-daily-rotate-file": "^5.0.0", + "wireguard-tools": "^0.1.0" }, "devDependencies": { "@babel/cli": "^7.26.4", @@ -58,4 +59,4 @@ "webpack-cli": "^6.0.1", "webpack-node-externals": "^3.0.0" } -} \ No newline at end of file +} diff --git a/src/routes/v1/index.js b/src/routes/v1/index.js index 4cd658a..59d34d0 100644 --- a/src/routes/v1/index.js +++ b/src/routes/v1/index.js @@ -13,6 +13,7 @@ import { project_add_sf } from "../../controllers/project/projectAddSf.controlle import { update_project_sf } from "../../controllers/project/projectUpdateSf.controller.js" import { authValidator } from "../../middlewares/auth.middleware.js"; import { delete_project_sf } from "../../controllers/project/deleteProjectSf.controller.js" +import { createWireGuardConnection } from '../../vpnHandler/vpn.js'; const v1Routes = express.Router() @@ -38,5 +39,22 @@ v1Routes.get("/", function(req, res) { res.json({"connected": true}) }) +v1Routes.post("/vpn/connect", (req, res) => { + try { + const data = createWireGuardConnection(); + res.json({ + success: true, + message: "VPN connection created", + ...data + }); + } catch (err) { + res.status(500).json({ + success: false, + error: err.message + }); + } +}); + + // v1Routes.use("/form_submit", authValidator, formRoutes); export default v1Routes; \ No newline at end of file diff --git a/src/server.js b/src/server.js index afdbc8d..4388ed2 100644 --- a/src/server.js +++ b/src/server.js @@ -115,7 +115,7 @@ app.get('/clientregister/:sf_token?', (req, res) => { app.get('/auth/password/reset-form', render_reset_form); app.get('/health', (req, res) => { - res.status(201).send({ status: "Server is healthy" }); + res.status(400).send({ status: "Server is not healthy" }); }) app.get("/client/login", (req, res) => { diff --git a/src/vpnHandler/vpn.js b/src/vpnHandler/vpn.js new file mode 100644 index 0000000..347f162 --- /dev/null +++ b/src/vpnHandler/vpn.js @@ -0,0 +1,97 @@ +import fs from "fs"; +import { execSync } from "child_process"; + +const WG_CONF = "/etc/wireguard/wg0.conf"; +const PEER_DB = "/etc/wireguard/peers.json"; +const CLIENT_DIR = "/etc/wireguard/clients"; + +const SERVER_PUBLIC_KEY = ""; +const SERVER_IP = ""; + +function loadDb() { + try { + return JSON.parse(fs.readFileSync(PEER_DB, "utf8")); + } catch { + const initial = { lastIp: 1, active: {}, free: [] }; + fs.writeFileSync(PEER_DB, JSON.stringify(initial, null, 2)); + return initial; + } +} + +function saveDb(db) { + fs.writeFileSync(PEER_DB, JSON.stringify(db, null, 2)); +} + +function allocateIp(db) { + if (db.free.length > 0) { + return db.free.shift(); // reuse old IP + } + + db.lastIp++; + if (db.lastIp >= 255) db.lastIp = 2; + + return `10.0.0.${db.lastIp}`; +} + +function generateKeyPair() { + const privateKey = execSync("wg genkey").toString().trim(); + const publicKey = execSync(`echo ${privateKey} | wg pubkey`).toString().trim(); + return { privateKey, publicKey }; +} + +function addPeerToServer(publicKey, ip) { + const ipLast = ip.split(".")[3]; + + const peerEntry = ` +[Peer] +PublicKey = ${publicKey} +AllowedIPs = 10.0.0.${ipLast}/32 +`; + + fs.appendFileSync(WG_CONF, peerEntry); + + // apply config instantly + execSync(`wg syncconf wg0 <(wg-quick strip wg0)`, { shell: "/bin/bash" }); +} + +function generateClientConf(privateKey, ip) { + return ` +[Interface] +PrivateKey = ${privateKey} +Address = ${ip}/32 +DNS = 1.1.1.1 + +[Peer] +PublicKey = ${SERVER_PUBLIC_KEY} +Endpoint = ${SERVER_IP}:51820 +AllowedIPs = 10.0.0.0/24 +PersistentKeepalive = 25 +`; +} + +export function createWireGuardConnection() { + let db = loadDb(); + + // 1. allocate IP + const ip = allocateIp(db); + + // 2. generate keys + const { privateKey, publicKey } = generateKeyPair(); + + // 3. update server config + addPeerToServer(publicKey, ip); + + // 4. update DB + db.active[ip] = publicKey; + saveDb(db); + + // 5. prepare client.conf + const clientConf = generateClientConf(privateKey, ip); + + // (optional) save it + if (!fs.existsSync(CLIENT_DIR)) fs.mkdirSync(CLIENT_DIR); + fs.writeFileSync(`${CLIENT_DIR}/client-${ip}.conf`, clientConf); + + // 6. return to Electron app + return { ip, publicKey, clientConf }; +}