@push.rocks/smartnftables 1.6.0 → 1.8.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/changelog.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-14 - 1.8.0
4
+
5
+ ### Features
6
+
7
+ - Add terminal policy detachment with exact PERSIST verification, retained-intent recovery and failure-safe native process cleanup.
8
+
9
+ ## 2026-09-14 - 1.7.0
10
+
11
+ ### Features
12
+
13
+ - Add a host-wide allocation-pool guard with direction-aware IPv4 denial, bounded atomic updates, and native packet and recovery qualification.
14
+
3
15
  ## 2026-09-13 - 1.6.0
4
16
 
5
17
  ### Features
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "3dd9944031490243ac7c5452afde0681acc1672c975fd9c15d6956f7e8408604",
3
+ "binarySha256": "e7f3f755b5f9579969e94d70442710a8b2ebde0aa8dda4c0b312094b8771caf6",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartnftables",
6
- "projectVersion": "1.6.0",
7
- "gitCommit": "4962c8d3ebc469287abf0c2acf5e3b413fa23e64",
6
+ "projectVersion": "1.8.0",
7
+ "gitCommit": "63a819de3661273bd4df2cea457f9fb3c4131cc9",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-13T17:03:57.902Z",
9
+ "builtAt": "2026-09-14T03:26:32.636Z",
10
10
  "tsrustVersion": "1.13.0",
11
11
  "binary": "smartnftables",
12
12
  "target": "linux_amd64_musl"
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "0fbd6afc372d977bb68c9816e3fcb042d2202a433344a4c8ffc44b6e6d66e3c8",
3
+ "binarySha256": "f80787be41352d19daf0b3916cb285fe29aa3e88f637180109a07ab8ec609b14",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartnftables",
6
- "projectVersion": "1.6.0",
7
- "gitCommit": "4962c8d3ebc469287abf0c2acf5e3b413fa23e64",
6
+ "projectVersion": "1.8.0",
7
+ "gitCommit": "63a819de3661273bd4df2cea457f9fb3c4131cc9",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-13T17:03:58.037Z",
9
+ "builtAt": "2026-09-14T03:26:32.863Z",
10
10
  "tsrustVersion": "1.13.0",
11
11
  "binary": "smartnftables",
12
12
  "target": "linux_arm64_musl"
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartnftables',
6
- version: '1.6.0',
6
+ version: '1.8.0',
7
7
  description: 'A TypeScript module for managing nftables rules including NAT, firewall, and rate limiting with a high-level API.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG1IQUFtSDtDQUNqSSxDQUFBIn0=
@@ -18,6 +18,13 @@ export declare class ManagedNftables<TPolicy extends TManagedNftPolicy = IManage
18
18
  release(expected: IAppliedManagedNftPolicy<TPolicy>): Promise<{
19
19
  released: true;
20
20
  }>;
21
- /** Join policy operations and owned-table deletion, then native termination; packet/allocation fencing belongs to the caller. */
21
+ /** Permanently selects retention cleanup, verifies exact PERSIST retention, and
22
+ * joins the native process. Retain the full applied value before this call.
23
+ * After any failure close() only terminates; it never deletes the table. */
24
+ detach(expected: IAppliedManagedNftPolicy<TPolicy>): Promise<{
25
+ detached: true;
26
+ }>;
27
+ /** Join policy operations, then delete and terminate unless detach selected retention.
28
+ * Packet/allocation fencing belongs to the caller; already-admitted close cannot be undone. */
22
29
  close(): Promise<void>;
23
30
  }
@@ -62,6 +62,9 @@ export class ManagedNftables {
62
62
  #work = null;
63
63
  #close = null;
64
64
  #nativeMayOwn = false;
65
+ #retaining = false;
66
+ #detachExpected = null;
67
+ #detach = null;
65
68
  constructor(options) {
66
69
  this.#options = snapshot(options);
67
70
  if (Object.keys(this.#options).some((key) => !['ownerId', 'instanceId', 'tableName', 'binaryPath', 'networkNamespaceFd'].includes(key))
@@ -78,7 +81,7 @@ export class ManagedNftables {
78
81
  get state() { return this.#state; }
79
82
  /** Starts only the compiler/IPC owner. The returned idle status is not enforcement. */
80
83
  start() {
81
- if (this.#state !== 'new')
84
+ if (this.#state !== 'new' || this.#retaining)
82
85
  return Promise.reject(new ManagedNftablesError('UNAVAILABLE'));
83
86
  this.#state = 'starting';
84
87
  const work = (async () => {
@@ -92,7 +95,7 @@ export class ManagedNftables {
92
95
  cliArgs: ['--management', ...(this.#options.networkNamespaceFd === undefined ? [] : ['--network-namespace-fd', '3'])],
93
96
  inheritedFileDescriptors: this.#options.networkNamespaceFd === undefined ? undefined : [this.#options.networkNamespaceFd],
94
97
  readyTimeoutMs: 3000, requestTimeoutMs: 30_000, maxPayloadSize: 262_144,
95
- maxPendingRequestsByMethod: { preparePolicy: 1, openOwner: 1, reconcilePolicy: 1, inspectPolicy: 1, releasePolicy: 1, closeOwner: 1 },
98
+ maxPendingRequestsByMethod: { preparePolicy: 1, openOwner: 1, reconcilePolicy: 1, inspectPolicy: 1, releasePolicy: 1, detachPolicy: 1, closeOwner: 1 },
96
99
  });
97
100
  this.#bridge.on('exit', () => {
98
101
  if (!['closing', 'stopped'].includes(this.#state))
@@ -119,17 +122,50 @@ export class ManagedNftables {
119
122
  release(expected) {
120
123
  return this.#invoke('releasePolicy', expected);
121
124
  }
122
- /** Join policy operations and owned-table deletion, then native termination; packet/allocation fencing belongs to the caller. */
125
+ /** Permanently selects retention cleanup, verifies exact PERSIST retention, and
126
+ * joins the native process. Retain the full applied value before this call.
127
+ * After any failure close() only terminates; it never deletes the table. */
128
+ detach(expected) {
129
+ this.#retaining = true;
130
+ let captured;
131
+ try {
132
+ captured = snapshot(expected);
133
+ if (this.#detachExpected && !plugins.util.isDeepStrictEqual(this.#detachExpected, captured))
134
+ throw new ManagedNftablesError('CONFLICT');
135
+ if (this.#detach)
136
+ return this.#detach;
137
+ if (!['ready', 'failed-owned'].includes(this.#state) || !this.#bridge || this.#work || this.#close)
138
+ throw new ManagedNftablesError('UNAVAILABLE');
139
+ this.#detachExpected = captured;
140
+ }
141
+ catch (error) {
142
+ return Promise.reject(error instanceof ManagedNftablesError ? error : new ManagedNftablesError('INVALID'));
143
+ }
144
+ const work = this.#track(this.#bridge.sendCommand('detachPolicy', captured).then((value) => {
145
+ const result = snapshot(value);
146
+ if (result?.detached !== true || Object.keys(result).length !== 1)
147
+ throw new ManagedNftablesError('PROTOCOL');
148
+ this.#nativeMayOwn = false;
149
+ return result;
150
+ }));
151
+ const detached = (async () => { const result = await work; await this.close(); return result; })();
152
+ this.#detach = detached;
153
+ void detached.catch(() => { this.#detach = null; });
154
+ return detached;
155
+ }
156
+ /** Join policy operations, then delete and terminate unless detach selected retention.
157
+ * Packet/allocation fencing belongs to the caller; already-admitted close cannot be undone. */
123
158
  close() {
124
159
  if (this.#close)
125
160
  return this.#close;
126
161
  if (this.#state === 'stopped')
127
162
  return Promise.resolve();
163
+ const retaining = this.#retaining;
128
164
  this.#state = 'closing';
129
165
  this.#close = (async () => {
130
166
  await this.#work?.catch(() => undefined);
131
167
  if (this.#bridge) {
132
- if (this.#nativeMayOwn) {
168
+ if (this.#nativeMayOwn && !retaining) {
133
169
  const result = await this.#bridge.sendCommand('closeOwner', {});
134
170
  if (result.released !== true)
135
171
  throw new ManagedNftablesError('PROTOCOL');
@@ -146,7 +182,7 @@ export class ManagedNftables {
146
182
  return this.#close;
147
183
  }
148
184
  #invoke(method, params) {
149
- if (!['ready', 'failed-owned'].includes(this.#state) || !this.#bridge || this.#work)
185
+ if (this.#retaining || !['ready', 'failed-owned'].includes(this.#state) || !this.#bridge || this.#work)
150
186
  return Promise.reject(new ManagedNftablesError('UNAVAILABLE'));
151
187
  let captured;
152
188
  try {
@@ -179,4 +215,4 @@ export class ManagedNftables {
179
215
  return owned;
180
216
  }
181
217
  }
182
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QyxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsS0FBSztJQUM3QyxZQUE0QixJQUFZO1FBQUksS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQW5ELFNBQUksR0FBSixJQUFJLENBQVE7SUFBd0MsQ0FBQztDQUNsRjtBQUVELG9GQUFvRjtBQUNwRixNQUFNLFVBQVUsUUFBUSxDQUFJLEtBQVE7SUFDbEMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYSxFQUFVLEVBQUU7UUFDdkMsSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLE9BQU8sSUFBSSxDQUFDLFdBQVcsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsT0FBTztZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3SCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQztJQUNGLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFFLEtBQWEsRUFBVyxFQUFFO1FBQ3RELElBQUksRUFBRSxLQUFLLEdBQUcsTUFBTSxJQUFJLEtBQUssR0FBRyxFQUFFO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlFLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtZQUFFLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BELElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxTQUFTO1lBQUUsT0FBTyxL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
218
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QyxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsS0FBSztJQUM3QyxZQUE0QixJQUFZO1FBQUksS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQW5ELFNBQUksR0FBSixJQUFJLENBQVE7SUFBd0MsQ0FBQztDQUNsRjtBQUVELG9GQUFvRjtBQUNwRixNQUFNLFVBQVUsUUFBUSxDQUFJLEtBQVE7SUFDbEMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYSxFQUFVLEVBQUU7UUFDdkMsSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLE9BQU8sSUFBSSxDQUFDLFdBQVcsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsT0FBTztZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3SCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQztJQUNGLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFFLEtBQWEsRUFBVyxFQUFFO1FBQ3RELElBQUksRUFBRSxLQUFLLEdBQUcsTUFBTSxJQUFJLEtBQUssR0FBRyxFQUFFO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlFLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtZQUFFLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BELElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxTQUFTO1lBQUUsT0FBTyxL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
@@ -84,8 +84,16 @@ export interface IManagedNftHostTransitScopeV2 {
84
84
  /** Exact current address present on uplink. No masquerade or default-route inference. */
85
85
  snatAddress: string;
86
86
  }
87
+ /** Host-wide IPv4 destination denial for caller-authenticated private allocation pools.
88
+ * This has no link dependency or allow grants. It is not allocation-release or boot-order proof. */
89
+ export interface IManagedNftAllocationPoolGuardScopeV2 {
90
+ kind: 'allocationPoolGuard';
91
+ authorityDigest: string;
92
+ /** Complete current pool list: 1–64 canonical, disjoint RFC1918 prefixes. */
93
+ prefixes: string[];
94
+ }
87
95
  export interface IManagedNftPolicyV2 {
88
96
  schemaVersion: 2;
89
97
  revision: number;
90
- scope: IManagedNftRouterEgressScopeV2 | IManagedNftHostTransitScopeV2;
98
+ scope: IManagedNftRouterEgressScopeV2 | IManagedNftHostTransitScopeV2 | IManagedNftAllocationPoolGuardScopeV2;
91
99
  }
@@ -53,7 +53,7 @@ export interface IManagedNftTransition<TPolicy extends TManagedNftPolicy = IMana
53
53
  }
54
54
  export interface IManagedNftStatus<TPolicy extends TManagedNftPolicy = IManagedNftPolicy> {
55
55
  identity: IManagedNftIdentity;
56
- state: 'idle' | 'applied' | 'failed-owned' | 'released';
56
+ state: 'idle' | 'applied' | 'failed-owned' | 'released' | 'retained';
57
57
  enforced: boolean;
58
58
  applied: IAppliedManagedNftPolicy<TPolicy> | null;
59
59
  pending: IManagedNftTransition<TPolicy> | null;
@@ -96,6 +96,12 @@ export type TManagedNftCommands<TPolicy extends TManagedNftPolicy = IManagedNftP
96
96
  released: true;
97
97
  };
98
98
  };
99
+ detachPolicy: {
100
+ params: IAppliedManagedNftPolicy<TPolicy>;
101
+ result: {
102
+ detached: true;
103
+ };
104
+ };
99
105
  closeOwner: {
100
106
  params: Record<string, never>;
101
107
  result: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartnftables",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "private": false,
5
5
  "description": "A TypeScript module for managing nftables rules including NAT, firewall, and rate limiting with a high-level API.",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -113,7 +113,8 @@ grants remain outstanding.
113
113
  | `reconcile({ previous, target })` | Creates or replaces the entire policy in one kernel transaction. `previous` is null initially, otherwise the full prior applied result. |
114
114
  | `inspect()` | Reads the current owned graph and reports `enforced`, pending intent and a bounded error code. |
115
115
  | `release(applied)` | Verifies the exact retained graph and confirms deletion; repeats are idempotent. |
116
- | `close()` | Stops command admission, joins admitted policy operations, confirms owned-table deletion, then joins the native process. Failure retains the owner for explicit recovery. |
116
+ | `detach(applied)` | Selects terminal retention, verifies the same graph with PERSIST-only flags after dropping socket ownership, then joins the native process. Returns `{ detached: true }`; retain the full applied result. |
117
+ | `close()` | Stops command admission, joins admitted policy operations, confirms owned-table deletion unless `detach()` selected retention, then joins the native process. Failure retains the owner for explicit recovery. |
117
118
 
118
119
  Schema-v1 policies are directed. Return traffic requires its own grant; there is no broad
119
120
  connection-tracking bypass. A null endpoint denotes the actual local host, with an
@@ -154,6 +155,33 @@ invalidates the old boot receipt; the caller must fence its old link/workload
154
155
  lifecycle before starting a new owner instance. Cleanup failures and uncertain
155
156
  operations must not be reported as successful release or used to transfer authority.
156
157
 
158
+ For an orderly restart that must preserve policy, persist the complete applied
159
+ result and call `await owner.detach(applied)` before calling `close()`. The native
160
+ operation validates the exact receipt, graph, interfaces, boot and namespace. It
161
+ accepts this socket's OWNER/PERSIST table or an exact orphan; it never adopts a
162
+ foreign owner. After closing the owner socket it reads the graph again and requires
163
+ the same handle and compiled body with PERSIST-only flags. Native inspection then
164
+ reports `state: 'retained'`. The facade returns success only after child termination.
165
+
166
+ Calling `detach()` irreversibly selects retention cleanup, including invalid input,
167
+ transport failure, timeout, native rejection or malformed acknowledgement. Subsequent
168
+ `close()` only joins and terminates; it never sends a table-deletion command. It
169
+ cannot undo a `close()` already admitted earlier. Other policy operations reject.
170
+ Resolve any pending policy transition by exact replay before requesting detachment.
171
+ If a detach ACK is lost while the process remains available, only the same captured
172
+ body may be retried. After process loss, start a fresh owner with identical options
173
+ and retry `detach(applied)` against the exact orphan. Absent graphs, foreign active
174
+ owners, changed interfaces, handles, bodies or boot/namespace identities reject.
175
+ Native release/reconcile/close commands reject after a valid detach intent, even
176
+ when post-drop verification fails; explicit recovery requires the retained body.
177
+
178
+ Detachment confirms retention at observation time. A completed facade retry returns
179
+ its existing acknowledgement; it is not a fresh kernel inspection. Retention does
180
+ not guarantee future enforcement, namespace survival or reboot ordering, and does
181
+ not withdraw external authority, drain packets, clear conntrack or release leases.
182
+ A separately created owner can later recover the full policy and perform an
183
+ explicit release after the caller has fenced that authority and packet lifecycle.
184
+
157
185
  Apply and release receipts describe this owner's table and policy operations.
158
186
  They do not certify that packets admitted under an earlier graph have drained:
159
187
  foreign NFQUEUE or other deferred packet owners may still hold such packets.
@@ -168,12 +196,41 @@ conntrack/NAT state. This restriction also applies to private veth/TUN forwardin
168
196
  The prepared/applied/transition/status interfaces accept the same policy type
169
197
  parameter; existing callers default to the unchanged schema-v1 contract. V1
170
198
  canonical digests and compiled bytes are preserved. V2 uses its own hash domain.
171
- An owner cannot transition between v1 private, v2 router, and v2 host policy kinds.
199
+ An owner cannot transition between v1 private, v2 router, v2 host transit, and v2
200
+ allocation-pool guard policy kinds.
172
201
 
173
202
  | V2 scope | Required authority and behavior |
174
203
  | --- | --- |
175
204
  | `routerEgress` | Private `endpoints` and `rules`, one exact `links` binding per endpoint, a separate veth `handoff`, `protection`, and active `generations`. Private veth/TUN/local DNS and egress share one table so terminal private denial cannot override a separate egress table. |
176
205
  | `hostTransit` | Exact handoff `link`/`allocations` pairs, complete `protection`, an explicit veth or Ethernet `uplink`, and its current `snatAddress`. It checks each handoff's leased source address and protocol/port range, default conntrack zone, direction, uplink, and protected destinations before outer SNAT. |
206
+ | `allocationPoolGuard` | An authenticated `authorityDigest` and complete current allocation-pool `prefixes`. Installs host-wide IPv4 destination denial before any handoff exists, without link, uplink or SNAT dependencies. |
207
+
208
+ `allocationPoolGuard` accepts 1–64 canonical, disjoint RFC1918 prefixes. Supply
209
+ the actual allocation pools, not the broader protected union containing management
210
+ LANs, resolvers and platform endpoints. It applies to IPv4 INPUT, FORWARD and OUTPUT
211
+ at filter priority 0, after normal destination NAT. It drops an original conntrack
212
+ destination in a pool. Reply-direction packets additionally check their current
213
+ source, then return; all remaining packets check their current destination. This
214
+ denies DNAT into or away from a pool and untracked current-destination traffic,
215
+ while permitting reverse-SNAT replies to authorized transit sources. It grants no
216
+ egress permissions; exact `hostTransit` and router policies remain separate owners.
217
+ Unrelated host traffic and IPv6 retain their existing behavior.
218
+
219
+ For example, a guard policy is `{ schemaVersion: 2, revision: 1, scope: {
220
+ kind: 'allocationPoolGuard', authorityDigest, prefixes: ['10.240.0.0/16',
221
+ '10.241.0.0/16'] } }`. Hashing binds the supplied body, but does not authenticate
222
+ the authority or prove its completeness. An empty `hostTransit.handoffs` array
223
+ provides no host-wide denial and is not a substitute for this scope.
224
+
225
+ The caller must retain the authenticated authority, complete transition and applied
226
+ receipt, verify `inspect().enforced`, and qualify the complete packet path. This
227
+ guard alone proves neither early-boot/late-shutdown ordering nor protection after
228
+ kernel reboot. It does not fence foreign packet queues, later packet rewrites,
229
+ offloads or proxies, and supplies no quarantine release, conntrack drainage or
230
+ allocation-reuse evidence. Historical leases remain the caller's durable ownership
231
+ responsibility. PERSIST retains this table only while its network namespace exists;
232
+ `release()` and ordinary `close()` delete it, so revoke any external protection
233
+ claim before release, or use the verified retention operation described above.
177
234
 
178
235
  Local bindings include name, index, kind, MAC (null for L3 TUN), interface-link
179
236
  index, and required IPv4 addresses. RTM_GETLINK/RTM_GETADDR verify those local
@@ -4,6 +4,8 @@ use crate::wire::{self, Attr};
4
4
 
5
5
  #[path = "egress.host.rs"]
6
6
  mod host;
7
+ #[path = "egress.poolguard.rs"]
8
+ mod poolguard;
7
9
  #[path = "egress.router.rs"]
8
10
  mod router;
9
11
 
@@ -283,6 +285,7 @@ pub(super) fn compile(prepared: &Prepared, table: &str) -> Result<Vec<(u16, Vec<
283
285
  router::compile(&mut program, scope, prepared.policy.revision)?
284
286
  }
285
287
  Scope::HostTransit(scope) => host::compile(&mut program, scope)?,
288
+ Scope::AllocationPoolGuard(scope) => poolguard::compile(&mut program, scope)?,
286
289
  }
287
290
  Ok(program.operations)
288
291
  }
@@ -0,0 +1,30 @@
1
+ use super::*;
2
+
3
+ pub(super) fn compile(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) -> Result<()> {
4
+ for (name, hook) in [("input", 1), ("forward", 2), ("output", 3)] {
5
+ program.chain(name, Some((hook, 0, "filter")))?;
6
+ }
7
+ for name in ["pool_guard", "pool_reply"] {
8
+ program.chain(name, None)?;
9
+ }
10
+ for name in ["input", "forward", "output"] {
11
+ program.jump(name, ipv4(), "pool_guard")?;
12
+ }
13
+ // An earlier owner's DNAT must not conceal the original protected target.
14
+ for prefix in &scope.prefixes {
15
+ program.end("pool_guard", original_address(prefix, false)?, 0)?;
16
+ }
17
+ // GOTO, not JUMP: RETURN from pool_reply must return to the base chain,
18
+ // skipping current-destination denial for legitimate reverse-SNAT replies.
19
+ let mut reply = ct(1, None, vec![1]);
20
+ reply.push(verdict(-4, Some("pool_reply")));
21
+ program.rule("pool_guard", reply)?;
22
+ // A missing conntrack tuple/direction breaks only its rule. Untracked or
23
+ // invalid packets still reach this unconditional current-destination guard.
24
+ for prefix in &scope.prefixes {
25
+ program.end("pool_guard", current_address(prefix, false)?, 0)?;
26
+ program.end("pool_reply", current_address(prefix, true)?, 0)?;
27
+ }
28
+ program.end("pool_guard", vec![], -5)?;
29
+ program.end("pool_reply", vec![], -5)
30
+ }
@@ -119,10 +119,17 @@ pub struct HostScope {
119
119
  pub snat_address: String,
120
120
  }
121
121
  #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
122
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
123
+ pub struct AllocationPoolGuardScope {
124
+ pub authority_digest: String,
125
+ pub prefixes: Vec<String>,
126
+ }
127
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
122
128
  #[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
123
129
  pub enum Scope {
124
130
  RouterEgress(RouterScope),
125
131
  HostTransit(HostScope),
132
+ AllocationPoolGuard(AllocationPoolGuardScope),
126
133
  }
127
134
  #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
128
135
  #[serde(rename_all = "camelCase", deny_unknown_fields)]
@@ -532,6 +539,23 @@ fn normalize_router(value: &mut RouterScope, revision: u64) -> Result<()> {
532
539
  )
533
540
  }))
534
541
  }
542
+ fn normalize_pool_guard(value: &mut AllocationPoolGuardScope) -> Result<()> {
543
+ require(
544
+ digest(&value.authority_digest) && !value.prefixes.is_empty() && value.prefixes.len() <= 64,
545
+ )?;
546
+ value.prefixes.sort();
547
+ for (index, item) in value.prefixes.iter().enumerate() {
548
+ require(
549
+ covers("10.0.0.0/8", item)?
550
+ || covers("172.16.0.0/12", item)?
551
+ || covers("192.168.0.0/16", item)?,
552
+ )?;
553
+ for previous in &value.prefixes[..index] {
554
+ require(!overlaps(item, previous)?)?;
555
+ }
556
+ }
557
+ Ok(())
558
+ }
535
559
  fn normalize_host(value: &mut HostScope) -> Result<()> {
536
560
  normalize_protection(&mut value.protection)?;
537
561
  normalize_link(&mut value.uplink)?;
@@ -642,6 +666,7 @@ impl Policy {
642
666
  match &mut self.scope {
643
667
  Scope::RouterEgress(scope) => normalize_router(scope, self.revision)?,
644
668
  Scope::HostTransit(scope) => normalize_host(scope)?,
669
+ Scope::AllocationPoolGuard(scope) => normalize_pool_guard(scope)?,
645
670
  }
646
671
  Ok(self)
647
672
  }
@@ -667,6 +692,8 @@ impl Prepared {
667
692
  .map(|item| &item.link)
668
693
  .chain(std::iter::once(&scope.uplink))
669
694
  .collect(),
695
+ // No link/route dependency: this policy must apply before allocation.
696
+ Scope::AllocationPoolGuard(_) => return Ok(()),
670
697
  };
671
698
  crate::wire::verify_bound_interfaces(links.into_iter().map(|link| {
672
699
  crate::wire::BoundInterface {
@@ -45,6 +45,11 @@ pub(crate) fn host() -> Value {
45
45
  "handoffs":[{"link":link(4,"handoff","veth","10.240.0.1"),"allocations":[allocation()]}],
46
46
  "uplink":link(2,"ens18","ethernet","192.0.2.2"),"snatAddress":"192.0.2.2"}})
47
47
  }
48
+ pub(crate) fn pool_guard() -> Value {
49
+ json!({"schemaVersion":2,"revision":1,"scope":{"kind":"allocationPoolGuard",
50
+ "authorityDigest":format!("sha256:{}", "b".repeat(64)),
51
+ "prefixes":["10.241.0.0/16", "10.240.0.0/16"]}})
52
+ }
48
53
  fn normalized(value: Value) -> super::Result<Policy> {
49
54
  serde_json::from_value::<Policy>(value)
50
55
  .map_err(|_| crate::Error::Invalid)?
@@ -330,3 +335,83 @@ fn managed_union_keeps_strict_duplicate_and_unknown_field_rejection() {
330
335
  )
331
336
  .is_err());
332
337
  }
338
+
339
+ #[test]
340
+ fn allocation_pool_guard_is_canonical_authority_bound_and_private_only() {
341
+ let prepared = normalized(pool_guard()).unwrap().prepare().unwrap();
342
+ let mut reordered = pool_guard();
343
+ reordered["scope"]["prefixes"]
344
+ .as_array_mut()
345
+ .unwrap()
346
+ .reverse();
347
+ assert_eq!(normalized(reordered).unwrap().prepare().unwrap(), prepared);
348
+ let mut changed_authority = pool_guard();
349
+ changed_authority["scope"]["authorityDigest"] = json!(format!("sha256:{}", "c".repeat(64)));
350
+ assert_ne!(
351
+ normalized(changed_authority)
352
+ .unwrap()
353
+ .prepare()
354
+ .unwrap()
355
+ .digest,
356
+ prepared.digest
357
+ );
358
+ for prefixes in [
359
+ json!([]),
360
+ json!(["10.0.0.1/8"]),
361
+ json!(["10.0.0.0/8", "10.1.0.0/16"]),
362
+ json!(["10.0.0.0/8", "10.0.0.0/8"]),
363
+ json!(["0.0.0.0/0"]),
364
+ json!(["192.0.2.0/24"]),
365
+ json!(["127.0.0.0/8"]),
366
+ json!(["172.0.0.0/8"]),
367
+ json!(["192.168.0.0/15"]),
368
+ json!(["10.0.0.0/08"]),
369
+ json!(["::/0"]),
370
+ ] {
371
+ assert!(normalized(changed(pool_guard(), "/scope/prefixes", prefixes)).is_err());
372
+ }
373
+ let mut extra = pool_guard();
374
+ extra["scope"]["handoffs"] = json!([]);
375
+ assert!(normalized(extra).is_err());
376
+ assert!(normalized(changed(
377
+ pool_guard(),
378
+ "/scope/authorityDigest",
379
+ json!("sha256:bad")
380
+ ))
381
+ .is_err());
382
+ assert!(normalized(changed(
383
+ pool_guard(),
384
+ "/scope/prefixes",
385
+ json!(["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"])
386
+ ))
387
+ .is_ok());
388
+ let duplicate = serde_json::to_string(&pool_guard())
389
+ .unwrap()
390
+ .replace("\"prefixes\":", "\"prefixes\":[],\"prefixes\":");
391
+ assert!(serde_json::from_str::<crate::managed::Policy>(&duplicate).is_err());
392
+ }
393
+
394
+ #[test]
395
+ fn allocation_pool_guard_maximum_fits_the_atomic_graph_without_link_bindings() {
396
+ let prefixes: Vec<_> = (0..64).map(|part| format!("10.{part}.0.0/16")).collect();
397
+ let prepared = normalized(changed(pool_guard(), "/scope/prefixes", json!(prefixes)))
398
+ .unwrap()
399
+ .prepare()
400
+ .unwrap();
401
+ prepared.validate().unwrap();
402
+ prepared.validate_interfaces().unwrap();
403
+ let program = prepared
404
+ .program(&format!("snft_{}", "x".repeat(59)))
405
+ .unwrap();
406
+ let bytes: usize = program
407
+ .iter()
408
+ .map(|(_, attrs)| crate::wire::encode_attrs(attrs).len() + 20)
409
+ .sum();
410
+ assert!(program.len() <= 768 && bytes <= 100_000);
411
+ println!(
412
+ "POOL_GUARD_MAX prefixes=64 operations={} bytes={bytes}",
413
+ program.len()
414
+ );
415
+ let excessive: Vec<_> = (0..65).map(|part| format!("10.{part}.0.0/16")).collect();
416
+ assert!(normalized(changed(pool_guard(), "/scope/prefixes", json!(excessive))).is_err());
417
+ }
package/rust/src/main.rs CHANGED
@@ -91,6 +91,10 @@ fn dispatch(owner: &mut Option<owner::Owner>, request: Request) -> Result<serde_
91
91
  .release(decode(request.params)?)?;
92
92
  Ok(serde_json::json!({"released":true}))
93
93
  }
94
+ "detachPolicy" => {
95
+ owner.as_mut().ok_or(Error::Conflict)?.detach(decode(request.params)?)?;
96
+ Ok(serde_json::json!({"detached":true}))
97
+ }
94
98
  "closeOwner" => {
95
99
  if decode::<serde_json::Value>(request.params)? != serde_json::json!({}) {
96
100
  return Err(Error::Invalid);
@@ -34,6 +34,10 @@ impl Policy {
34
34
  scope: egress::Scope::HostTransit(_),
35
35
  ..
36
36
  }) => "hostTransitV2",
37
+ Self::Egress(egress::Policy {
38
+ scope: egress::Scope::AllocationPoolGuard(_),
39
+ ..
40
+ }) => "allocationPoolGuardV2",
37
41
  }
38
42
  }
39
43
  pub fn prepare(self) -> Result<Prepared> {