Skip to content

Commit

Permalink
code refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Sirherobrine23 committed Aug 24, 2023
1 parent 80e6ad6 commit c834a01
Show file tree
Hide file tree
Showing 22 changed files with 232 additions and 268 deletions.
9 changes: 2 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Node.js
/build/
node_modules/
/*-lock*
*-lock*
/*.tgz
*.node

Expand All @@ -12,9 +12,4 @@ src/**/*.d.ts
# Anothers
.vscode-ctags*
*.addrs.json
.DS_Store

# Wireguard projects
addons/wg_go
addons/wireguard-go.exe
addons/wireguard-go
.DS_Store
3 changes: 0 additions & 3 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ src/**/*.ts
.github/
.git*

# Wireguard
addons/wg_go/wireguard-go

# node-gyp
build/**/*

Expand Down
101 changes: 12 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,24 @@
# Wireguard-tools.js

A quick way to use Wireguard with Node.js without having to run the Wireguard tools. We've included some `wg` command patterns to avoid confusion and to maintain a base between the tools.
Efficiently manage your Wireguard interface right from nodejs, no `wg` required.

> **Note**
>
> we have pre-copiled files for Windows, MacOS (x64/intel) and Linux (arm64, x86_64), else require `gcc` or `clang` installed to compile Node addon.
With this module it is possible to:
other tools are wrappers over `wg`, `wireguard-tools.js` is not like that, it is a `C/C++` addon in which you don't need to have `wg` installed, as this module has full compatibility of its own `wg`.

- Add/Remove a Wireguard interface.
- Add/Remove/Modify `peers` for an interface.
- Add IPs to the interface.
- Create `pre-shared`, `private` and `public` keys.
- Write the Wireguard configuration file and convert it to JSON format.
## Support to:

## Example
1. Userpsace [(wireguard-go)](https://git.zx2c4.com/wireguard-go/about/)
2. Maneger wireguard interface (linux create if not exist's).
3. Generate `pre-shared`, `private` and `public` keys.
4. [wg-quick](https://man7.org/linux/man-pages/man8/wg-quick.8.html) file support.

> **Note**
>
> we have pre-copiled files for Windows, MacOS (x64/intel) and Linux (arm64, x86_64) else arch and system require `gcc` or `clang` installed to compile Node addon.
>
> To manage the Wireguard interfaces in linux, root access is required.
>
> Another system's require [wireguard-go](https://github.com/WireGuard/wireguard-go)
### Get Current peers and Statistics

```ts
import { parseWgDevice } from "wireguard-tools.js";
const wireguardInterfaces = parseWgDevice("wg0");
// Wg0 is the interface name.
console.log("Wg0:\n%o", wireguardInterfaces);
```

# Add/Update Wireguard interface

```ts
import { addDevice, parseWgDevice } from "wireguard-tools.js";
addDevice("wg0", {
publicKey: "string",
privateKey: "string",
portListen: 27301,
Address: [
"1.1.1.1/32",
"8.8.8.8/32"/** Mark this peer to be removed, any changes remove this option */
],
replacePeers: true,
peers: {
"publicKeyPeer": {
removeMe: false,
presharedKey: "string",
keepInterval: 5,
endpoint: "google.com:27301",
allowedIPs: [
"8.8.8.8",
"8.8.4.4."
]
}
}
});
const wireguardInterfaces = parseWgDevice("wg0");
// Wg0 is the interface name.
console.log("Wg0:\n%o", wireguardInterfaces);
```

### Parse wireguard configuration file

```ts
import { readFileSync } from "node:fs";
import { utils } from "wireguard-tools.js";
const configFile = readFileSync("/etc/wireguard/wg0.conf", "utf8");
const configJson = utils.config.parseConfig(configFile);
console.log("Config file JSON:\n%o", configJson.data);
```
> Another system's require `wireguard-go` [(check this page)](https://github.com/WireGuard/wireguard-go)
### Create Config
## Examples

```ts
import { utils } from "wireguard-tools.js";
const wireguardConfig = utils.config.writeConfig({
interface: {
private: "CEOntDE9saQaHLhD/WzZuYky3+elOfnBUCXoSveD3kc=",
public: "xaZtpi3VCkBMhSTKM6jl/YjPJ370iYpBlLYwSyZ3W08=",
address: [
{
ip: "10.0.0.1",
subnet: 24
}
]
},
peer: {
"tF4YxTqLIJdNQcqvz1jtIF993zSk79hP+zdBxQlaowA=": {
Keepalive: 25,
Endpoint: {
host: "wireguard.example.com",
port: 51820
},
}
}
});
console.log("Config file:\n%s", wireguardConfig);
```
Moved to `examples` folder
1 change: 1 addition & 0 deletions configsExamples/wg0.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Config example to Server
[Interface]
ListenPort = 38451
Address = 10.144.0.1/32, 192.160.0.1/32, 10.80.0.1/32, 10.48.0.1/32, 10.0.0.1/32, 10.208.0.1/32, 10.64.0.1/32, 10.176.0.1/32, 10.160.0.1/32, 10.96.0.1/32, 2002:0A90:0001::/128, 2002:C0A0:0001::/128, 2002:0A50:0001::/128, 2002:0A30:0001::/128, 2002:0A00:0001::/128, 2002:0AD0:0001::/128, 2002:0A40:0001::/128, 2002:0AB0:0001::/128, 2002:0AA0:0001::/128, 2002:0A60:0001::/128
Expand Down
2 changes: 2 additions & 0 deletions configsExamples/wg1.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Config example to Client
[Interface]
PrivateKey = hOrmHYYoPZ01ni2H1GBZFEkCxHUC7B7kj5EXK10PD1y=
Address = 1.1.1.1/32
DNS = 1.1.1.1, 8.8.8.8

# Client peer
[Peer]
PublicKey = 3H4TlYF0XLan8b3uJ8ULcPQVRoSsuFkwAUqTlzPgYSo=
PresharedKey = EYuaY6ohH3IkmwL6rADLdk/2In1VlvuVwGb0nfvQNS+=
Expand Down
19 changes: 19 additions & 0 deletions examples/addPeer/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { listDevices, parseWgDevice, addDevice, utils } from "wireguard-tools.js";
const deviceName = (await listDevices()).at(-1);
if (!deviceName) {
console.info("Create wireguard interface");
process.exit(-1);
}

// Get current config
const currentConfig = await parseWgDevice(deviceName);

// Create Peer
const peerKey = await utils.keygenAsync(true);
currentConfig.peers[peerKey.public] = { presharedKey: peerKey.preshared, allowedIPs: [ utils.ipManipulation.randomIp("10.22.66.1/24", Object.keys(currentConfig.peers).map(k => currentConfig.peers[k].allowedIPs).flat(2).filter(Boolean)) ] };
currentConfig.peers[peerKey.public].allowedIPs.push(utils.ipManipulation.toV6(currentConfig.peers[peerKey.public].allowedIPs.at(0)));
console.log("Add %O peer", peerKey.public);

// Set to interface
await addDevice(deviceName, currentConfig);
console.dir(currentConfig, { colors: true, depth: null });
11 changes: 11 additions & 0 deletions examples/addPeer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "add_peer",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "node index.mjs"
},
"dependencies": {
"wireguard-tools.js": "file:../.."
}
}
71 changes: 49 additions & 22 deletions libs/build.mjs
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
// @ts-check
import child_process from "node:child_process";
import { promises as fs } from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
const __dirname = path.dirname(fileURLToPath(import.meta.url)); // Fix ESM __dirname
const nodeGyp = path.resolve(createRequire(import.meta.url).resolve("node-gyp"), "../../bin/node-gyp.js"); // Node gyp script
const env = Object.assign({}, process.env);

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const prebuilds = path.resolve(__dirname, "../prebuilds");
const build = path.resolve(__dirname, "../build/Release");
const nodeGyp = path.resolve(createRequire(import.meta.url).resolve("node-gyp"), "../../bin/node-gyp.js");
const env = Object.assign({}, process.env);
const buildDir = path.resolve(__dirname, "../build") /* Build Directory */, buildRelease = path.join(buildDir, "Release"), buildDebug = path.join(buildDir, "Debug");
async function exist(path) {
return fs.open(path).then(() => true, () => false);
}

async function fork(...args) {
return new Promise((resolve, reject) => {
const child = child_process.fork(...args);
/**
*
* @param {string} command
* @param {string[]} args
* @param {Omit<import("child_process").ForkOptions, "stdio">} options
*/
async function fork(command, args, options) {
if (options) options["stdio"] = undefined;
return new Promise((done, reject) => {
const child = child_process.fork(command, args, options);
child.on("error", reject);
child.on("exit", (code, signal) => {
if (code === 0) resolve();
else reject(new Error(`Process exited with code ${code} and signal ${signal}`));
if (child.stdout) child.stdout.pipe(process.stdout);
if (child.stderr) child.stderr.pipe(process.stderr);
child.once("exit", (code, sig) => {
if (code === 0) return done(0);
return reject(new Error(("Process exit with ").concat(String(code), " and signal ", String(sig))));
});
});
};

async function exist(path) {
return fs.open(path).then(() => true).catch(() => false);
}

// Fix CI prebuild download
if (await exist(prebuilds)) {
const prebuildsFolder = (await fs.readdir(prebuilds)).filter(file => file.startsWith("prebuilds_"));
for (const folder of prebuildsFolder) {
Expand All @@ -40,8 +51,25 @@ if (await exist(prebuilds)) {
}
}

/**
*
* @param {string} platform
* @param {string} arch
*/
async function migrateBuildAddon(platform, arch) {
const files = (await fs.readdir(buildRelease)).filter(f => f.endsWith(".node"));
if (await exist(path.join(prebuilds, `${platform}_${arch}`))) await fs.rm(path.join(prebuilds, `${platform}_${arch}`), {recursive: true, force: true});
await fs.mkdir(path.join(prebuilds, `${platform}_${arch}`), {recursive: true});
for (const file of files) await fs.rename(path.join(buildRelease, file), path.join(prebuilds, `${platform}_${arch}`, file));
await fs.rm(buildDir, { recursive: true, force: true });
}

if (process.argv.slice(2).at(0) === "build") {
let archs = [];
if (process.argv.includes("--clean")) {
if (await exist(buildDir)) await fs.rm(buildDir, { recursive: true, force: true });
if (await exist(prebuilds)) await fs.rm(prebuilds, { recursive: true, force: true });
}
if (process.argv.includes("--auto")) {
if (process.platform === "linux") archs.push("x64", "arm64");
else archs.push(process.arch);
Expand All @@ -50,7 +78,6 @@ if (process.argv.slice(2).at(0) === "build") {
if (archs.length <= 0) archs.push(process.arch);
}
for (const arch of Array.from(new Set(archs))) {
console.log("Bulding to %O\n", arch);
if (process.platform === "linux" && arch !== process.arch) {
if (arch === "x64") {
// x86_64-linux-gnu-gcc
Expand All @@ -63,12 +90,12 @@ if (process.argv.slice(2).at(0) === "build") {
}
}

await fork(nodeGyp, ["rebuild", "-j", "max", "--arch="+arch], {stdio: "inherit", env});
const files = (await fs.readdir(build)).filter(f => f.endsWith(".node"));
if (await exist(path.join(prebuilds, `${process.platform}_${arch}`))) await fs.rm(path.join(prebuilds, `${process.platform}_${arch}`), {recursive: true, force: true});
await fs.mkdir(path.join(prebuilds, `${process.platform}_${arch}`), {recursive: true});
for (const file of files) await fs.rename(path.join(build, file), path.join(prebuilds, `${process.platform}_${arch}`, file));
console.log("Bulding to %O\n", arch);
await fork(nodeGyp, ["rebuild", "-j", "max", ("--arch=").concat(arch)], {env});
console.log("Migrating addons!");
await migrateBuildAddon(process.platform, arch);
}
} else if (!(await exist(path.join(prebuilds, `${process.platform}_${process.arch}`)) || await exist(build))) {
await fork(nodeGyp, ["rebuild", "-j", "max"], {stdio: "inherit", env});
} else if (!(await exist(path.join(prebuilds, `${process.platform}_${process.arch}`)) || await exist(buildRelease))) {
await fork(nodeGyp, ["rebuild", "-j", "max"], {env});
await migrateBuildAddon(process.platform, process.arch);
}
17 changes: 9 additions & 8 deletions libs/prebuildifyLoad.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,30 @@ const path = require("path");
module.exports = main;
/**
* Load node addon
* @param {string|number|undefined} name
* @param {string|undefined} path
* @param {string|undefined} name
* @returns {any}
*/
function main(pathLocation, name) {
function main(name, pathLocation) {
if (!pathLocation) pathLocation = process.cwd();
else pathLocation = path.resolve(pathLocation);
const folders = [
path.join(pathLocation, "build", "Release"),
path.join(pathLocation, "build", "Debug"),
path.join(pathLocation, "prebuilds", `${process.platform}-${process.arch}`),
path.join(pathLocation, "prebuilds", `${process.platform}_${process.arch}`)
]
];
for (const folder of folders) {
if (fs.existsSync(folder)) {
if (!name) name = (fs.readdirSync(folder)).filter(file => file.endsWith(".node")).at(0);
const files = (fs.readdirSync(folder)).filter(file => file.endsWith(".node"));
if (typeof name === "number") return require(path.join(folder, files.at(name)));
else if (!name) name = files.at(0);
if (typeof name === "string") {
if (!(name.endsWith(".node"))) name += ".node";
if (fs.existsSync(path.join(folder, name))) return require(path.join(folder, name));
const bname = name;
if ((name = files.find(s => s.startsWith(name)))) return require(path.join(folder, name));
name = bname;
}
}


}
throw new Error("Cannot get node addon");
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wireguard-tools.js",
"version": "1.7.2",
"version": "1.7.3",
"description": "Control your wireguard interface from node.js, not a wireguard-tools wrapper!",
"main": "src/index.js",
"types": "src/index.d.ts",
Expand Down Expand Up @@ -40,9 +40,8 @@
"postpack": "tsc --build --clean && node-gyp clean"
},
"devDependencies": {
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@types/node": "^20.5.2",
"@types/node": "^20.5.3",
"mocha": "^10.2.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * as userspace from "./userspace";
export * as utils from "./utils/index";
export * from "./mergeSets";
export * as userspace from "./userspace";
export * from "./mergeSets";
Loading

0 comments on commit c834a01

Please sign in to comment.