@push.rocks/smartnftables 1.3.1 → 1.4.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 +10 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +4 -4
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.managednftables.js +9 -3
- package/dist_ts/managed.types.d.ts +5 -1
- package/package.json +4 -4
- package/readme.md +23 -0
- package/rust/src/main.rs +20 -2
- package/rust/src/namespace.rs +51 -0
- package/rust/src/owner_namespace_tests.rs +242 -0
- package/rust/src/owner_tests.rs +3 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.managednftables.ts +7 -2
- package/ts/managed.types.ts +5 -1
package/changelog.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-13 - 1.4.0
|
|
4
|
+
|
|
5
|
+
### Maintenance
|
|
6
|
+
|
|
7
|
+
- Update release, build and test tooling with explicit development build-script policy.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Add Linux namespace descriptor entry before native policy ownership, with isolated persistence and recovery qualification.
|
|
12
|
+
|
|
3
13
|
## 2026-09-09 - 1.3.1
|
|
4
14
|
|
|
5
15
|
### Fixes
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "a97fc50e1f566505de1a412ee316f66996fd2f8450d07f6919c242dfd1746f0a",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartnftables",
|
|
6
|
-
"projectVersion": "1.
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "1.4.0",
|
|
7
|
+
"gitCommit": "4cd43db6efa140559ba87b90b4cdddf758239e42",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-13T12:15:55.817Z",
|
|
10
10
|
"tsrustVersion": "1.13.0",
|
|
11
11
|
"binary": "smartnftables",
|
|
12
12
|
"target": "linux_amd64_musl"
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "029d5e31ef6656bfb45588179dc75cd211a6d66254b7104da62c9fd9c687e61c",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartnftables",
|
|
6
|
-
"projectVersion": "1.
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "1.4.0",
|
|
7
|
+
"gitCommit": "4cd43db6efa140559ba87b90b4cdddf758239e42",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-13T12:15:56.577Z",
|
|
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
|
+
version: '1.4.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=
|
|
@@ -64,12 +64,16 @@ export class ManagedNftables {
|
|
|
64
64
|
#nativeMayOwn = false;
|
|
65
65
|
constructor(options) {
|
|
66
66
|
this.#options = snapshot(options);
|
|
67
|
-
if (Object.keys(this.#options).some((key) => !['ownerId', 'instanceId', 'tableName', 'binaryPath'].includes(key))
|
|
67
|
+
if (Object.keys(this.#options).some((key) => !['ownerId', 'instanceId', 'tableName', 'binaryPath', 'networkNamespaceFd'].includes(key))
|
|
68
68
|
|| ![this.#options.ownerId, this.#options.instanceId, this.#options.tableName].every((value) => typeof value === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(value))
|
|
69
69
|
|| this.#options.instanceId.length < 16 || !this.#options.tableName.startsWith('snft_')
|
|
70
70
|
|| this.#options.binaryPath !== undefined && (!plugins.path.isAbsolute(this.#options.binaryPath) || this.#options.binaryPath.includes('\0'))) {
|
|
71
71
|
throw new ManagedNftablesError('INVALID');
|
|
72
72
|
}
|
|
73
|
+
if (this.#options.networkNamespaceFd !== undefined && (process.platform !== 'linux'
|
|
74
|
+
|| !Number.isInteger(this.#options.networkNamespaceFd) || this.#options.networkNamespaceFd < 0
|
|
75
|
+
|| this.#options.networkNamespaceFd > 0x7fff_ffff))
|
|
76
|
+
throw new ManagedNftablesError('INVALID');
|
|
73
77
|
}
|
|
74
78
|
get state() { return this.#state; }
|
|
75
79
|
/** Starts only the compiler/IPC owner. The returned idle status is not enforcement. */
|
|
@@ -84,7 +88,9 @@ export class ManagedNftables {
|
|
|
84
88
|
this.#bridge = new plugins.smartrust.RustBridge({
|
|
85
89
|
binaryName: 'smartnftables', binaryPath: this.#options.binaryPath
|
|
86
90
|
?? plugins.path.join(root, 'dist_rust', `smartnftables_linux_${process.arch === 'x64' ? 'amd64' : 'arm64'}_musl`),
|
|
87
|
-
localPaths: [], searchSystemPath: false,
|
|
91
|
+
localPaths: [], searchSystemPath: false,
|
|
92
|
+
cliArgs: ['--management', ...(this.#options.networkNamespaceFd === undefined ? [] : ['--network-namespace-fd', '3'])],
|
|
93
|
+
inheritedFileDescriptors: this.#options.networkNamespaceFd === undefined ? undefined : [this.#options.networkNamespaceFd],
|
|
88
94
|
readyTimeoutMs: 3000, requestTimeoutMs: 30_000, maxPayloadSize: 262_144,
|
|
89
95
|
maxPendingRequestsByMethod: { preparePolicy: 1, openOwner: 1, reconcilePolicy: 1, inspectPolicy: 1, releasePolicy: 1, closeOwner: 1 },
|
|
90
96
|
});
|
|
@@ -173,4 +179,4 @@ export class ManagedNftables {
|
|
|
173
179
|
return owned;
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QyxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsS0FBSztJQUM3QyxZQUE0QixJQUFZO1FBQUksS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQW5ELFNBQUksR0FBSixJQUFJLENBQVE7SUFBd0MsQ0FBQztDQUNsRjtBQUVELG9GQUFvRjtBQUNwRixTQUFTLFFBQVEsQ0FBSSxLQUFRO0lBQzNCLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztJQUNkLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNwQixNQUFNLE1BQU0sR0FBRyxDQUFDLEtBQWEsRUFBVSxFQUFFO1FBQ3ZDLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxPQUFPLElBQUksQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLE9BQU87WUFBRSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDN0gsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDLENBQUM7SUFDRixNQUFNLElBQUksR0FBRyxDQUFDLEtBQWMsRUFBRSxLQUFhLEVBQVcsRUFBRTtRQUN0RCxJQUFJLEVBQUUsS0FBSyxHQUFHLE1BQU0sSUFBSSxLQUFLLEdBQUcsRUFBRTtZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM5RSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVE7WUFBRSxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwRCxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssU0FBUztZQUFFLE9BQU8sS0FBSyxD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
|
|
182
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QyxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsS0FBSztJQUM3QyxZQUE0QixJQUFZO1FBQUksS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQW5ELFNBQUksR0FBSixJQUFJLENBQVE7SUFBd0MsQ0FBQztDQUNsRjtBQUVELG9GQUFvRjtBQUNwRixTQUFTLFFBQVEsQ0FBSSxLQUFRO0lBQzNCLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztJQUNkLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNwQixNQUFNLE1BQU0sR0FBRyxDQUFDLEtBQWEsRUFBVSxFQUFFO1FBQ3ZDLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxPQUFPLElBQUksQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLE9BQU87WUFBRSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDN0gsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDLENBQUM7SUFDRixNQUFNLElBQUksR0FBRyxDQUFDLEtBQWMsRUFBRSxLQUFhLEVBQVcsRUFBRTtRQUN0RCxJQUFJLEVBQUUsS0FBSyxHQUFHLE1BQU0sSUFBSSxLQUFLLEdBQUcsRUFBRTtZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM5RSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVE7WUFBRSxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwRCxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssU0FBUztZQUFFLE9BQU8sS0FBSyxD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
|
|
@@ -66,6 +66,10 @@ export interface IManagedNftOptions {
|
|
|
66
66
|
tableName: string;
|
|
67
67
|
/** Explicit executable selection for qualified distributions and isolated tests. */
|
|
68
68
|
binaryPath?: string;
|
|
69
|
+
/** Linux network namespace descriptor, owned by the caller and valid through start().
|
|
70
|
+
* The native process enters before readiness or netlink activity. Keep a namespace
|
|
71
|
+
* owner alive for policy lifetime and recovery; this process option is not durable authority. */
|
|
72
|
+
networkNamespaceFd?: number;
|
|
69
73
|
}
|
|
70
74
|
export type TManagedNftCommands = {
|
|
71
75
|
preparePolicy: {
|
|
@@ -73,7 +77,7 @@ export type TManagedNftCommands = {
|
|
|
73
77
|
result: IPreparedManagedNftPolicy;
|
|
74
78
|
};
|
|
75
79
|
openOwner: {
|
|
76
|
-
params:
|
|
80
|
+
params: Pick<IManagedNftOptions, 'ownerId' | 'instanceId' | 'tableName'>;
|
|
77
81
|
result: IManagedNftStatus;
|
|
78
82
|
};
|
|
79
83
|
reconcilePolicy: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartnftables",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"author": "Lossless GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@git.zone/cli": "6.
|
|
13
|
-
"@git.zone/tsbuild": "^4.4.
|
|
12
|
+
"@git.zone/cli": "6.13.2",
|
|
13
|
+
"@git.zone/tsbuild": "^4.4.3",
|
|
14
14
|
"@git.zone/tsrun": "^2.0.6",
|
|
15
15
|
"@git.zone/tsrust": "1.13.0",
|
|
16
|
-
"@git.zone/tstest": "
|
|
16
|
+
"@git.zone/tstest": "6.0.0",
|
|
17
17
|
"@types/node": "26.4.1",
|
|
18
18
|
"typescript": "^7.0.2"
|
|
19
19
|
},
|
package/readme.md
CHANGED
|
@@ -26,6 +26,29 @@ namespace, and working nftables OWNER/PERSIST support. The published native matr
|
|
|
26
26
|
is Linux x86_64 and arm64, statically linked with musl. Actual kernel support is
|
|
27
27
|
checked when applying policy; starting the compiler does not establish enforcement.
|
|
28
28
|
|
|
29
|
+
An optional caller-owned `networkNamespaceFd` selects an already-created Linux
|
|
30
|
+
network namespace. Keep that descriptor valid through `start()`. The native
|
|
31
|
+
process validates and enters its inherited copy before readiness, owner identity
|
|
32
|
+
capture or netlink activity, then closes the copy. Invalid descriptors and denied
|
|
33
|
+
entry fail before readiness. The option is process configuration and never enters
|
|
34
|
+
`openOwner`, policy commands or durable receipts. Native callers use
|
|
35
|
+
`--management --network-namespace-fd 3` with an inherited descriptor at FD 3.
|
|
36
|
+
|
|
37
|
+
The caller owns the namespace lifetime, links and routes. Retain a namespace
|
|
38
|
+
owner across process loss and recovery: PERSIST retains policy only while its
|
|
39
|
+
namespace exists. An entered process keeps its namespace membership if the source
|
|
40
|
+
descriptor is closed, but a replacement process requires a valid descriptor.
|
|
41
|
+
Receipts bind the actual target namespace device and inode; recovery in another
|
|
42
|
+
namespace rejects. Await complete policy and process cleanup before releasing
|
|
43
|
+
the caller's final namespace owner. Namespace entry does not create uplink
|
|
44
|
+
authority, drain conntrack flows or authorize address and port reuse.
|
|
45
|
+
|
|
46
|
+
The Linux x86_64 musl candidate passed nine isolated Linux 6.18.35 kernel tests,
|
|
47
|
+
including inherited namespace entry, retained policy after process loss, recovery
|
|
48
|
+
in the original namespace, rejection in another namespace, and denied entry
|
|
49
|
+
before readiness. ARM binaries are built; privileged ARM and complete Pallet
|
|
50
|
+
workload integration remain separate qualification requirements.
|
|
51
|
+
|
|
29
52
|
The caller owns workload links and durable state. Keep links disabled until their
|
|
30
53
|
policy is confirmed, serialize link changes with policy operations, and disable
|
|
31
54
|
them before cleanup. Persist the full prepared transition before calling
|
package/rust/src/main.rs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
mod namespace;
|
|
1
2
|
mod owner;
|
|
2
3
|
mod policy;
|
|
3
4
|
#[cfg(test)]
|
|
@@ -97,9 +98,26 @@ fn dispatch(owner: &mut Option<owner::Owner>, request: Request) -> Result<serde_
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
fn main() {
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
let arguments = std::env::args().skip(1).collect::<Vec<_>>();
|
|
102
|
+
let descriptor = match arguments.as_slice() {
|
|
103
|
+
[management] if management == "--management" => None,
|
|
104
|
+
[management, option, fd]
|
|
105
|
+
if management == "--management" && option == "--network-namespace-fd" =>
|
|
106
|
+
{
|
|
107
|
+
match fd.parse::<i32>() {
|
|
108
|
+
Ok(value) => Some(value),
|
|
109
|
+
Err(_) => std::process::exit(2),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
_ => std::process::exit(2),
|
|
113
|
+
};
|
|
114
|
+
if let Some(fd) = descriptor {
|
|
115
|
+
if let Err(error) = namespace::enter(fd) {
|
|
116
|
+
eprintln!("Network namespace startup failed: {error}");
|
|
117
|
+
std::process::exit(1);
|
|
118
|
+
}
|
|
102
119
|
}
|
|
120
|
+
// The single native owner must enter before readiness, context capture or sockets.
|
|
103
121
|
let mut output = std::io::stdout().lock();
|
|
104
122
|
writeln!(output, "{{\"event\":\"ready\",\"data\":{{}}}}").unwrap();
|
|
105
123
|
output.flush().unwrap();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//! Startup-only entry. No thread, runtime, socket or ready event may precede it.
|
|
2
|
+
use std::io;
|
|
3
|
+
|
|
4
|
+
#[cfg(target_os = "linux")]
|
|
5
|
+
pub fn enter(fd: i32) -> io::Result<()> {
|
|
6
|
+
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
|
|
7
|
+
|
|
8
|
+
// Only an explicitly inherited extra descriptor is accepted. Never consume IPC.
|
|
9
|
+
if fd < 3 {
|
|
10
|
+
return Err(io::Error::new(
|
|
11
|
+
io::ErrorKind::InvalidInput,
|
|
12
|
+
"network namespace descriptor must be at least 3",
|
|
13
|
+
));
|
|
14
|
+
}
|
|
15
|
+
if unsafe { libc::fcntl(fd, libc::F_GETFD) } < 0 {
|
|
16
|
+
return Err(io::Error::new(
|
|
17
|
+
io::ErrorKind::InvalidInput,
|
|
18
|
+
"network namespace descriptor is not open",
|
|
19
|
+
));
|
|
20
|
+
}
|
|
21
|
+
// Startup is single-threaded; this is the child's owned copy, not the parent's.
|
|
22
|
+
let descriptor = unsafe { OwnedFd::from_raw_fd(fd) };
|
|
23
|
+
let namespace_type = unsafe { libc::ioctl(descriptor.as_raw_fd(), libc::NS_GET_NSTYPE) };
|
|
24
|
+
if namespace_type != libc::CLONE_NEWNET {
|
|
25
|
+
return Err(io::Error::new(
|
|
26
|
+
io::ErrorKind::InvalidInput,
|
|
27
|
+
"descriptor is not a Linux network namespace",
|
|
28
|
+
));
|
|
29
|
+
}
|
|
30
|
+
if unsafe { libc::setns(descriptor.as_raw_fd(), libc::CLONE_NEWNET) } != 0 {
|
|
31
|
+
return Err(io::Error::new(
|
|
32
|
+
io::ErrorKind::PermissionDenied,
|
|
33
|
+
format!(
|
|
34
|
+
"network namespace entry failed: {}",
|
|
35
|
+
io::Error::last_os_error()
|
|
36
|
+
),
|
|
37
|
+
));
|
|
38
|
+
}
|
|
39
|
+
// Close the inherited capability before creating threads. Namespace membership
|
|
40
|
+
// itself keeps the namespace alive until the complete process has exited.
|
|
41
|
+
drop(descriptor);
|
|
42
|
+
Ok(())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[cfg(not(target_os = "linux"))]
|
|
46
|
+
pub fn enter(_fd: i32) -> io::Result<()> {
|
|
47
|
+
Err(io::Error::new(
|
|
48
|
+
io::ErrorKind::Unsupported,
|
|
49
|
+
"network namespace entry requires Linux",
|
|
50
|
+
))
|
|
51
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
use super::{empty, in_namespace, ip, isolated, options};
|
|
2
|
+
use crate::owner::{Applied, Owner, Transition};
|
|
3
|
+
use serde_json::{json, Value};
|
|
4
|
+
use std::fs::{self, File};
|
|
5
|
+
use std::io::{BufRead, BufReader, Read, Write};
|
|
6
|
+
use std::os::fd::AsRawFd;
|
|
7
|
+
use std::os::unix::fs::MetadataExt;
|
|
8
|
+
use std::os::unix::process::CommandExt;
|
|
9
|
+
use std::process::{Child, Command, Stdio};
|
|
10
|
+
use std::sync::mpsc::{self, Receiver};
|
|
11
|
+
use std::thread::{self, JoinHandle};
|
|
12
|
+
use std::time::{Duration, Instant};
|
|
13
|
+
|
|
14
|
+
fn command(namespace: &File) -> Command {
|
|
15
|
+
let fd = namespace.as_raw_fd();
|
|
16
|
+
let mut command = Command::new("/bin/managed");
|
|
17
|
+
command
|
|
18
|
+
.args(["--management", "--network-namespace-fd", "3"])
|
|
19
|
+
.stdin(Stdio::piped())
|
|
20
|
+
.stdout(Stdio::piped())
|
|
21
|
+
.stderr(Stdio::inherit());
|
|
22
|
+
unsafe {
|
|
23
|
+
command.pre_exec(move || {
|
|
24
|
+
if libc::dup2(fd, 3) < 0 || libc::fcntl(3, libc::F_SETFD, 0) < 0 {
|
|
25
|
+
return Err(std::io::Error::last_os_error());
|
|
26
|
+
}
|
|
27
|
+
Ok(())
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
command
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
struct NativeOwner {
|
|
34
|
+
child: Child,
|
|
35
|
+
messages: Receiver<Value>,
|
|
36
|
+
reader: Option<JoinHandle<()>>,
|
|
37
|
+
}
|
|
38
|
+
impl NativeOwner {
|
|
39
|
+
fn start(namespace: &File) -> Self {
|
|
40
|
+
let mut child = command(namespace).spawn().unwrap();
|
|
41
|
+
let output = child.stdout.take().unwrap();
|
|
42
|
+
let (send, messages) = mpsc::channel();
|
|
43
|
+
let reader = thread::spawn(move || {
|
|
44
|
+
for line in BufReader::new(output.take(1024 * 1024)).lines() {
|
|
45
|
+
let Ok(line) = line else { break };
|
|
46
|
+
if line.len() > 262_144 {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
let Ok(value) = serde_json::from_str(&line) else {
|
|
50
|
+
break;
|
|
51
|
+
};
|
|
52
|
+
if send.send(value).is_err() {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
let owner = Self {
|
|
58
|
+
child,
|
|
59
|
+
messages,
|
|
60
|
+
reader: Some(reader),
|
|
61
|
+
};
|
|
62
|
+
assert_eq!(
|
|
63
|
+
owner.messages.recv_timeout(Duration::from_secs(5)).unwrap()["event"],
|
|
64
|
+
"ready"
|
|
65
|
+
);
|
|
66
|
+
owner
|
|
67
|
+
}
|
|
68
|
+
fn response(&mut self, method: &str, params: Value) -> Value {
|
|
69
|
+
writeln!(
|
|
70
|
+
self.child.stdin.as_mut().unwrap(),
|
|
71
|
+
"{}",
|
|
72
|
+
json!({"id":method,"method":method,"params":params})
|
|
73
|
+
)
|
|
74
|
+
.unwrap();
|
|
75
|
+
let response = self.messages.recv_timeout(Duration::from_secs(5)).unwrap();
|
|
76
|
+
assert_eq!(response["id"], method);
|
|
77
|
+
response
|
|
78
|
+
}
|
|
79
|
+
fn call(&mut self, method: &str, params: Value) -> Value {
|
|
80
|
+
let response = self.response(method, params);
|
|
81
|
+
assert_eq!(response["success"], true, "{response}");
|
|
82
|
+
response["result"].clone()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
impl Drop for NativeOwner {
|
|
86
|
+
fn drop(&mut self) {
|
|
87
|
+
let _ = self.child.kill();
|
|
88
|
+
let _ = self.child.wait();
|
|
89
|
+
if let Some(reader) = self.reader.take() {
|
|
90
|
+
let _ = reader.join();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[test]
|
|
96
|
+
#[ignore = "requires the explicitly isolated QEMU qualification guest"]
|
|
97
|
+
fn inherited_namespace_binds_policy_and_persistent_recovery() {
|
|
98
|
+
isolated();
|
|
99
|
+
ip(&["netns", "add", "fdtarget"]);
|
|
100
|
+
let namespace = File::open("/run/netns/fdtarget").unwrap();
|
|
101
|
+
let metadata = namespace.metadata().unwrap();
|
|
102
|
+
let initial = fs::metadata("/proc/thread-self/ns/net").unwrap();
|
|
103
|
+
assert_ne!(metadata.ino(), initial.ino());
|
|
104
|
+
let mut child = NativeOwner::start(&namespace);
|
|
105
|
+
let pid = child.child.id();
|
|
106
|
+
let status = child.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
107
|
+
assert_eq!(
|
|
108
|
+
status["identity"]["namespaceDevice"],
|
|
109
|
+
metadata.dev().to_string()
|
|
110
|
+
);
|
|
111
|
+
assert_eq!(
|
|
112
|
+
status["identity"]["namespaceInode"],
|
|
113
|
+
metadata.ino().to_string()
|
|
114
|
+
);
|
|
115
|
+
assert_eq!(
|
|
116
|
+
fs::read_dir(format!("/proc/{pid}/task")).unwrap().count(),
|
|
117
|
+
1
|
|
118
|
+
);
|
|
119
|
+
let target_link = fs::read_link(format!("/proc/{pid}/ns/net")).unwrap();
|
|
120
|
+
for fd in fs::read_dir(format!("/proc/{pid}/fd")).unwrap() {
|
|
121
|
+
if let Ok(target) = fs::read_link(fd.unwrap().path()) {
|
|
122
|
+
assert_ne!(
|
|
123
|
+
target, target_link,
|
|
124
|
+
"child namespace descriptor must be closed"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
let first = child.call(
|
|
129
|
+
"reconcilePolicy",
|
|
130
|
+
serde_json::to_value(Transition {
|
|
131
|
+
previous: None,
|
|
132
|
+
target: empty(1),
|
|
133
|
+
})
|
|
134
|
+
.unwrap(),
|
|
135
|
+
);
|
|
136
|
+
assert!(Owner::new(options("nsfd"))
|
|
137
|
+
.unwrap()
|
|
138
|
+
.graph()
|
|
139
|
+
.unwrap()
|
|
140
|
+
.is_none());
|
|
141
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
142
|
+
.unwrap()
|
|
143
|
+
.graph()
|
|
144
|
+
.unwrap()
|
|
145
|
+
.is_some()));
|
|
146
|
+
assert_eq!(
|
|
147
|
+
unsafe { libc::ioctl(namespace.as_raw_fd(), libc::NS_GET_NSTYPE) },
|
|
148
|
+
libc::CLONE_NEWNET
|
|
149
|
+
);
|
|
150
|
+
drop(child); // PERSIST must retain the target graph after native process loss.
|
|
151
|
+
assert!(!std::path::Path::new(&format!("/proc/{pid}")).exists());
|
|
152
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
153
|
+
.unwrap()
|
|
154
|
+
.graph()
|
|
155
|
+
.unwrap()
|
|
156
|
+
.is_some()));
|
|
157
|
+
|
|
158
|
+
let previous: Applied = serde_json::from_value(first).unwrap();
|
|
159
|
+
let transition = serde_json::to_value(Transition {
|
|
160
|
+
previous: Some(previous),
|
|
161
|
+
target: empty(2),
|
|
162
|
+
})
|
|
163
|
+
.unwrap();
|
|
164
|
+
let wrong_namespace = File::open("/proc/thread-self/ns/net").unwrap();
|
|
165
|
+
let mut wrong = NativeOwner::start(&wrong_namespace);
|
|
166
|
+
wrong.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
167
|
+
let denied = wrong.response("reconcilePolicy", transition.clone());
|
|
168
|
+
assert_eq!(denied["success"], false);
|
|
169
|
+
assert_eq!(denied["errorCode"], "CONFLICT");
|
|
170
|
+
assert!(Owner::new(options("nsfd"))
|
|
171
|
+
.unwrap()
|
|
172
|
+
.graph()
|
|
173
|
+
.unwrap()
|
|
174
|
+
.is_none());
|
|
175
|
+
drop(wrong);
|
|
176
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
177
|
+
.unwrap()
|
|
178
|
+
.graph()
|
|
179
|
+
.unwrap()
|
|
180
|
+
.is_some()));
|
|
181
|
+
|
|
182
|
+
let mut recovered = NativeOwner::start(&namespace);
|
|
183
|
+
recovered.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
184
|
+
drop(namespace); // The entered child keeps membership; caller capability is not consumed by it.
|
|
185
|
+
let applied = recovered.call("reconcilePolicy", transition);
|
|
186
|
+
assert_eq!(applied["receipt"]["revision"], 2);
|
|
187
|
+
assert_eq!(
|
|
188
|
+
applied["receipt"]["identity"]["namespaceInode"],
|
|
189
|
+
metadata.ino().to_string()
|
|
190
|
+
);
|
|
191
|
+
assert_eq!(recovered.call("inspectPolicy", json!({}))["enforced"], true);
|
|
192
|
+
assert_eq!(recovered.call("closeOwner", json!({}))["released"], true);
|
|
193
|
+
drop(recovered);
|
|
194
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
195
|
+
.unwrap()
|
|
196
|
+
.graph()
|
|
197
|
+
.unwrap()
|
|
198
|
+
.is_none()));
|
|
199
|
+
assert!(Owner::new(options("nsfd"))
|
|
200
|
+
.unwrap()
|
|
201
|
+
.graph()
|
|
202
|
+
.unwrap()
|
|
203
|
+
.is_none());
|
|
204
|
+
assert_eq!(
|
|
205
|
+
fs::metadata("/proc/thread-self/ns/net").unwrap().ino(),
|
|
206
|
+
initial.ino()
|
|
207
|
+
);
|
|
208
|
+
ip(&["netns", "delete", "fdtarget"]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#[test]
|
|
212
|
+
#[ignore = "requires the explicitly isolated QEMU qualification guest"]
|
|
213
|
+
fn valid_descriptor_without_entry_permission_never_becomes_ready() {
|
|
214
|
+
isolated();
|
|
215
|
+
let namespace = File::open("/proc/thread-self/ns/net").unwrap();
|
|
216
|
+
let mut child = command(&namespace)
|
|
217
|
+
.uid(65534)
|
|
218
|
+
.gid(65534)
|
|
219
|
+
.stdin(Stdio::null())
|
|
220
|
+
.stderr(Stdio::piped())
|
|
221
|
+
.spawn()
|
|
222
|
+
.unwrap();
|
|
223
|
+
let deadline = Instant::now() + Duration::from_secs(5);
|
|
224
|
+
while child.try_wait().unwrap().is_none() {
|
|
225
|
+
if Instant::now() >= deadline {
|
|
226
|
+
let _ = child.kill();
|
|
227
|
+
let _ = child.wait();
|
|
228
|
+
panic!("denied namespace entry did not exit");
|
|
229
|
+
}
|
|
230
|
+
thread::sleep(Duration::from_millis(10));
|
|
231
|
+
}
|
|
232
|
+
let output = child.wait_with_output().unwrap();
|
|
233
|
+
assert_eq!(output.status.code(), Some(1));
|
|
234
|
+
assert!(output.stdout.is_empty());
|
|
235
|
+
assert!(String::from_utf8(output.stderr)
|
|
236
|
+
.unwrap()
|
|
237
|
+
.contains("network namespace entry failed"));
|
|
238
|
+
assert_eq!(
|
|
239
|
+
unsafe { libc::ioctl(namespace.as_raw_fd(), libc::NS_GET_NSTYPE) },
|
|
240
|
+
libc::CLONE_NEWNET
|
|
241
|
+
);
|
|
242
|
+
}
|
package/rust/src/owner_tests.rs
CHANGED
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -53,12 +53,15 @@ export class ManagedNftables {
|
|
|
53
53
|
|
|
54
54
|
constructor(options: IManagedNftOptions) {
|
|
55
55
|
this.#options = snapshot(options);
|
|
56
|
-
if (Object.keys(this.#options).some((key) => !['ownerId', 'instanceId', 'tableName', 'binaryPath'].includes(key))
|
|
56
|
+
if (Object.keys(this.#options).some((key) => !['ownerId', 'instanceId', 'tableName', 'binaryPath', 'networkNamespaceFd'].includes(key))
|
|
57
57
|
|| ![this.#options.ownerId, this.#options.instanceId, this.#options.tableName].every((value) => typeof value === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(value))
|
|
58
58
|
|| this.#options.instanceId.length < 16 || !this.#options.tableName.startsWith('snft_')
|
|
59
59
|
|| this.#options.binaryPath !== undefined && (!plugins.path.isAbsolute(this.#options.binaryPath) || this.#options.binaryPath.includes('\0'))) {
|
|
60
60
|
throw new ManagedNftablesError('INVALID');
|
|
61
61
|
}
|
|
62
|
+
if (this.#options.networkNamespaceFd !== undefined && (process.platform !== 'linux'
|
|
63
|
+
|| !Number.isInteger(this.#options.networkNamespaceFd) || this.#options.networkNamespaceFd < 0
|
|
64
|
+
|| this.#options.networkNamespaceFd > 0x7fff_ffff)) throw new ManagedNftablesError('INVALID');
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
public get state(): string { return this.#state; }
|
|
@@ -73,7 +76,9 @@ export class ManagedNftables {
|
|
|
73
76
|
this.#bridge = new plugins.smartrust.RustBridge<TManagedNftCommands>({
|
|
74
77
|
binaryName: 'smartnftables', binaryPath: this.#options.binaryPath
|
|
75
78
|
?? plugins.path.join(root, 'dist_rust', `smartnftables_linux_${process.arch === 'x64' ? 'amd64' : 'arm64'}_musl`),
|
|
76
|
-
localPaths: [], searchSystemPath: false,
|
|
79
|
+
localPaths: [], searchSystemPath: false,
|
|
80
|
+
cliArgs: ['--management', ...(this.#options.networkNamespaceFd === undefined ? [] : ['--network-namespace-fd', '3'])],
|
|
81
|
+
inheritedFileDescriptors: this.#options.networkNamespaceFd === undefined ? undefined : [this.#options.networkNamespaceFd],
|
|
77
82
|
readyTimeoutMs: 3000, requestTimeoutMs: 30_000, maxPayloadSize: 262_144,
|
|
78
83
|
maxPendingRequestsByMethod: { preparePolicy: 1, openOwner: 1, reconcilePolicy: 1, inspectPolicy: 1, releasePolicy: 1, closeOwner: 1 },
|
|
79
84
|
});
|
package/ts/managed.types.ts
CHANGED
|
@@ -75,11 +75,15 @@ export interface IManagedNftOptions {
|
|
|
75
75
|
tableName: string;
|
|
76
76
|
/** Explicit executable selection for qualified distributions and isolated tests. */
|
|
77
77
|
binaryPath?: string;
|
|
78
|
+
/** Linux network namespace descriptor, owned by the caller and valid through start().
|
|
79
|
+
* The native process enters before readiness or netlink activity. Keep a namespace
|
|
80
|
+
* owner alive for policy lifetime and recovery; this process option is not durable authority. */
|
|
81
|
+
networkNamespaceFd?: number;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
export type TManagedNftCommands = {
|
|
81
85
|
preparePolicy: { params: IManagedNftPolicy; result: IPreparedManagedNftPolicy };
|
|
82
|
-
openOwner: { params:
|
|
86
|
+
openOwner: { params: Pick<IManagedNftOptions, 'ownerId' | 'instanceId' | 'tableName'>; result: IManagedNftStatus };
|
|
83
87
|
reconcilePolicy: { params: IManagedNftTransition; result: IAppliedManagedNftPolicy };
|
|
84
88
|
inspectPolicy: { params: Record<string, never>; result: IManagedNftStatus };
|
|
85
89
|
releasePolicy: { params: IAppliedManagedNftPolicy; result: { released: true } };
|