wireguard-tools.js
Version:
Control your wireguard interface from node.js, not a wireguard-tools wrapper!
231 lines (230 loc) • 10.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WgQuick = exports.stringify = exports.parse = void 0;
const net_1 = require("net");
const wginterface_1 = require("./wginterface");
const key_1 = require("./key");
const crypto_1 = require("crypto");
function parse(config) {
if (Buffer.isBuffer(config))
config = config.toString();
const lines = config.trim().split(/\r?\n/).map(s => s.trim()).filter(s => (!!s) && !(s.startsWith("#")));
return Object.assign({}, { peers: {} }, lines.reduce((acc, line, index) => {
if (line.toLowerCase() === "[peer]") {
if (typeof acc.at(-1).start === "undefined")
acc.at(-1).start = index;
else {
acc.at(-1).end = index - 1;
acc.push({ start: index });
}
}
return acc;
}, [{ start: 0 }]).map(({ start, end }) => {
const [target, ...linesConfig] = lines.slice(start, end);
return {
target: target.toLowerCase() === "[peer]" ? "peer" : "interface",
linesConfig: linesConfig.map(s => {
const ii = s.indexOf("="), keyName = s.substring(0, ii).trim(), value = s.substring(ii + 1).trim();
return { keyName, value };
})
};
}).reduce((acc, info) => {
if (info.target === "interface") {
for (const { keyName, value } of info.linesConfig) {
if (keyName.toLowerCase() === "privatekey")
acc.privateKey = value;
else if (keyName.toLowerCase() === "publickey")
acc.publicKey = value;
else if (keyName.toLowerCase() === "address")
acc.Address = ([acc.Address, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean);
else if (keyName.toLowerCase() === "dns")
acc.DNS = ([acc.DNS, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean);
else if (keyName.toLowerCase() === "listenport") {
const port = parseInt(value);
if (port >= 1 && port <= ((2 ** 16) - 1))
acc.portListen = port;
}
}
}
else if (info.target === "peer") {
acc.peers = (acc.peers || {});
const publicKey = info.linesConfig.find(s => s.keyName.toLowerCase() === "publickey")?.value;
if (!!publicKey) {
for (const { keyName, value } of info.linesConfig) {
if (keyName.toLowerCase() === "publickey")
continue;
else if (keyName.toLowerCase() === "allowedips")
(acc.peers[publicKey] || (acc.peers[publicKey] = {})).allowedIPs = ([acc.peers[publicKey].allowedIPs, value]).flat(3).map(s => s && s.split(",")).flat(2).map(s => s && s.trim()).filter(Boolean);
else if (keyName.toLowerCase() === "presharedkey")
(acc.peers[publicKey] || (acc.peers[publicKey] = {})).presharedKey = value;
else if (keyName.toLowerCase() === "endpoint")
(acc.peers[publicKey] || (acc.peers[publicKey] = {})).endpoint = value;
}
}
}
return acc;
}, {}));
}
exports.parse = parse;
function stringify({ portListen = 0, privateKey, publicKey, Address, DNS = ["1.1.1.1", "8.8.8.8"], peers = {} }) {
const configString = ["[Interface]"];
if (typeof publicKey === "string" && publicKey.length === wginterface_1.constants.WG_B64_LENGTH)
configString.push(("PublicKey = ").concat(publicKey));
if (typeof privateKey === "string" && privateKey.length === wginterface_1.constants.WG_B64_LENGTH)
configString.push(("PrivateKey = ").concat(privateKey));
if (portListen >= 0 && portListen <= ((2 ** 16) - 1))
configString.push(("ListenPort = ").concat(String(portListen)));
if (Array.isArray(Address) && Address.length > 0)
configString.push(("Address = ").concat(...(Address.join(", "))));
if (Array.isArray(DNS) && DNS.length > 0)
configString.push(("DNS = ").concat(DNS.join(", ")));
// Peers mount
for (const pubKey of Object.keys(peers)) {
configString.push("", "[Peer]", ("PublicKey = ").concat(pubKey));
const { presharedKey, allowedIPs, keepInterval, endpoint } = peers[pubKey];
if (typeof presharedKey === "string" && presharedKey.length > 0)
configString.push(("PresharedKey = ").concat(presharedKey));
if (typeof endpoint === "string" && endpoint.length > 0)
configString.push(("Endpoint = ").concat(endpoint));
if (typeof keepInterval === "number" && keepInterval > 0)
configString.push(("PersistentKeepalive = ").concat(String(keepInterval)));
if (Array.isArray(allowedIPs) && allowedIPs.length > 0)
configString.push(("AllowedIPs = ").concat(allowedIPs.join(", ")));
}
return configString.join("\n").trim();
}
exports.stringify = stringify;
/**
* Create and Manipulate interface Configs
*/
class WgQuick extends Map {
_privateKey;
/**
* Set private key to interface
*/
set privateKey(key) {
if (key.length === wginterface_1.constants.WG_B64_LENGTH) {
this._privateKey = key;
(0, key_1.publicKey)(key).then(key => this._publicKey = key, () => { });
}
else
throw new Error("Invalid private key");
}
/**
* Get private key from interface config
*/
get privateKey() {
if (String(this._privateKey).length === wginterface_1.constants.WG_B64_LENGTH)
return this._privateKey;
return undefined;
}
_publicKey;
/**
* Set public key to interface
*/
set publicKey(key) {
if (key.length === wginterface_1.constants.WG_B64_LENGTH)
this._publicKey = key;
else
throw new Error("Invalid public key");
}
/**
* Get public key from interface config
*/
get publicKey() {
if (String(this._publicKey).length === wginterface_1.constants.WG_B64_LENGTH)
return this._publicKey;
return undefined;
}
_portListen = (0, crypto_1.randomInt)(2000, 65535);
/** Get port to listen, 0 less a 65535 */
set portListen(port) {
if (port >= 1 && port <= 65535)
this._portListen = port;
else if (port === 0)
this._portListen = (port = (0, crypto_1.randomInt)(2000, 65535));
else
throw new Error("Set valid range port 0 to or equal at a 65535");
}
get portListen() {
if (this._portListen <= 0)
return (this._portListen = (0, crypto_1.randomInt)(2000, 65535));
return this._portListen || 0;
}
_Address = ["10.0.0.1/24"];
get Address() {
return this._Address || [];
}
set Address(addr) {
this._Address = Array.isArray(addr) ? addr.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : [];
}
_DNS = ["1.1.1.1", "8.8.8.8"];
get DNS() { return this._DNS || []; }
set DNS(dns) {
this._DNS = Array.isArray(dns) ? dns.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : [];
}
constructor(config) {
super();
if (!config)
return;
const { privateKey, publicKey, portListen, Address, DNS, peers } = config;
this._portListen = portListen;
this.privateKey = privateKey;
this._publicKey = publicKey;
this.Address = Array.isArray(Address) ? Address.filter(s => !!((0, net_1.isIP)(s.split("/")[0]))) : [];
this.DNS = Array.isArray(DNS) ? DNS.filter(s => !!((0, net_1.isIP)(s))) : [];
for (const pubKey in (peers || {}))
if (pubKey.length === wginterface_1.constants.WG_B64_LENGTH && peers[pubKey])
this.set(pubKey, peers[pubKey]);
}
set() {
if (arguments.length === 0 || typeof arguments[0] === "boolean" || typeof arguments[0] === "number") {
let preshareKey = !!arguments[1];
if (typeof arguments[0] === "number") {
if (!(arguments[0] > 0))
throw new Error("Set less 1 or equal");
return Promise.all(Array(arguments[0]).fill(undefined).map(async () => this.set(preshareKey)));
}
preshareKey = !!arguments[0];
return Promise.resolve().then(async () => {
let peerKey;
while (!peerKey) {
peerKey = await (0, key_1.genKey)(true);
if (this.has(peerKey.publicKey))
peerKey = undefined;
}
super.set(peerKey.publicKey, {
...(preshareKey ? { presharedKey: peerKey.presharedKey } : {}),
keepInterval: 5,
allowedIPs: [],
});
return peerKey.publicKey;
});
}
if (String(arguments[0] || "").length !== wginterface_1.constants.WG_B64_LENGTH)
throw new Error("Set valid peer public key!");
const { allowedIPs = [], endpoint, keepInterval = 0, presharedKey } = (arguments[1] || {});
super.set(arguments[0], {
...(String(presharedKey || "").length === wginterface_1.constants.WG_B64_LENGTH ? { presharedKey } : {}),
...(String(endpoint || "").length >= 1 ? { endpoint } : {}),
keepInterval,
allowedIPs,
});
return this;
}
toJSON() {
const { _privateKey, _publicKey, _Address, _DNS, _portListen } = this;
return {
...(_privateKey ? { privateKey: _privateKey } : {}),
...(_publicKey ? { publicKey: _publicKey } : {}),
...(_Address ? { Address: _Address } : { Address: [] }),
portListen: _portListen,
...(_DNS ? { DNS: _DNS } : { DNS: [] }),
peers: Array.from(this.entries()).reduce((acc, [pubKey, config]) => Object.assign(acc, { [pubKey]: config }), {}),
};
}
toString() {
return stringify(this.toJSON());
}
}
exports.WgQuick = WgQuick;