Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intergrate ZeroNSd support #25

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ temp
.env.test.local
.env.production.local

.idea

# Created by https://www.toptal.com/developers/gitignore/api/vscode,yarn,react,node
# Edit at https://www.toptal.com/developers/gitignore?templates=vscode,yarn,react,node

Expand Down Expand Up @@ -156,9 +158,9 @@ sketch

# if you are NOT using Zero-installs, then:
# comment the following lines
!.yarn/cache
#!.yarn/cache

# and uncomment the following lines
# .pnp.*
.pnp.*

# End of https://www.toptal.com/developers/gitignore/api/vscode,yarn,react,node
48 changes: 48 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,54 @@ jspm_packages/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test
.env.production

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Misc
.DS_Store
.env.local
Expand Down
48 changes: 44 additions & 4 deletions backend/services/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const axios = require("axios");
const api = require("../utils/controller-api");
const db = require("../utils/db");
const constants = require("../utils/constants");
const zns = require("../utils/zns");

async function getNetworkAdditionalData(data) {
let additionalData = db
Expand All @@ -21,11 +22,30 @@ async function getNetworkAdditionalData(data) {
delete data.remoteTraceLevel;
delete data.remoteTraceTarget;

//let ad = { ...additionalData.value() };
let ad_ = additionalData.value();
let ad = JSON.parse(JSON.stringify(ad_));
data.dns = {
domain: ad_.dnsDomain,
servers: [],
};
if (ad_.dnsIP) data.dns["servers"].push(ad_.dnsIP);
console.log(
`*** ad_="${JSON.stringify(ad_, null, 3)}" -> ad="${JSON.stringify(
ad,
null,
3
)}" -> ${JSON.stringify(data.dns, null, 3)}`
);
delete ad.dnsIP;
delete ad.dnsDomain;
delete ad.dnsEnable;
delete ad.dnsWildcard;
return {
id: data.id,
type: "Network",
clock: Math.floor(new Date().getTime() / 1000),
...additionalData.value(),
...ad,
config: data,
};
}
Expand All @@ -46,13 +66,11 @@ async function getNetworksData(nwids) {
return [];
});

let data = Promise.all(
return Promise.all(
multipleRes.map((el) => {
return getNetworkAdditionalData(el.data);
})
);

return data;
}

exports.createNetworkAdditionalData = createNetworkAdditionalData;
Expand All @@ -62,6 +80,9 @@ async function createNetworkAdditionalData(nwid) {
additionalConfig: {
description: "",
rulesSource: constants.defaultRulesSource,
dnsEnable: false,
dnsDomain: "",
dnsWildcard: false,
},
members: [],
};
Expand All @@ -79,13 +100,32 @@ async function updateNetworkAdditionalData(nwid, data) {
if (data.hasOwnProperty("rulesSource")) {
additionalData.rulesSource = data.rulesSource;
}
if (data.hasOwnProperty("dnsEnable")) {
if (data.dnsEnable) {
//TODO: start ZeroNSd and get its IP address
additionalData.dnsIP = "127.0.0.1";
} else {
additionalData.dnsIP = null;
}
additionalData.dnsEnable = data.dnsEnable;
}
if (data.hasOwnProperty("dnsDomain")) {
additionalData.dnsDomain = data.dnsDomain;
}
if (data.hasOwnProperty("dnsWildcard")) {
additionalData.dnsWildcard = data.dnsWildcard;
}

if (additionalData) {
db.get("networks")
.filter({ id: nwid })
.map("additionalConfig")
.map((additionalConfig) => _.assign(additionalConfig, additionalData))
.write();

if (data.hasOwnProperty("dnsEnable")) {
zns.handleNet(db.get("networks").filter({ id: nwid }).value()[0]);
}
}
}

Expand Down
147 changes: 147 additions & 0 deletions backend/utils/zns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
const cp = require("child_process");
const path = require("path");
const fs = require("fs");

const db = require("../utils/db");

//TODO: does this kind of "optimization" make sense in Node.js?
let token = null;
function getToken() {
if (!token)
try {
token = db.get("users").value()[0].token;
} catch {
console.warn("*** token retrieval failed");
}
return token;
}

function setPid(nwid, pid) {
db.get("networks")
.filter({ id: nwid })
.map("additionalConfig")
.map((additionalConfig) => (additionalConfig.pidDNS = pid))
.write();
}

const isRunning = (query, pid) => {
return new Promise(function (resolve) {
//FIXME: Check if pgrep is available
cp.exec(`pgrep ${query}`, (err, stdout) => {
resolve(stdout.indexOf(`${pid}`) > -1);
});
});
};

function startDNS(token, nwid, conf) {
//FIXME: check it does the right thing when conf.pidDNS is null/undefined
isRunning("zeronsd", conf.pidDNS).then((ok) => {
if (ok) {
console.log(
`startDNS(${token}, ${nwid}): already active on PID ${conf.pidDNS}`
);
} else {
let cmd = "zeronsd";
let opts = Array();
if (process.geteuid() === 0) {
// production in Docker container
} else {
// we are debugging
let myLocal = "/home/mcon/.cargo/bin";
let pth = process.env.PATH.split(path.delimiter);
if (!pth.includes(myLocal)) pth.push(myLocal);
if (
!process.env.PATH.split(path.delimiter).some(function (d) {
let e = path.resolve(d, cmd);
console.log(`*** PATH testing: "${d}" -> "${e}"`);
try {
fs.accessSync(e, fs.constants.X_OK);
console.log(" is executable");
cmd = "sudo";
opts.push("-E", e);
return true;
} catch (e) {
console.warn(" cannot execute");
return false;
}
})
) {
console.error(`*** zeronsd not found in PATH (${process.env.PATH})`);
return;
}
}
opts.push("start");
if (conf.hasOwnProperty("dnsWildcard") && conf.dnsWildcard) {
opts.push("-w");
}
if (conf.hasOwnProperty("dnsDomain") && !!conf.dnsDomain) {
opts.push("-d", conf.dnsDomain);
}
opts.push(nwid);
process.env.ZEROTIER_CENTRAL_TOKEN = token;
console.log(`*** PATH: "${process.env.PATH}"`);
console.log(
`*** ZEROTIER_CENTRAL_TOKEN: "${process.env.ZEROTIER_CENTRAL_TOKEN}"`
);
let dns = cp.spawn(cmd, opts, { detached: true });
dns.on("spawn", () => {
console.log(
`zeronsd successfully spawned [${dns.pid}](${dns.spawnargs})`
);
setPid(nwid, dns.pid);
});
dns.stdout.on("data", (data) => {
console.log(`zeronsd spawn stdout: ${data}`);
});
dns.stderr.on("data", (data) => {
console.error(`zeronsd spawn stderr: ${data}`);
});
dns.on("error", (error) => {
console.log(`zeronsd spawn ERROR: [${error}](${dns.spawnargs})`);
});
dns.on("close", (code) => {
console.log(`zeronsd exited: [${code}](${dns.spawnargs})`);
setPid(nwid, null);
});
}
});
}

function stopDNS(nwid, conf) {
let pid = conf.pidDNS;
if (pid) {
isRunning("zeronsd", pid).then((ok) => {
if (ok) {
console.log(`stopDNS(${nwid}): stopping PID ${pid}`);
try {
process.kill(pid);
} catch (e) {
console.error(`stopDNS(${nwid}): stopping PID ${pid} FAILED (${e})`);
}
} else {
console.log(`stopDNS(${nwid}): PID ${pid} is stale`);
}
});
setPid(nwid, null);
} else {
console.log(`stopDNS(${nwid}): net has no PID`);
}
}

exports.handleNet = handleNet;
function handleNet(net) {
let cfg = net.additionalConfig;
if (cfg.dnsEnable) {
startDNS(getToken(), net.id, cfg);
} else {
stopDNS(net.id, cfg);
}
}

exports.scan = scan;
function scan() {
let nets = db.get("networks").value();
nets.forEach((net) => {
handleNet(net);
});
}
Loading