Validate complete engine addresses and retain shared socket inputs (#503)
This commit is contained in:
parent
97ce77d3de
commit
4a87fc2c7d
@@ -41,6 +41,8 @@ let
|
|||||||
''
|
''
|
||||||
mkdir -p "$out/lib" "$out/bin"
|
mkdir -p "$out/lib" "$out/bin"
|
||||||
cp ${./service.mjs} "$out/lib/service.mjs"
|
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"
|
cp ${./worker.mjs} "$out/lib/worker.mjs"
|
||||||
makeWrapper ${pkgs.nodejs_24}/bin/node "$out/bin/quixos-restricted-registry" \
|
makeWrapper ${pkgs.nodejs_24}/bin/node "$out/bin/quixos-restricted-registry" \
|
||||||
--add-flags "$out/lib/service.mjs" \
|
--add-flags "$out/lib/service.mjs" \
|
||||||
@@ -48,6 +50,13 @@ let
|
|||||||
--set QUIXOS_RESTRICTED_SANDBOX ${sandbox}/bin/quixos-restricted-sandbox \
|
--set QUIXOS_RESTRICTED_SANDBOX ${sandbox}/bin/quixos-restricted-sandbox \
|
||||||
--set QUIXOS_RESTRICTED_WORKER "$out/lib/worker.mjs"
|
--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 =
|
qualification =
|
||||||
pkgs.runCommand "quixos-restricted-execution-qualification"
|
pkgs.runCommand "quixos-restricted-execution-qualification"
|
||||||
{
|
{
|
||||||
@@ -56,7 +65,7 @@ let
|
|||||||
QUIXOS_TEST_SDK = "${sdk}/core.mjs";
|
QUIXOS_TEST_SDK = "${sdk}/core.mjs";
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
node --test ${./.}/service.test.mjs
|
node --test ${qualificationSource}/restricted-execution/service.test.mjs
|
||||||
touch "$out"
|
touch "$out"
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
@@ -69,6 +78,7 @@ in
|
|||||||
nativeProbeSandbox
|
nativeProbeSandbox
|
||||||
sdk
|
sdk
|
||||||
runtime
|
runtime
|
||||||
|
qualificationSource
|
||||||
qualification
|
qualification
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,25 @@ assert builtins.match "[a-z][a-z0-9_-]{0,63}" authorityUser != null;
|
|||||||
assert builtins.match "/run/[a-zA-Z0-9_/-]+" engineDirectory != null;
|
assert builtins.match "/run/[a-zA-Z0-9_/-]+" engineDirectory != null;
|
||||||
assert !(pkgs.lib.hasInfix ".." engineDirectory);
|
assert !(pkgs.lib.hasInfix ".." engineDirectory);
|
||||||
assert !(pkgs.lib.hasSuffix "/" engineDirectory);
|
assert !(pkgs.lib.hasSuffix "/" engineDirectory);
|
||||||
assert builtins.stringLength engineDirectory < 80;
|
|
||||||
assert builtins.isInt firstUid && builtins.isInt identityCount;
|
assert builtins.isInt firstUid && builtins.isInt identityCount;
|
||||||
assert firstUid >= 610000 && firstUid + identityCount <= 2147483647;
|
assert firstUid >= 610000 && firstUid + identityCount <= 2147483647;
|
||||||
assert identityCount >= 1 && identityCount <= 64;
|
assert identityCount >= 1 && identityCount <= 64;
|
||||||
pkgs.runCommand "quixos-restricted-identity-launcher" { nativeBuildInputs = [ pkgs.stdenv.cc ]; } ''
|
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"
|
mkdir -p "$out/bin"
|
||||||
$CC -O2 -Wall -Wextra -Werror ${./identity-launcher.c} \
|
$CC -O2 -Wall -Wextra -Werror ${./identity-launcher.c} \
|
||||||
-DSANDBOX_EXECUTABLE='"${sandbox}/bin/quixos-restricted-sandbox"' \
|
-DSANDBOX_EXECUTABLE='"${sandbox}/bin/quixos-restricted-sandbox"' \
|
||||||
@@ -25,4 +39,4 @@ pkgs.runCommand "quixos-restricted-identity-launcher" { nativeBuildInputs = [ pk
|
|||||||
-DENGINE_FIRST_UID=${toString firstUid} \
|
-DENGINE_FIRST_UID=${toString firstUid} \
|
||||||
-DENGINE_IDENTITY_COUNT=${toString identityCount} \
|
-DENGINE_IDENTITY_COUNT=${toString identityCount} \
|
||||||
-o "$out/bin/quixos-restricted-engine"
|
-o "$out/bin/quixos-restricted-engine"
|
||||||
''
|
''
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { constants } from "node:fs";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join, dirname } from "node:path";
|
import { join, dirname } from "node:path";
|
||||||
|
|
||||||
|
import { checkedSocketPath } from "../socket-address.mjs";
|
||||||
|
|
||||||
const abi = "quixos-transaction-ports-v1";
|
const abi = "quixos-transaction-ports-v1";
|
||||||
/** Reserved supervisor faults are not authored domain rejections. A graph
|
/** Reserved supervisor faults are not authored domain rejections. A graph
|
||||||
* transaction must roll back without recording a permanent rejected outcome
|
* transaction must roll back without recording a permanent rejected outcome
|
||||||
@@ -359,10 +361,16 @@ export async function serveRestrictedRegistry(config, io = process) {
|
|||||||
() => destroyEngine(engine),
|
() => destroyEngine(engine),
|
||||||
);
|
);
|
||||||
if (!call.open) return;
|
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-"));
|
engine.directory = await mkdtemp(join(tmpdir(), "qx-engine-"));
|
||||||
if (!call.open) return;
|
if (!call.open) return;
|
||||||
const broker = join(engine.directory, "broker.sock");
|
const broker = checkedSocketPath(join(engine.directory, "broker.sock"));
|
||||||
engine.socket = join(engine.directory, "engine.sock");
|
engine.socket = checkedSocketPath(join(engine.directory, "engine.sock"));
|
||||||
engine.server = createServer(async (request, response) => {
|
engine.server = createServer(async (request, response) => {
|
||||||
const call = engine.current;
|
const call = engine.current;
|
||||||
const message = call?.message;
|
const message = call?.message;
|
||||||
|
|||||||
+2
-1
@@ -68,7 +68,8 @@ export function socketDirectory(identity, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function socketAddress(identity, role, options) {
|
export function socketAddress(identity, role, options) {
|
||||||
if (typeof role !== "string" || !/^[a-z][a-z0-9-]{0,10}$/.test(role)) throw Error("Invalid bounded socket role");
|
if (typeof role !== "string" || role !== role.trim() || !/^[a-z][a-z0-9-]{0,10}$/.test(role))
|
||||||
|
throw Error("Invalid bounded socket role");
|
||||||
return checkedSocketPath(path.join(socketDirectory(identity, options), role + ".sock"));
|
return checkedSocketPath(path.join(socketDirectory(identity, options), role + ".sock"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user