1.0.0 • Published 9 months ago

node-libnftables v1.0.0

Weekly downloads
-
License
ISC
Repository
github
Last release
9 months ago

node-libnftables

ABI stable native bindings to libnftables, to view and modify nftables firewall rulesets on Linux. For example, to build a paywall or a Wi-Fi hotspot.

Uses node-addon-api and cmake-js to compile and link to libnftables on your platform.

If you only want to deploy static nftables firewall rules then this is probably not the tool you need. E.g. on Debian just add the ruleset to /etc/nftables.conf, and Debian will re-install the rules on boot.

Refer to the Nftables Wiki for more information on nftables:

https://wiki.nftables.org/wiki-nftables/index.php/Main_Page

Security

To view or modify nftables firewall rules you must run node.js as a user account with CAP_NET_ADMIN capabilities. So as well as being able to view and modify firewall configurations, the user account will also have sufficient privileges to view and modify network interface configurations and routing tables. Be careful!

For example, confine your application within an unprivileged LXC container, and configure the global route table to route traffic of interest via the LXC's virtual network interfaces.

Dependencies

  • Linux kernel >= 4.14
  • libnftables >= 0.9.6
  • Linux cmake, build-essential, and libnftables-dev packages

Installing

Installation uses cmake-js to compile the add-on and link to libnftables on your platform. Binaries are not included in the NPM module.

First, make sure you have a working build system including the libftables headers: E.g. for Debian 12:

sudo apt install build-essential cmake curl libnftables-dev

Now use npm to install node.js dependencies and compile the add-on for your system:

npm install

Testing

Loads the module and attempts to read the running nftables ruleset. Just a simple test to validate that the addon has linked to libnftables and node is running as a user with sufficient privileges to manage nftables rulesets.

sudo npm test

Usage Examples

const { LibNftablesContext, NFT_FLAGS } = require('node-libnftables');
const nftContext = new LibNftablesContext();

let baseRuleSet = [
    'add table ip paywall',    
    'add chain ip paywall forward { type filter hook forward priority 0; policy drop; }',
    'add set ip paywall authorised { type ipv4_addr; flags interval; }',
    'add rule ip paywall forward ip saddr @authorised accept', 
    'add rule ip paywall forward ip daddr @authorised ct state established,related accept',
    'add element ip paywall authorised { 10.1.2.3, 10.1.2.4, 192.168.1.0/24 }'
];

for (const rule of baseRuleSet) {
    nftContext.runCmd(rule);
}

let listChain = 'list chain ip paywall forward'; // see `man nft` for syntax

nftContext.runCmd(listChain).asArray();  // returns an array of [rules]
>>> [
    'table ip paywall ',
    'chain forward ',
    'type filter hook forward priority filter; policy drop;',
    'ip saddr @authorised accept',
    'ip daddr @authorised ct state established,related accept'
]

// include ruleset handles in the output, and return results as a Map of { handle: rule }
nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_HANDLE).runCmd(listChain).asMap(); // most methods are chainable
>>> Map(5) {
    '32' => 'table ip paywall',
        '2' => 'set authorised',
        '1' => 'chain forward',
        '3' => 'ip saddr @authorised accept',
        '4' => 'ip daddr @authorised ct state established,related accept'
}

// libnftables can also be configured to output JSON. 
// See `man libnftables-json` for a description of the schema
nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_JSON, NFT_FLAGS.OUTPUT_NUMERIC_ALL);
nftContext.runCmd(listChain).asObject();
>>> {
    nftables: [
        { metainfo: [Object] },
        { chain: [Object] },
        { rule: [Object] },
        { rule: [Object] }
    ]
}

Usage

The module exports a class that creates a libnftables context and provides some helper methods to run nftables commands and parse the output.

The module also exports an object containing the output control flags for the installed version of libnftables.

const { LibNftablesContext, NFT_FLAGS } = require('node-libnftables');
const nftContext = new LibNftablesContext();

One libnftables context should be sufficient for most use cases. You may encounter NF_NETLINK resource issues if you try to use multiple libnftables contexts, or if you create and release libnftables contexts too rapidly.

All methods throw an Error if a command is not accepted by libnftables. The Error type and message may contain further information.

Most methods are chainable:

let ruleset = nftContext.setOutputFlags('NFT_FLAGS.TERSE').runCmd('list ruleset').asArray();

LibNftablesContext

Constructor

Creates a new libnftables context

const nftContext = new LibNftablesContext();

runCmd (nftCommand)

Run nftables command(s), similar to using "nft -i".

See man nftables and https://wiki.nftables.org/wiki-nftables/index.php/Main_Page for examples.

Multiple command lines can be sent, delimited by the newline character (\n)

@param nftCommand {string}- single or multiline string, as used by the nft command line utility

@returns {LibNftablesContext}

@throws {Error} nftables rejected a command

nftContext.runCmd('add rule ip filter input iif lo accept');

asString ()

Returns the last nftables response as a raw string, including any tabs (\t) and newline (\n) characters

@returns {string}

nftContext.runCmd('list chain ip filter input').asString();

asArray ()

Returns the last nftables response as an array of response lines, filtered to omit tabs, braces and empty lines

@returns {string[]}

nftContext.runCmd('list chain ip filter input').asArray();

asMap ()

Returns the last nftables response as a Map object with nftables handles for each rule. You can use the nftables handle to delete a rule.

Note: Requires that the OUTPUT_HANDLE flag has been set, else the map will be empty

@returns Map { handle: rule }

nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_HANDLE);
nftContext.runCmd('list chain ip filter input').asMap();

asObject ()

Parses the last response from nftables as JSON and returns the response as an object. The OUTPUT_JSON flag must have been set or JSON parsing will fail.

See libnftables-json(5) for a description of the supported schema.

@returns {any}

@throws {SyntaxError} libnftables did not return valid JSON

nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_JSON);
nftContext.runCmd('list chain ip filter input').asObject();

setOutputFlags (flag1, flag2, ...)

Set output flags for the libnftables context. See NFT_FLAGS for the valid flag values. Overwrites any previous flag settings. Flag values persist for the lifetime of the context unless overwritten by a new call to setOutputFlags().

@param {...int} flags - one or more output flags to set. All other flags will be cleared.

@returns {LibNftablesContext}

@throws {Error} invalid flag values

// Output ruleset handles, numeric output, omit contents of sets. All other flags are cleared.
nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_HANDLE, NFT_FLAGS.OUTPUT_NUMERIC_ALL, NFT_FLAGS.OUTPUT_TERSE);

dryRun (nftCommands)

Set Dry Run Mode. When Dry Run is enabled libnftables will parse commands, but will not update the rule set.

@param dry {boolean} - default True

@returns {LibNftablesContext}

nftContext.dryRun(true);
nftContext.runCmd('add rule ip filter input ip saddr 127.0.0.0/8 accept');
nftContext.dryRun(false);

refreshContext ()

Refresh the libnftables context. You may need this to make sure ruleset counters have been updated. Releases the current libnftables context and creates a new context with the same output flags. Not needed unless your counters are not updating frequently enough.

@returns {LibNftablesContext}

@throws {Error} unable to create the new context

nftContext.refreshContext();

NFT_FLAGS

A helper object that provides values of the nftables output control flags for the installed version of libnftables

// Configure the libnftables context to output ruleset handles, numeric output, omit contents of sets. 
// All other flags are cleared.
nftContext.setOutputFlags(NFT_FLAGS.OUTPUT_HANDLE, NFT_FLAGS.OUTPUT_NUMERIC_ALL, NFT_FLAGS.OUTPUT_TERSE);

OUTPUT_DEFAULT

Restore the default output settings for the installed version of libnftables

OUTPUT_ECHO

Echo the commands sent to nftables, as well as the responses.

OUTPUT_HANDLE

Upon insertion into the ruleset, some elements are assigned a unique handle for identification purposes. For example, when deleting a table or chain, it may be identified either by name or handle. Rules on the other hand must be deleted by handle, because there is no other way to uniquely identify them. This flag makes ruleset listings include handle values

OUTPUT_REVERSE_DNS

libnftables will use Reverse DNS lookups for IP addresses in the output. Note that this may add significant delay to commands, depending on the speed of your DNS resolver.

OUTPUT_SERVICE_NAME

Translate ports to service names as defined in /etc/services

OUTPUT_STATELESS

Omit stateful data e.g. packet and byte counters from the output.

OUTPUT_JSON

Format output as JSON. See libnftables-json(5) for a description of the supported schema. If enabled at compile-time, libnftables accepts commands in JSON format and is able to print output in JSON format as well. This flag enables JSON output; the input command format is auto-detected.

OUTPUT_GUID

Translate numeric UID/GID to names as defined in /etc/passwd and /etc/group

OUTPUT_NUMERIC_PROTOCOL

Output layer 4 protocols numerically

OUTPUT_NUMERIC_PRIORITY

Output nftables base chain priorities numerically

OUTPUT_NUMERIC_SYMBOL

Output symbolic constants numerically

OUTPUT_NUMERIC_TIME

Output time values numerically

OUTPUT_NUMERIC_ALL

Sets OUTPUT_NUMERIC_PROTOCOL, OUTPUT_NUMERIC_PRIORITY, OUTPUT_NUMERIC_SYMBOL, and OUTPUT_NUMERIC_TIME

OUTPUT_TERSE

Omit contents of nftables sets and maps.