Skip to content

Commit

Permalink
Refactor and Restructure
Browse files Browse the repository at this point in the history
Restructured project for logical reasons.

Updated README.md for new launching requirements.
Also documented hot loading of rules.json.

Tweaked index.js for new paths.
Removed some redundant code.
Added ability to call callback, if supplied, on rule triggers.
Moved output updates to individual function interval of 2.5 seconds
This should improve battery usage on laptops on high bandwidth interfacs
Added catch for final counters insert failure.
  • Loading branch information
Dustyn Blackmore committed Apr 18, 2018
1 parent b0f0958 commit f6d3197
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 24 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Once running, you must initialize the app with sudo (Due to use of libpcap).

I personally use;

```sudo `which node` index.js```
```sudo `which node` src/index.js```

# Usage
You can customize your rules within the *.json configuration files.
Expand All @@ -52,8 +52,10 @@ is actually functioning. The overall flow is;
filters)

# Customisation
Configuration files may be found in src/config.
* interfaces.json - specify your trusted, and untrusted, interfaces.
* rules.json - Specify what ports, in which 'trust' zones you want to allow
* * Note: Changes to this file are 'hot loaded'. Care should be taken.
* base.rules - Is the 'initial' template of rules deployed. (Creates the
appropriate table, chains)
* locked.rules - Is basically what the script 'should' fall back to if there
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "nftables_firewall.js",
"version": "0.0.1",
"description": "Is a test / proof of concept firewall using NodeJS and nftables/nfqueue.",
"main": "index.js",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
43 changes: 21 additions & 22 deletions index.js → src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ const sysClassNetInterfaces = '/sys/class/net/';
const fs = require('fs');
const nfq = require('nfqueue');
const IPv4 = require('pcap/decode/ipv4');
let rules = require('./rules.json').rules;
let rules = require('./config/rules.json').rules;
// const rules = require('./rules.json').rules;
const systemInterfaces = require('./interfaces.json').interfaces;
const systemInterfaces = require('./config/interfaces.json').interfaces;
const { exec } = require('child_process');

const nft = require('./src/nftables')({ exec: exec });
const nft = require('./nftables')({ exec: exec });

let ruleWatch = fs.watch('./rules.json', 'utf8', () => { setTimeout(loadRules, 500) });
let ruleWatch = fs.watch('./src/config', () => { setTimeout(loadRules, 500) });

function loadRules (err, filename) {
console.log('Detected ' + filename + ' change. Ingesting.');
fs.readFile('./rules.json', 'utf8', (err, data) => {
fs.readFile('./config/rules.json', 'utf8', (err, data) => {
if (err) throw err;
let newRules = JSON.parse(data);
rules = newRules.rules;
Expand Down Expand Up @@ -46,18 +46,6 @@ let packetsRejectedOut = 0;
// An array to store our interfaces.
let interfaces = []

function execute (command) {
return new Promise(function (resolve, reject) {
exec(command, (err, stdout, stderr) => {
if (err) {
reject(err)
} else {
resolve(stdout);
}
});
});
}

// Sets base rules, with default to 'drop', but allows established and related connections.
function insertFinalCounters () {
return Promise.all([
Expand Down Expand Up @@ -101,6 +89,9 @@ function determineVerdict (interface, packet, direction) {
if (rules[direction][packet.protocol.toString()].global.ports) {
if (rules[direction][packet.protocol.toString()].global.ports[packet.payload.dport]) {
thisVerdict = NF_ACCEPT;
if (rules[direction][packet.protocol.toString()].global.ports[packet.payload.dport].callback) {
rules[direction][packet.protocol.toString()].global.ports[packet.payload.dport].callback();
}
return thisVerdict;
}
} else {
Expand All @@ -115,6 +106,9 @@ function determineVerdict (interface, packet, direction) {
if (rules[direction][packet.protocol.toString()][interface.zone].ports) {
if (rules[direction][packet.protocol.toString()][interface.zone].ports[packet.payload.dport]) {
thisVerdict = NF_ACCEPT;
if (rules[direction][packet.protocol.toString()][interface.zone].ports[packet.payload.dport].callback) {
rules[direction][packet.protocol.toString()][interface.zone].ports[packet.payload.dport].callback();
}
}
} else {
thisVerdict = NF_ACCEPT;
Expand All @@ -125,6 +119,10 @@ function determineVerdict (interface, packet, direction) {
return thisVerdict;
}

function updateOutput () {
process.stdout.write('Connections - Accepted: ' + packetsAccepted + ' (I: ' + packetsAcceptedIn + ' O: ' + packetsAcceptedOut + ') - Rejected: ' + packetsRejected + ' (I: ' + packetsRejectedIn + ' O: ' + packetsRejectedOut + ')\r');
}

function bindQueueHandlers () {
interfaces.forEach(interface => {
interface.queueIn = nfq.createQueueHandler(parseInt(interface.number), buffer, (nfpacket) => {
Expand All @@ -140,9 +138,8 @@ function bindQueueHandlers () {
packetsAcceptedIn++;
nfpacket.setVerdict(NF_REQUEUE, 999);
}

process.stdout.write('Connections - Accepted: ' + packetsAccepted + ' (I: ' + packetsAcceptedIn + ' O: ' + packetsAcceptedOut + ') - Rejected: ' + packetsRejected + ' (I: ' + packetsRejectedIn + ' O: ' + packetsRejectedOut + ')\r');
});

interface.queueOut = nfq.createQueueHandler(parseInt('100' + interface.number), buffer, (nfpacket) => {
let packet = new IPv4().decode(nfpacket.payload, 0);
let thisVerdict = determineVerdict(interface, packet, 'outgoing');
Expand All @@ -160,14 +157,12 @@ function bindQueueHandlers () {
packetsAcceptedOut++;
nfpacket.setVerdict(NF_REQUEUE, 999);
}

process.stdout.write('Connections - Accepted: ' + packetsAccepted + ' (I: ' + packetsAcceptedIn + ' O: ' + packetsAcceptedOut + ') - Rejected: ' + packetsRejected + ' (I: ' + packetsRejectedIn + ' O: ' + packetsRejectedOut + ')\r');
});
})
}

nft.flush().then(
(resolved) => nft.inject('./base.rules'),
(resolved) => nft.inject('./src/config/base.rules'),
(reject) => console.log('failed to flush rules')
).then(
(resolved) => setupInterfaces(),
Expand All @@ -178,4 +173,8 @@ nft.flush().then(
).then(
(resolved) => insertFinalCounters(),
(reject) => console.log('Failed to bind queue handlers')
).catch(
(err) => console.log('Failed to insert final counters')
);

const outputInterval = setInterval(updateOutput, 2500);

0 comments on commit f6d3197

Please sign in to comment.