Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
RunOnFluxBot committed Nov 25, 2024
1 parent 77e9452 commit f0894c2
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 7 deletions.
32 changes: 32 additions & 0 deletions services/dockerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ async function appDockerCreate(appSpecifications, appName, isComponent, fullAppS
'max-size': '20m',
},
},
ExtraHosts: [`fluxnode.service:${config.server.fluxNodeServiceAddress}`],
},
};

Expand Down Expand Up @@ -1121,6 +1122,36 @@ async function dockerLogsFix() {
}
}

async function getAppNameByContainerIp(ip) {
const fluxNetworks = await docker.listNetworks({
filters: JSON.stringify({
name: ['fluxDockerNetwork'],
}),
});

const fluxNetworkNames = fluxNetworks.map((n) => n.Name);

const networkPromises = [];
fluxNetworkNames.forEach((networkName) => {
const dockerNetwork = docker.getNetwork(networkName);
networkPromises.push(dockerNetwork.inspect());
});

const fluxNetworkData = await Promise.all(networkPromises);

let appName = null;
// eslint-disable-next-line no-restricted-syntax
for (const fluxNetwork of fluxNetworkData) {
const subnet = fluxNetwork.IPAM.Config[0].Subnet;
if (serviceHelper.ipInSubnet(ip, subnet)) {
appName = fluxNetwork.Name.split('_')[1];
break;
}
}

return appName;
}

module.exports = {
appDockerCreate,
appDockerUpdateCpu,
Expand Down Expand Up @@ -1166,4 +1197,5 @@ module.exports = {
pruneNetworks,
pruneVolumes,
removeFluxAppDockerNetwork,
getAppNameByContainerIp,
};
76 changes: 76 additions & 0 deletions services/fluxNetworkHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,80 @@ async function allowNodeToBindPrivilegedPorts() {
}
}

/**
* docker network including mask to allow to verification. For example: 172.23.123.0/24
* @returns {Promise<void>}
*/
async function allowOnlyDockerNetworksToFluxNodeService() {
const firewallActive = await isFirewallActive();

if (!firewallActive) return;

const fluxAppDockerNetworks = '172.23.0.0/16';
const { fluxNodeServiceAddress } = config.server;
const allowDockerNetworks = `LANG="en_US.UTF-8" && sudo ufw allow from ${fluxAppDockerNetworks} proto tcp to ${fluxNodeServiceAddress}/32 port 80`;
// have to use iptables here as ufw won't filter loopback
const denyRule = `INPUT -i lo ! -s ${fluxAppDockerNetworks} -d ${fluxNodeServiceAddress}/32 -j DROP`;
const checkDenyRule = `LANG="en_US.UTF-8" && sudo iptables -C ${denyRule}`;
const denyAllElse = `LANG="en_US.UTF-8" && sudo iptables -I ${denyRule}`;

const cmdAsync = util.promisify(nodecmd.get);

try {
const cmd = await cmdAsync(allowDockerNetworks);
if (serviceHelper.ensureString(cmd).includes('updated') || serviceHelper.ensureString(cmd).includes('existing') || serviceHelper.ensureString(cmd).includes('added')) {
log.info(`Firewall adjusted for network: ${fluxAppDockerNetworks} to address: ${fluxNodeServiceAddress}/32`);
} else {
log.warn(`Failed to adjust Firewall for network: ${fluxAppDockerNetworks} to address: ${fluxNodeServiceAddress}/32`);
}
} catch (err) {
log.error(err);
}

const denied = await cmdAsync(checkDenyRule).catch(async (err) => {
if (err.message.includes('Bad rule')) {
try {
await cmdAsync(denyAllElse);
log.info(`Firewall adjusted to deny access to: ${fluxNodeServiceAddress}/32`);
} catch (error) {
log.error(error);
}
}
});

if (denied) log.info(`Fireall already denying access to ${fluxNodeServiceAddress}/32`);
}

/**
* Adds the 169.254 adddress to the loopback interface for use with the flux node service.
*/
async function addFluxNodeServiceIpToLoopback() {
const cmdAsync = util.promisify(nodecmd.get);

// could also check exists first with:
// ip -f inet addr show lo | grep 169.254.43.43/32
const ip = config.server.fluxNodeServiceAddress;
const addIp = `sudo ip addr add ${ip}/32 dev lo`;

let ok = false;
try {
await cmdAsync(addIp);
ok = true;
} catch (err) {
if (err.message.includes('File exists')) {
ok = true;
} else {
log.error(err);
}
}

if (ok) {
log.info(`fluxNodeService IP: ${ip} added to loopback interface`);
} else {
log.warn(`Failed to add fluxNodeService IP ${ip} to loopback interface`);
}
}

module.exports = {
isFluxAvailable,
checkFluxAvailability,
Expand Down Expand Up @@ -1704,4 +1778,6 @@ module.exports = {
allowNodeToBindPrivilegedPorts,
removeDockerContainerAccessToNonRoutable,
getMaxNumberOfIpChanges,
allowOnlyDockerNetworksToFluxNodeService,
addFluxNodeServiceIpToLoopback,
};
101 changes: 101 additions & 0 deletions services/fluxNodeService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Service that will be available to all docker apps on the network to get Host information
* Host Public IP
* Host Unique Identifier
* Host Geolocation
*/

const config = require('config');
const log = require('../lib/log');
const messageHelper = require('./messageHelper');
const geolocationService = require('./geolocationService');
const fluxNetworkHelper = require('./fluxNetworkHelper');
const generalService = require('./generalService');
const dockerService = require('./dockerService');

const express = require('express');

let server = null;

async function getHostInfo(req, res) {
try {
const app = await dockerService.getAppNameByContainerIp(req.socket.remoteAddress);
if (!app) {
const errMessage = messageHelper.errUnauthorizedMessage();
res.json(errMessage);
} else {
const hostInfo = {};
const nodeCollateralInfo = await generalService.obtainNodeCollateralInformation().catch(() => { throw new Error('Host Identifier information not available at the moment'); });
hostInfo.id = nodeCollateralInfo.txhash + nodeCollateralInfo.txindex;
const myIP = await fluxNetworkHelper.getMyFluxIPandPort();
if (myIP) {
hostInfo.ip = myIP.split(':')[0];
const myGeo = await geolocationService.getNodeGeolocation();
if (myGeo) {
delete myGeo.ip;
delete myGeo.org;
hostInfo.geo = myGeo;
} else {
throw new Error('Geolocation information not available at the moment');
}
} else {
throw new Error('Host IP information not available at the moment');
}

const message = messageHelper.createDataMessage(hostInfo);
res.json(message);
}
} catch (error) {
log.error(`getHostInfo: ${error}`);
const errorResponse = messageHelper.createErrorMessage(
error.message || error,
error.name,
error.code,
);
res.json(errorResponse);
}
}

function handleError(middleware, req, res, next) {
// eslint-disable-next-line consistent-return
middleware(req, res, (err) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
res.statusMessage = err.message;
return res.sendStatus(400);
}
if (err) {
log.error(err);
return res.sendStatus(400);
}

next();
});
}

function start() {
if (server) return;

const app = express();
app.use((req, res, next) => {
handleError(express.json(), req, res, next);
});
app.get('/hostinfo', getHostInfo);
app.all('*', (_, res) => res.status(404).end());

const bindAddress = config.server.fluxNodeServiceAddress;
server = app.listen(80, bindAddress, () => {
log.info(`Server listening on port: 80 address: ${bindAddress}`);
});
}

function stop() {
if (server) {
server.close();
server = null;
}
}

module.exports = {
start,
stop,
};
10 changes: 7 additions & 3 deletions services/geolocationService.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ async function setNodeGeolocation() {
try {
const myIP = await fluxNetworkHelper.getMyFluxIPandPort();
if (!myIP) {
throw new Error('Flux IP not detected. Flux geolocation service is awaiting');
log.error('Flux IP not detected. Flux geolocation service is awaiting');
setTimeout(() => {
setNodeGeolocation();
}, 10 * 1000);
return;
}
if (!storedGeolocation || myIP !== storedIp || execution % 4 === 0) {
log.info(`Checking geolocation of ${myIP}`);
Expand Down Expand Up @@ -66,9 +70,9 @@ async function setNodeGeolocation() {
}
}
execution += 1;
setTimeout(() => { // executes again in 12h
setTimeout(() => { // executes again in 24h
setNodeGeolocation();
}, 12 * 60 * 60 * 1000);
}, 24 * 60 * 60 * 1000);
} catch (error) {
log.error(`Failed to get Geolocation with ${error}`);
log.error(error);
Expand Down
10 changes: 6 additions & 4 deletions services/serviceManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const pgpService = require('./pgpService');
const dockerService = require('./dockerService');
const backupRestoreService = require('./backupRestoreService');
const systemService = require('./systemService');
const fluxNodeService = require('./fluxNodeService');

const apiPort = userconfig.initial.apiport || config.server.apiport;
const development = userconfig.initial.development || false;
Expand All @@ -37,6 +38,9 @@ async function startFluxFunctions() {
upnpService.adjustFirewallForUPNP();
}, 1 * 60 * 60 * 1000); // every 1 hours
}
await fluxNetworkHelper.addFluxNodeServiceIpToLoopback();
await fluxNetworkHelper.allowOnlyDockerNetworksToFluxNodeService();
fluxNodeService.start();
await daemonServiceUtils.buildFluxdClient();
log.info('Checking docker log for corruption...');
await dockerService.dockerLogsFix();
Expand Down Expand Up @@ -123,10 +127,8 @@ async function startFluxFunctions() {
setInterval(() => {
appsService.restorePortsSupport(); // restore fluxos and apps ports/upnp
}, 10 * 60 * 1000); // every 10 minutes
setTimeout(() => {
log.info('Starting setting Node Geolocation');
geolocationService.setNodeGeolocation();
}, 90 * 1000);
log.info('Starting setting Node Geolocation');
geolocationService.setNodeGeolocation();
setTimeout(() => {
const { daemon: { zmqport } } = config;
log.info(`Ensuring zmq is enabled for fluxd on port: ${zmqport}`);
Expand Down

0 comments on commit f0894c2

Please sign in to comment.