nftables-napi 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # nftables-napi
2
2
 
3
- Native Node.js binding for nftables via libnftnl + libmnl. Manages IPv4/IPv6 tables with dynamic sets and timeout support through direct netlink communication — no shell commands, no `nft` CLI.
3
+ Native Node.js binding for nftables via libnftnl + libmnl. Manages IPv4/IPv6 firewall tables with dynamic IP sets, port blocking, named counters, and timeout support through direct netlink communication — no shell commands, no `nft` CLI.
4
4
 
5
- Requires Linux with `CAP_NET_ADMIN` or root.
5
+ Requires Linux kernel ≥ 5.7 with `CAP_NET_ADMIN` or root.
6
6
 
7
7
  ## Install
8
8
 
@@ -34,26 +34,44 @@ RUN apt-get update && apt-get install -y libnftnl13 libmnl0 && rm -rf /var/lib/a
34
34
  const { NftManager } = require("nftables-napi");
35
35
 
36
36
  const nft = new NftManager({
37
- tableName: "tablename",
38
- sets: ["blacklist", "droplist"],
37
+ tableName: "myfw",
38
+ sets: ["blacklist"],
39
+ outSets: ["blocklist"],
40
+ outPortSets: ["blocked_ports"],
39
41
  });
40
42
 
41
43
  await nft.createTable();
42
44
 
43
- // Add with timeout (seconds)
45
+ // ── IP blocking (input/forward) ──
46
+
44
47
  await nft.addAddress({ ip: "1.2.3.4", set: "blacklist", timeout: 1800 });
45
48
  await nft.addAddress({ ip: "2001:db8::1", set: "blacklist", timeout: 3600 });
46
-
47
- // Add permanent (no timeout)
48
- await nft.addAddress({ ip: "5.6.7.8", set: "droplist" });
49
-
50
- // Bulk add
51
49
  await nft.addAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist", timeout: 7200 });
52
50
 
53
- // Remove
54
51
  await nft.removeAddress({ ip: "1.2.3.4", set: "blacklist" });
55
52
  await nft.removeAddresses({ ips: ["10.0.0.1", "10.0.0.2"], set: "blacklist" });
56
53
 
54
+ // ── IP blocking (output) ──
55
+
56
+ await nft.addAddress({ ip: "93.184.216.34", set: "blocklist" });
57
+ await nft.removeAddress({ ip: "93.184.216.34", set: "blocklist" });
58
+
59
+ // ── Port blocking (output, tcp/udp) ──
60
+
61
+ // Block port 80 for both TCP and UDP
62
+ await nft.addPort({ port: 80, set: "blocked_ports", timeout: 3600 });
63
+
64
+ // Block port 443 for TCP only
65
+ await nft.addPort({ port: 443, set: "blocked_ports", protocol: "tcp" });
66
+
67
+ // Bulk port operations
68
+ await nft.addPorts({ ports: [8080, 8443], set: "blocked_ports", protocol: "tcp" });
69
+ await nft.removePorts({ ports: [8080, 8443], set: "blocked_ports", protocol: "tcp" });
70
+
71
+ await nft.removePort({ port: 80, set: "blocked_ports" });
72
+
73
+ // ── Cleanup ──
74
+
57
75
  await nft.deleteTable();
58
76
  ```
59
77
 
@@ -61,23 +79,119 @@ await nft.deleteTable();
61
79
 
62
80
  ### `new NftManager(options)`
63
81
 
64
- | Option | Type | Required | Description |
65
- | ----------- | ---------- | -------- | ------------------------------------------------------------------------------------------------ |
66
- | `tableName` | `string` | Yes | Base table name (IPv6 table auto-appends `'6'`) |
67
- | `sets` | `string[]` | Yes | Set names (1+, unique, non-empty). IPv6 sets auto-append `'6'`. Log prefix: `'{name}: '` |
82
+ | Option | Type | Required | Description |
83
+ | --- | --- | --- | --- |
84
+ | `tableName` | `string` | Yes | Base table name. IPv6 table auto-appends `'6'`. |
85
+ | `sets` | `string[]` | Yes | Input/forward IP set names (1). Block by **source** address on input and forward chains. Rules: log + named counter + drop. IPv6 sets auto-append `'6'`. |
86
+ | `outSets` | `string[]` | No | Output IP set names. Block by **destination** address on output chain. Rules: named counter + drop (no log). IPv6 sets auto-append `'6'`. |
87
+ | `outPortSets` | `string[]` | No | Output port set names. Block by **destination port** (TCP/UDP) on output chain using concatenated `inet_proto . inet_service` sets. Ports are added to both IPv4 and IPv6 tables. IPv6 sets auto-append `'6'`. |
68
88
 
69
89
  ### Methods
70
90
 
71
- All methods return `Promise<void>`.
91
+ All methods return `Promise<void>` and throw on error.
92
+
93
+ #### Table management
94
+
95
+ | Method | Description |
96
+ | --- | --- |
97
+ | `createTable()` | Create IPv4/IPv6 tables with all configured sets, chains, named counters, and filter rules. Idempotent — deletes existing tables first. |
98
+ | `deleteTable()` | Delete both tables. Idempotent — no error if tables don't exist. |
99
+
100
+ #### IP address operations
101
+
102
+ Work with both `sets` (input/forward) and `outSets` (output).
72
103
 
73
- | Method | Description |
74
- | -------------------------------------- | --------------------------------------------------------------------- |
75
- | `createTable()` | Create IPv4/IPv6 tables with all configured sets and filter chains. Idempotent. |
76
- | `deleteTable()` | Delete both tables. Idempotent. |
77
- | `addAddress({ ip, set, timeout? })` | Add IP to set. `timeout` in seconds, omit for permanent. |
78
- | `removeAddress({ ip, set })` | Remove IP from set. Idempotent. |
79
- | `addAddresses({ ips, set, timeout? })` | Bulk add to set. Chunked for efficient netlink communication. |
80
- | `removeAddresses({ ips, set })` | Bulk remove from set. Idempotent. |
104
+ | Method | Description |
105
+ | --- | --- |
106
+ | `addAddress({ ip, set, timeout? })` | Add IP to set. Auto-detects IPv4/IPv6. `timeout` in seconds, omit for permanent. |
107
+ | `removeAddress({ ip, set })` | Remove IP from set. Idempotent. |
108
+ | `addAddresses({ ips, set, timeout? })` | Bulk add. Chunked internally for efficient netlink communication. Empty array is a no-op. |
109
+ | `removeAddresses({ ips, set })` | Bulk remove. Idempotent. Empty array is a no-op. |
110
+
111
+ #### Port operations
112
+
113
+ Work with `outPortSets` only. Ports are added to both IPv4 and IPv6 tables.
114
+
115
+ | Method | Description |
116
+ | --- | --- |
117
+ | `addPort({ port, set, protocol?, timeout? })` | Add port to set. `protocol`: `'tcp'`, `'udp'`, or omit for both. `timeout` in seconds. |
118
+ | `removePort({ port, set, protocol? })` | Remove port from set. Idempotent. |
119
+ | `addPorts({ ports, set, protocol?, timeout? })` | Bulk add ports. Empty array is a no-op. |
120
+ | `removePorts({ ports, set, protocol? })` | Bulk remove ports. Idempotent. Empty array is a no-op. |
121
+
122
+ ### What `createTable()` builds
123
+
124
+ For a config with `sets: ["bl"]`, `outSets: ["out"]`, `outPortSets: ["ports"]`:
125
+
126
+ ```
127
+ table ip myfw {
128
+ counter "processed" { packets 0 bytes 0 }
129
+ counter "bl" { packets 0 bytes 0 }
130
+ counter "out" { packets 0 bytes 0 }
131
+ counter "ports" { packets 0 bytes 0 }
132
+
133
+ set bl {
134
+ type ipv4_addr
135
+ flags timeout
136
+ counter
137
+ }
138
+
139
+ set out {
140
+ type ipv4_addr
141
+ flags timeout
142
+ counter
143
+ }
144
+
145
+ set ports {
146
+ type inet_proto . inet_service
147
+ flags timeout
148
+ counter
149
+ }
150
+
151
+ chain input {
152
+ type filter hook input priority -10; policy accept;
153
+ counter name "processed"
154
+ ip saddr @bl log prefix "bl: " counter name "bl" drop
155
+ }
156
+
157
+ chain forward {
158
+ type filter hook forward priority -10; policy accept;
159
+ counter name "processed"
160
+ ip saddr @bl log prefix "bl: " counter name "bl" drop
161
+ }
162
+
163
+ chain output {
164
+ type filter hook output priority -10; policy accept;
165
+ ip daddr @out counter name "out" drop
166
+ meta l4proto . th dport @ports counter name "ports" drop
167
+ }
168
+ }
169
+ ```
170
+
171
+ IPv6 table (`myfw6`) mirrors the same structure with `ipv6_addr` sets and corresponding offsets.
172
+
173
+ ## Kernel compatibility
174
+
175
+ Minimum: **Linux 5.7**
176
+
177
+ | Feature | Kernel | Used for |
178
+ | --- | --- | --- |
179
+ | nftables core | 3.13 | tables, chains, sets, rules |
180
+ | Set timeouts | 4.1 | element expiration |
181
+ | Named counters | 4.10 | traffic accounting |
182
+ | Concatenated sets | 5.6 | port blocking (`inet_proto . inet_service`) |
183
+ | Per-element set expressions | 5.7 | per-element counters |
184
+
185
+ | Distro | Kernel | Compatible |
186
+ | --- | --- | --- |
187
+ | Ubuntu 22.04+ | 5.15+ | Yes |
188
+ | Ubuntu 20.04 (HWE) | 5.15 | Yes |
189
+ | Ubuntu 20.04 (GA) | 5.4 | No |
190
+ | Debian 11+ | 5.10+ | Yes |
191
+ | Debian 10 | 4.19 | No |
192
+ | RHEL / Rocky 9 | 5.14 | Yes |
193
+ | RHEL / Rocky 8 | 4.18 | No |
194
+ | Alpine 3.16+ | 5.15+ | Yes |
81
195
 
82
196
  ## Building from source
83
197
 
@@ -85,9 +199,15 @@ All methods return `Promise<void>`.
85
199
  # Dependencies (Debian/Ubuntu)
86
200
  sudo apt install pkg-config libnftnl-dev libmnl-dev build-essential
87
201
 
202
+ # Dependencies (Alpine)
203
+ apk add pkgconfig libnftnl-dev libmnl-dev build-base python3
204
+
88
205
  # Build
89
206
  npm run build
90
207
 
208
+ # Run tests (requires root / CAP_NET_ADMIN)
209
+ npm test
210
+
91
211
  # Prebuild for current platform
92
212
  npx prebuildify --napi --strip
93
213
 
package/binding.gyp CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "targets": [
3
3
  {
4
- "target_name": "remnawave_nft",
4
+ "target_name": "nftables_napi",
5
5
  "sources": [
6
6
  "src/addon.cpp",
7
7
  "src/nft_manager.cpp",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nftables-napi",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Native Node.js binding for nftables via libnftnl+libmnl — nftables firewall management",
5
5
  "author": {
6
6
  "name": "kastov",