Validate complete engine addresses and retain shared socket inputs (#503)

This commit is contained in:
Quixos Development Agent 01f26965-84fa-4a79-8069-725682c13065
2026-10-29 12:30:55 +00:00
parent 97ce77d3de
commit 4a87fc2c7d
4 changed files with 48 additions and 15 deletions
+11 -1
View File
@@ -41,6 +41,8 @@ let
''
mkdir -p "$out/lib" "$out/bin"
cp ${./service.mjs} "$out/lib/service.mjs"
cp ${../socket-address.mjs} "$out/lib/socket-address.mjs"
substituteInPlace "$out/lib/service.mjs" --replace-fail '../socket-address.mjs' './socket-address.mjs'
cp ${./worker.mjs} "$out/lib/worker.mjs"
makeWrapper ${pkgs.nodejs_24}/bin/node "$out/bin/quixos-restricted-registry" \
--add-flags "$out/lib/service.mjs" \
@@ -48,6 +50,13 @@ let
--set QUIXOS_RESTRICTED_SANDBOX ${sandbox}/bin/quixos-restricted-sandbox \
--set QUIXOS_RESTRICTED_WORKER "$out/lib/worker.mjs"
'';
qualificationSource = pkgs.lib.fileset.toSource {
root = ../.;
fileset = pkgs.lib.fileset.unions [
(pkgs.lib.fileset.fileFilter (file: file.hasExt "mjs") ./.)
../socket-address.mjs
];
};
qualification =
pkgs.runCommand "quixos-restricted-execution-qualification"
{
@@ -56,7 +65,7 @@ let
QUIXOS_TEST_SDK = "${sdk}/core.mjs";
}
''
node --test ${./.}/service.test.mjs
node --test ${qualificationSource}/restricted-execution/service.test.mjs
touch "$out"
'';
in
@@ -69,6 +78,7 @@ in
nativeProbeSandbox
sdk
runtime
qualificationSource
qualification
;
}
+25 -11
View File
@@ -12,17 +12,31 @@ assert builtins.match "[a-z][a-z0-9_-]{0,63}" authorityUser != null;
assert builtins.match "/run/[a-zA-Z0-9_/-]+" engineDirectory != null;
assert !(pkgs.lib.hasInfix ".." engineDirectory);
assert !(pkgs.lib.hasSuffix "/" engineDirectory);
assert builtins.stringLength engineDirectory < 80;
assert builtins.isInt firstUid && builtins.isInt identityCount;
assert firstUid >= 610000 && firstUid + identityCount <= 2147483647;
assert identityCount >= 1 && identityCount <= 64;
pkgs.runCommand "quixos-restricted-identity-launcher" { nativeBuildInputs = [ pkgs.stdenv.cc ]; } ''
mkdir -p "$out/bin"
$CC -O2 -Wall -Wextra -Werror ${./identity-launcher.c} \
-DSANDBOX_EXECUTABLE='"${sandbox}/bin/quixos-restricted-sandbox"' \
-DAUTHORITY_USER='"${authorityUser}"' \
-DENGINE_DIRECTORY='"${engineDirectory}"' \
-DENGINE_FIRST_UID=${toString firstUid} \
-DENGINE_IDENTITY_COUNT=${toString identityCount} \
-o "$out/bin/quixos-restricted-engine"
''
pkgs.runCommand "quixos-restricted-identity-launcher"
{
nativeBuildInputs = [
pkgs.stdenv.cc
pkgs.nodejs_24
];
QUIXOS_RESTRICTED_ENGINE_DIRECTORY = engineDirectory;
}
''
# Validate the complete generated filename with the shared UTF-8 policy,
# before compiling or installing a launcher for this configured /run root.
node --input-type=module <<'JS'
import { checkedSocketPath } from '${../socket-address.mjs}';
for (const filename of ["broker.sock", "engine.sock"])
checkedSocketPath(process.env.QUIXOS_RESTRICTED_ENGINE_DIRECTORY + "/qx-engine-XXXXXX/" + filename);
JS
mkdir -p "$out/bin"
$CC -O2 -Wall -Wextra -Werror ${./identity-launcher.c} \
-DSANDBOX_EXECUTABLE='"${sandbox}/bin/quixos-restricted-sandbox"' \
-DAUTHORITY_USER='"${authorityUser}"' \
-DENGINE_DIRECTORY='"${engineDirectory}"' \
-DENGINE_FIRST_UID=${toString firstUid} \
-DENGINE_IDENTITY_COUNT=${toString identityCount} \
-o "$out/bin/quixos-restricted-engine"
''
+10 -2
View File
@@ -6,6 +6,8 @@ import { constants } from "node:fs";
import { tmpdir } from "node:os";
import { join, dirname } from "node:path";
import { checkedSocketPath } from "../socket-address.mjs";
const abi = "quixos-transaction-ports-v1";
/** Reserved supervisor faults are not authored domain rejections. A graph
* transaction must roll back without recording a permanent rejected outcome
@@ -359,10 +361,16 @@ export async function serveRestrictedRegistry(config, io = process) {
() => destroyEngine(engine),
);
if (!call.open) return;
if (config.sandboxIdentity) {
// The privileged helper owns a fixed explicit /run root and requires
// this exact basename. Keep its cross-UID ancestry and identity rules.
for (const filename of ["broker.sock", "engine.sock"])
checkedSocketPath(tmpdir() + "/qx-engine-XXXXXX/" + filename);
}
engine.directory = await mkdtemp(join(tmpdir(), "qx-engine-"));
if (!call.open) return;
const broker = join(engine.directory, "broker.sock");
engine.socket = join(engine.directory, "engine.sock");
const broker = checkedSocketPath(join(engine.directory, "broker.sock"));
engine.socket = checkedSocketPath(join(engine.directory, "engine.sock"));
engine.server = createServer(async (request, response) => {
const call = engine.current;
const message = call?.message;