From 393a1ffd94de871e34b22677c418b4ee1878f877 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 29 Sep 2026 11:01:28 +0000 Subject: [PATCH 1/5] Enrolment code: run the verb in a memory-bounded system scope; auth-file is the observed auth.db Found on release 02568b3046 by proud-deer-538: 1. fleet-converge run 36556990543 refused HostBudgetUnreadable: the remote argv ran gunbc with no cgroup memory bound. It now runs 'sudo -n systemd-run --scope --uid= --gid= --property=MemoryMax=.. --property=MemoryHigh=.. -- env GUNBC_WORKSPACE_ROOT=.. run ..', bounds from gunbc.live_deploy.slice_bounds approval_broker_slice_memory_{max,high} (the verb resolves the broker's own closure), in its own scope rather than the broker's slice. New extdeps.systemd.systemd_run systemd_run_scope_as_account_argv beside the user-scope form (a user scope needs the account's session bus, which the elevated command lacks). 2. The ntfy readback stat'd /var/lib/gunbc-ntfy/user.db; srv1's server.yml names auth.db. approval_ntfy_auth_file_path is corrected to the observed file and stays a declaration: the stat runs through sudo at an exact argv an operator-installed sudoers line must name, and the readback refuses unless the running config names exactly this path. The srv1 sudoers line for that stat must be re-issued for auth.db. Fixtures follow; the other.db RED is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/extdeps/systemd/systemd_run.dag | 16 ++++++ .../approval_device_enrolment_code_issue.dag | 28 +++++++++-- dag/gunbc/auth/approval_ntfy_deployment.dag | 18 ++++++- ...val_device_enrolment_code_witness_test.dag | 13 ++++- ...oval_ntfy_access_readback_witness_test.dag | 50 +++++++++---------- .../approval_broker_helper_grant_test.dag | 2 +- 6 files changed, 95 insertions(+), 32 deletions(-) diff --git a/dag/extdeps/systemd/systemd_run.dag b/dag/extdeps/systemd/systemd_run.dag index 732180a4ea2..171ec7308dc 100644 --- a/dag/extdeps/systemd/systemd_run.dag +++ b/dag/extdeps/systemd/systemd_run.dag @@ -144,6 +144,22 @@ fn systemd_run_user_scope_command(properties: List, command: ) } +// ONE COMMAND IN A TRANSIENT SCOPE OF THE SYSTEM MANAGER, RUN AS A NAMED ACCOUNT +// (`systemd-run --scope --uid=U --gid=G -p NAME=VALUE ... -- CMD`). The user-manager form above needs +// the account's own session bus, which a command elevated with `sudo -u` from another login does not +// have. The system manager starts the scope, and systemd-run drops to the named uid and gid before it +// execs the command (systemd-run(1) --uid/--gid; src/run/run.c start_transient_scope applies +// arg_exec_user/arg_exec_group to the scope's child). So the command runs as that account inside a +// cgroup the kernel bounds. The caller must be root, since only the system manager can start a scope +// for another uid, and elevation is the caller's business, not this rendering's. The words come in +// the same order as the user form, so the property rendering stays in one place. +fn systemd_run_scope_as_account_argv(account: NonEmptyStr, properties: List, command_argv: List) -> List { + let head = ["systemd-run", "--scope", concat("--uid=", account as String), concat("--gid=", account as String)] + let with_properties = fold(systemd_run_property_argv(properties: properties), init: head, f: (acc, word) => list_push(acc, word)) + let with_separator = list_push(with_properties, "--") + fold(command_argv, init: with_separator, f: (acc, arg) => list_push(acc, arg)) +} + fn systemd_run_property(name: String, value: String) -> String { join([name, "=", value], "") } diff --git a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag index 1357492242b..8948369aafe 100644 --- a/dag/gunbc/auth/approval_device_enrolment_code_issue.dag +++ b/dag/gunbc/auth/approval_device_enrolment_code_issue.dag @@ -5,7 +5,11 @@ import std.algebra { trim } import std.process { ProcessExit, ExitSuccess, ExitFailure, exit_failure } import std.resources { Network } import gunbc.cli_wire { CliWireResponse, CliWirePrintable, CliWireUnprintable } -import extdeps.sudo.elevation { sudo_elevate_as } +import extdeps.sudo.elevation { sudo_elevate } +import extdeps.systemd.systemd_run { SystemdRunProperty, systemd_run_scope_as_account_argv } +import extdeps.systemd { MemoryMax, MemoryHigh } +import std.measure { byte_size_count } +import gunbc.live_deploy.slice_bounds { approval_broker_slice_memory_max, approval_broker_slice_memory_high } import extdeps.tools.env { env_prefixed_argv } import v2.std.orchestration { EnvSet } import gunbc.cli_run_workspace_root_scaffold { gunbc_workspace_root_env_name } @@ -69,10 +73,28 @@ fn enrolment_code_issue_ssh_target() -> SshTarget { // elevation through env(1), because sudo's env_reset would drop a variable set outside it. // The elevated principal is the operator's POSIX user as modeled; /home/ubuntu in that refusal was // the SSH login's working directory, which sudo keeps, not the account the verb ran as. +// +// AND IT RUNS IN A MEMORY-BOUNDED SCOPE, because the seed refuses to resolve without one. Its +// typed-module cache cap is derived from the cgroup's memory.high/memory.max, and with neither bound +// it refuses HostBudgetUnreadable rather than guessing (fleet-converge run 36556990543). An SSH +// session binds no such limit. The scope is a system-manager transient scope that runs the verb as +// the operator's account (extdeps.systemd.systemd_run systemd_run_scope_as_account_argv). A user +// scope would need that account's session bus, which the elevated command lacks. The bounds are the +// broker's own slice rows (gunbc.live_deploy.slice_bounds approval_broker_slice_memory_max and _high). +// The verb resolves the broker's routes closure, the same program the broker boots, so the same +// measured demand bounds it. It gets its OWN scope rather than joining the broker's slice, because +// joining would split one budget between the running broker and this run and put the broker at risk. +fn enrolment_code_issue_scope_properties() -> List { + [ + SystemdRunProperty { property: MemoryMax, value: to_string(byte_size_count(b: approval_broker_slice_memory_max)) as NonEmptyStr }, + SystemdRunProperty { property: MemoryHigh, value: to_string(byte_size_count(b: approval_broker_slice_memory_high)) as NonEmptyStr }, + ] +} + fn enrolment_code_issue_remote_argv(revision: ReleaseRevisionBinding) -> List { let root = srv1_gunbc_approval_broker_root as String let release_dir = approval_broker_release_dir(root: root, revision: revision) - let inv = sudo_elevate_as(user: fleet_posix_operator_user.name, command: env_prefixed_argv(bindings: [ + let inv = sudo_elevate(command: systemd_run_scope_as_account_argv(account: fleet_posix_operator_user.name, properties: enrolment_code_issue_scope_properties(), command_argv: env_prefixed_argv(bindings: [ EnvSet { name: gunbc_workspace_root_env_name, value: release_dir }, ], command_argv: [ approval_broker_release_binary_path(root: root, revision: revision), @@ -82,7 +104,7 @@ fn enrolment_code_issue_remote_argv(revision: ReleaseRevisionBinding) -> List Bool { let argv = enrolment_code_issue_remote_argv(revision: RevisionBoundAtEmission { revision: "0123456789abcdef0123456789abcdef01234567" }) let joined = join(argv, " ") - string_contains(s: joined, pattern: "/usr/bin/sudo -n -u " + (fleet_posix_operator_user.name as String) + " /usr/bin/env " + gunbc_workspace_root_env_name + "=/opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567 /opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567/gunbc run ") + let op = fleet_posix_operator_user.name as String + string_contains(s: joined, pattern: "/usr/bin/sudo -n systemd-run --scope --uid=" + op + " --gid=" + op + + " --property=MemoryMax=" + to_string(byte_size_count(b: approval_broker_slice_memory_max)) + + " --property=MemoryHigh=" + to_string(byte_size_count(b: approval_broker_slice_memory_high)) + + " -- /usr/bin/env " + gunbc_workspace_root_env_name + "=/opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567 /opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567/gunbc run ") && string_contains(s: joined, pattern: "/releases/0123456789abcdef0123456789abcdef01234567/gunbc run ") && string_contains(s: joined, pattern: "--entry /opt/gunbc/approval-broker/releases/0123456789abcdef0123456789abcdef01234567/dag/gunbc/auth/approval_device_routes.dag --function issue_device_enrolment_code --arg revision=0123456789abcdef0123456789abcdef01234567") } diff --git a/dag/test/claim/approval_ntfy_access_readback_witness_test.dag b/dag/test/claim/approval_ntfy_access_readback_witness_test.dag index 14ef20189ed..88b3d1c6505 100644 --- a/dag/test/claim/approval_ntfy_access_readback_witness_test.dag +++ b/dag/test/claim/approval_ntfy_access_readback_witness_test.dag @@ -86,7 +86,7 @@ data fx_exact: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703658130 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153310 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153310 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -109,7 +109,7 @@ data fx_cmdoverride: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703679727 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153316 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153316 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -132,7 +132,7 @@ data fx_wildcard_cmd: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 00000000000000000000000000000000:0A1A 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703641093 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153322 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153322 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -155,7 +155,7 @@ data fx_otherexe: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703617727 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153328 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153328 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -178,7 +178,7 @@ data fx_envoverride: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703603445 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153335 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153335 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -201,7 +201,7 @@ data fx_cache: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703706119 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153341 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nauth-access-cache: true\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nauth-access-cache: true\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153341 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -224,7 +224,7 @@ data fx_cfg_default_rw: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703594203 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153347 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153347 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, @@ -247,7 +247,7 @@ data fx_wildcard_cfg: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 00000000000000000000000000000000:0A1A 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703532026 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153353 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: \":2586\"\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: \":2586\"\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153353 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -270,7 +270,7 @@ data fx_metrics_extra: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703606428 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 00000000000000000000000000000000:2383 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703665349 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153359 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nmetrics-listen-http: \":9091\"\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nmetrics-listen-http: \":9091\"\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153359 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -316,7 +316,7 @@ data fx_config_touched: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703705233 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153376 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153376 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -339,7 +339,7 @@ data fx_binary_touched: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703713392 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153378 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153378 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -362,7 +362,7 @@ data fx_authfile_replaced: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703704240 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153384 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153384 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153388 1790153388 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -408,7 +408,7 @@ data fx_config_swapped: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703645559 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11184389 1790153396 1790140876 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153401 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153389 1790153388 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, @@ -431,7 +431,7 @@ data fx_auth_swapped: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703640467 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11141485 1790153403 1790153401 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153403 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153389 1790153388 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -454,7 +454,7 @@ data fx_ambient_auth_file: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703709348 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11141485 1790153409 1790153401 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153409 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153408 1790153407 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- read-only access to topic gunbc-approvals\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -479,7 +479,7 @@ data fx_config_symlink: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703720529 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11141484 1790153414 1790153414 symbolic link\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n# changed between the reads\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n# changed between the reads\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141484 1790153414 1790153414 symbolic link\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153414 1790153407 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -502,7 +502,7 @@ data fx_auth_symlink: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703719526 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11141485 1790153421 1790153401 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153421 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153420 1790153420 symbolic link\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -525,7 +525,7 @@ data fx_restarted: List = [ FxRead { read: "net_tcp", exit_code: 0, stdout: " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n 0: 0100007F:0A1A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 2703688826 2 0000000000000000 100 0 0 10 0\n", stderr: "" }, FxRead { read: "net_tcp6", exit_code: 0, stdout: " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n", stderr: "" }, FxRead { read: "config_identity_before", exit_code: 0, stdout: "11141485 1790153428 1790153401 regular file\n", stderr: "" }, - FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/user.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, + FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153428 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153427 1790153407 regular file\n", stderr: "" }, FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, @@ -632,7 +632,7 @@ fn bound_observation(fx: List) -> NtfyRuntimeProcessObservation { NtfyOpenFd { target: "/dev/null", target_inode: 5 }, NtfyOpenFd { target: "anon_inode:[eventpoll]", target_inode: 1057 }, NtfyOpenFd { target: "pipe:[2703658101]", target_inode: 2703658101 }, - NtfyOpenFd { target: "/var/lib/gunbc-ntfy/user.db", target_inode: fx_auth_inode(fx: fx) }, + NtfyOpenFd { target: "/var/lib/gunbc-ntfy/auth.db", target_inode: fx_auth_inode(fx: fx) }, ], fx_listen_socket_fds(fx: fx)), } } @@ -775,7 +775,7 @@ test fn an_executable_of_another_inode_refuses() -> Bool { test fn an_auth_file_the_process_does_not_hold_refuses() -> Bool { let o = bound_observation(fx: fx_exact) match gap_of(fx: with_observation(fx: fx_exact, o: NtfyRuntimeProcessObservation { main_pid: o.main_pid, start_ticks_before: o.start_ticks_before, start_ticks_after: o.start_ticks_after, environment_names: o.environment_names, exe_target: o.exe_target, exe_inode: o.exe_inode, - open_fds: map(o.open_fds, fd => if fd.target == "/var/lib/gunbc-ntfy/user.db" { NtfyOpenFd { target: fd.target, target_inode: fd.target_inode + 7 } } else { fd }) })) { + open_fds: map(o.open_fds, fd => if fd.target == "/var/lib/gunbc-ntfy/auth.db" { NtfyOpenFd { target: fd.target, target_inode: fd.target_inode + 7 } } else { fd }) })) { Present { value: RuntimeAuthFileNotHeldByProcess { inode: i } } => i == "11184383" _ => false } @@ -785,8 +785,8 @@ test fn an_auth_file_the_process_does_not_hold_refuses() -> Bool { test fn an_auth_file_held_deleted_refuses_as_such() -> Bool { let o = bound_observation(fx: fx_exact) match gap_of(fx: with_observation(fx: fx_exact, o: NtfyRuntimeProcessObservation { main_pid: o.main_pid, start_ticks_before: o.start_ticks_before, start_ticks_after: o.start_ticks_after, environment_names: o.environment_names, exe_target: o.exe_target, exe_inode: o.exe_inode, - open_fds: concat(o.open_fds, [NtfyOpenFd { target: "/var/lib/gunbc-ntfy/user.db (deleted)", target_inode: 11184300 }]) })) { - Present { value: RuntimeAuthFileHeldDeleted { target: t } } => t == "/var/lib/gunbc-ntfy/user.db (deleted)" + open_fds: concat(o.open_fds, [NtfyOpenFd { target: "/var/lib/gunbc-ntfy/auth.db (deleted)", target_inode: 11184300 }]) })) { + Present { value: RuntimeAuthFileHeldDeleted { target: t } } => t == "/var/lib/gunbc-ntfy/auth.db (deleted)" _ => false } } @@ -796,7 +796,7 @@ test fn an_auth_file_held_deleted_refuses_as_such() -> Bool { test fn an_anon_inode_fd_does_not_bind_the_auth_file() -> Bool { let o = bound_observation(fx: fx_exact) match gap_of(fx: with_observation(fx: fx_exact, o: NtfyRuntimeProcessObservation { main_pid: o.main_pid, start_ticks_before: o.start_ticks_before, start_ticks_after: o.start_ticks_after, environment_names: o.environment_names, exe_target: o.exe_target, exe_inode: o.exe_inode, - open_fds: map(o.open_fds, fd => if fd.target == "/var/lib/gunbc-ntfy/user.db" { NtfyOpenFd { target: "anon_inode:[/var/lib/gunbc-ntfy/user.db]", target_inode: fd.target_inode } } else { fd }) })) { + open_fds: map(o.open_fds, fd => if fd.target == "/var/lib/gunbc-ntfy/auth.db" { NtfyOpenFd { target: "anon_inode:[/var/lib/gunbc-ntfy/auth.db]", target_inode: fd.target_inode } } else { fd }) })) { Present { value: RuntimeAuthFileNotHeldByProcess { inode: _ } } => true _ => false } @@ -829,8 +829,8 @@ test fn pin_net_tcp6() -> Bool { pinned(read: ReadTcp6SocketTable, want: "timeou test fn pin_config_identity_before() -> Bool { pinned(read: ReadConfigIdentityBefore, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /etc/gunbc-ntfy/server.yml") } test fn pin_config_identity_after() -> Bool { pinned(read: ReadConfigIdentityAfter, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /etc/gunbc-ntfy/server.yml") } test fn pin_config() -> Bool { pinned(read: ReadConfig, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy cat /etc/gunbc-ntfy/server.yml") } -test fn pin_auth_file_identity_before() -> Bool { pinned(read: ReadAuthFileIdentityBefore, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /var/lib/gunbc-ntfy/user.db") } -test fn pin_auth_file_identity_after() -> Bool { pinned(read: ReadAuthFileIdentityAfter, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /var/lib/gunbc-ntfy/user.db") } +test fn pin_auth_file_identity_before() -> Bool { pinned(read: ReadAuthFileIdentityBefore, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /var/lib/gunbc-ntfy/auth.db") } +test fn pin_auth_file_identity_after() -> Bool { pinned(read: ReadAuthFileIdentityAfter, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/stat -c %i %Z %W %F -- /var/lib/gunbc-ntfy/auth.db") } test fn pin_access_list_under_an_empty_environment() -> Bool { pinned(read: ReadAccessList, want: "timeout --kill-after 2s 10s /usr/bin/sudo -n -u gunbc-ntfy /usr/bin/env -i /usr/local/bin/ntfy access --config /etc/gunbc-ntfy/server.yml") } test fn pin_binary_inode() -> Bool { pinned(read: ReadBinaryInode, want: "timeout --kill-after 2s 10s /usr/bin/stat -c %i -- /usr/local/bin/ntfy") } // THE HELPER READ IS THE GRANTED ARGV, under its own wider timeout: the words after the timeout are diff --git a/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag b/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag index 37cbe4ceab3..d58c6c7631f 100644 --- a/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag +++ b/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag @@ -289,7 +289,7 @@ data sample_observation: NtfyRuntimeProcessObservation = NtfyRuntimeProcessObser exe_target: "/usr/local/bin/ntfy", exe_inode: 131, open_fds: [ - NtfyOpenFd { target: "/var/lib/gunbc-ntfy/user.db (deleted)", target_inode: 77 }, + NtfyOpenFd { target: "/var/lib/gunbc-ntfy/auth.db (deleted)", target_inode: 77 }, NtfyOpenFd { target: "socket:[55123]", target_inode: 55123 }, NtfyOpenFd { target: "/tmp/a=b c", target_inode: 12 }, ], From e9a415f80bcb982b8e2f85cbc460e7013313c3b1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 29 Sep 2026 11:02:38 +0000 Subject: [PATCH 2/5] approval_ntfy_auth_file_path note: state srv1's actual grant posture (no narrow stat grant; operator's broader sudo covers it) Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/auth/approval_ntfy_deployment.dag | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dag/gunbc/auth/approval_ntfy_deployment.dag b/dag/gunbc/auth/approval_ntfy_deployment.dag index 1c8db0810b9..59f53a008a5 100644 --- a/dag/gunbc/auth/approval_ntfy_deployment.dag +++ b/dag/gunbc/auth/approval_ntfy_deployment.dag @@ -62,11 +62,16 @@ data approval_ntfy_config_path: NonEmptyStr = join([approval_ntfy_config_dir as // installed with `auth-file: /var/lib/gunbc-ntfy/auth.db` in /etc/gunbc-ntfy/server.yml, and that file // exists while user.db does not. proud-deer-538 read this on 2026-09-29 running // issue_device_enrolment_code by hand against release 02568b3046, and the readback refused before -// minting. The name is now the observed one. Changing it also changes the exact stat argv that -// approval_ntfy_deployment's human step asks the operator to grant, so srv1's sudoers line for that -// stat must be re-issued naming auth.db, or every issuance refuses at that read. Nothing installs this -// server from the model yet (gunbc.auth.approval_ntfy_converge names it as outside its roster); the -// row becomes that installer's destination when one exists. +// minting. The name is now the observed one, and it changes the exact stat argv that this module's +// human step asks the operator to grant. +// +// THAT GRANT DOES NOT EXIST ON srv1 TODAY, and the stat still runs. proud-deer-538 read srv1 on +// 2026-09-29: sudoers.d holds only the broker-helpers grant, and the stat of auth.db succeeds under +// the operator account's own broader sudo. The narrow, exact-argv grant this module asks for is +// therefore an unmet posture, not a failing read. The earlier refusal was the missing user.db, not a +// missing grant. Nothing installs this server or that grant from the model yet +// (gunbc.auth.approval_ntfy_converge names the server as outside its roster). This row becomes that +// installer's destination when one exists, and the grant becomes its sudoers member. data approval_ntfy_auth_file_path: NonEmptyStr = join([approval_ntfy_state_root as String, "/auth.db"], "") as NonEmptyStr data approval_ntfy_unit_name: NonEmptyStr = "gunbc-ntfy.service" From 60dcd182326cf2cd5b310f5eb7251f8e9477db59 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 29 Sep 2026 11:16:10 +0000 Subject: [PATCH 3/5] ntfy ACL users: declare the names srv1's server carries (gunbc-broker publisher, briansrls operator) proud-deer-538 ran #12614's verb by hand on srv1; the readback refused on the ACL principals. The live 'ntfy access' lists publisher gunbc-broker (write-only) and operator briansrls (read-only), anonymous denied; the declared gunbc-approval-publisher / gunbc-approval-operator never existed on the host. The live accounts hold the broker's publisher token and the operator's phone subscription, so the declarations follow them (still compared, still refusing any other shape). Fixtures follow; the eve/root REDs are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/auth/approval_ntfy_deployment.dag | 14 +++- ...oval_ntfy_access_readback_witness_test.dag | 68 +++++++++---------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/dag/gunbc/auth/approval_ntfy_deployment.dag b/dag/gunbc/auth/approval_ntfy_deployment.dag index 59f53a008a5..269a9ba330d 100644 --- a/dag/gunbc/auth/approval_ntfy_deployment.dag +++ b/dag/gunbc/auth/approval_ntfy_deployment.dag @@ -81,8 +81,18 @@ data approval_ntfy_unit_name: NonEmptyStr = "gunbc-ntfy.service" // only WRITE the topic; the operator is the phone's sign-in and may only READ it. A server whose // users carry other names reads back as a deviation and enrolment-code issuance refuses -- loudly, // never by guessing which user is which. -data approval_ntfy_publisher_user: NonEmptyStr = "gunbc-approval-publisher" -data approval_ntfy_operator_user: NonEmptyStr = "gunbc-approval-operator" +// +// THE NAMES ARE THE ONES srv1's SERVER CARRIES, AND THE PREVIOUS ONES WERE NEVER CREATED. They read +// gunbc-approval-publisher and gunbc-approval-operator, names chosen here that no step on the host +// used. The live `ntfy access` that proud-deer-538 read on srv1 on 2026-09-29, running +// issue_device_enrolment_code by hand, lists publisher `gunbc-broker` (write-only on the topic) and +// operator `briansrls` (read-only), with anonymous access denied. Those accounts already carry the +// broker's publisher token and the operator's phone subscription, so the declaration follows them +// rather than asking for both credentials to be minted again. These stay declarations: the readback +// compares the live list against them and refuses any other shape. Nothing creates these users from +// the model yet. The ntfy installation vertical owns that, and these rows become its contract. +data approval_ntfy_publisher_user: NonEmptyStr = "gunbc-broker" +data approval_ntfy_operator_user: NonEmptyStr = "briansrls" // THE SERVER BINARY THE HOST ACTUALLY RUNS. Absolute because it runs under sudo as the server's // principal; an absent binary is a failed readback, which refuses. The readback compares the running // process's executable against THIS row, and that comparison is only a check because the row is not diff --git a/dag/test/claim/approval_ntfy_access_readback_witness_test.dag b/dag/test/claim/approval_ntfy_access_readback_witness_test.dag index 88b3d1c6505..adea617641d 100644 --- a/dag/test/claim/approval_ntfy_access_readback_witness_test.dag +++ b/dag/test/claim/approval_ntfy_access_readback_witness_test.dag @@ -89,7 +89,7 @@ data fx_exact: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153310 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=83665\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -112,7 +112,7 @@ data fx_cmdoverride: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153316 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=83831\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -135,7 +135,7 @@ data fx_wildcard_cmd: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153322 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=83999\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -158,7 +158,7 @@ data fx_otherexe: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153328 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84166\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -181,7 +181,7 @@ data fx_envoverride: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153335 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84334\nEnvironment=NTFY_AUTH_DEFAULT_ACCESS=read-write\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -204,7 +204,7 @@ data fx_cache: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nauth-access-cache: true\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153341 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84499\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -227,7 +227,7 @@ data fx_cfg_default_rw: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153347 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84664\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -250,7 +250,7 @@ data fx_wildcard_cfg: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: \":2586\"\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153353 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84832\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -273,7 +273,7 @@ data fx_metrics_extra: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\nmetrics-listen-http: \":9091\"\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153359 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=84997\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -296,7 +296,7 @@ data fx_authfile_other: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/other.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153366 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=85163\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -319,7 +319,7 @@ data fx_config_touched: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153376 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=85333\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -342,7 +342,7 @@ data fx_binary_touched: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153378 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11184383 1790140901 1790140882 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=85498\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -365,7 +365,7 @@ data fx_authfile_replaced: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11184389 1790153384 1790140876 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153388 1790153388 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141484 1790153388 1790153388 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=85666\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -411,7 +411,7 @@ data fx_config_swapped: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: read-write\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153401 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153389 1790153388 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141484 1790153389 1790153388 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=85998\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -434,7 +434,7 @@ data fx_auth_swapped: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153403 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153389 1790153388 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141486 1790153407 1790153407 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=86165\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -457,14 +457,14 @@ data fx_ambient_auth_file: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153409 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153408 1790153407 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- read-only access to topic gunbc-approvals\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- read-only access to topic gunbc-approvals\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141486 1790153408 1790153407 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=86373\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, FxRead { read: "pid_stat_after", exit_code: 0, stdout: "86373 (ntfy) S 83658 86373 86373 0 -1 4194304 5490 68 0 0 9 2 0 0 20 0 40 0 245607676 4383285248 9984 18446744073709551615 4194304 74414103 281474759051744 0 0 0 0 0 2143420159 0 0 0 17 64 0 0 0 0 0 74488800 75479617 990441472 281474759054369 281474759054634 281474759054634 281474759069564 0\n", stderr: "" }, ] -data fx_access_without_env_i_under_ambient_auth_file: String = "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data fx_access_without_env_i_under_ambient_auth_file: String = "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" data fx_config_symlink: List = [ FxRead { read: "unit_before", exit_code: 0, stdout: "ActiveState=active\nMainPID=86583\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -482,7 +482,7 @@ data fx_config_symlink: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n# changed between the reads\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141484 1790153414 1790153414 symbolic link\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153414 1790153407 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141486 1790153414 1790153407 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=86583\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -505,7 +505,7 @@ data fx_auth_symlink: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153421 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141484 1790153420 1790153420 symbolic link\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141484 1790153420 1790153420 symbolic link\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=86757\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, @@ -528,22 +528,22 @@ data fx_restarted: List = [ FxRead { read: "config", exit_code: 0, stdout: "auth-file: /var/lib/gunbc-ntfy/auth.db\nauth-default-access: deny-all\nlisten-http: 127.0.0.1:2586\nbase-url: http://127.0.0.1:2586\n", stderr: "" }, FxRead { read: "config_identity_after", exit_code: 0, stdout: "11141485 1790153428 1790153401 regular file\n", stderr: "" }, FxRead { read: "auth_file_identity_before", exit_code: 0, stdout: "11141486 1790153427 1790153407 regular file\n", stderr: "" }, - FxRead { read: "access_list", exit_code: 0, stdout: "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, + FxRead { read: "access_list", exit_code: 0, stdout: "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n", stderr: "" }, FxRead { read: "auth_file_identity_after", exit_code: 0, stdout: "11141486 1790153427 1790153407 regular file\n", stderr: "" }, FxRead { read: "front_door", exit_code: 0, stdout: "{\"TCP\":{\"443\":{\"HTTPS\":true}},\"Web\":{\"srv1.example.ts.net:443\":{\"Handlers\":{\"/\":{\"Proxy\":\"http://127.0.0.1:2586\"}}}}}", stderr: "" }, FxRead { read: "unit_after", exit_code: 0, stdout: "ActiveState=active\nMainPID=87088\nEnvironment=\nEnvironmentFiles=\nPassEnvironment=\n", stderr: "" }, FxRead { read: "pid_stat_after", exit_code: 0, stdout: "87088 (ntfy) S 83658 87088 87088 0 -1 4194304 5553 66 0 0 8 3 0 0 20 0 40 0 245609956 4317151232 10496 18446744073709551615 4194304 74414103 281474617439264 0 0 0 0 0 2143420159 0 0 0 17 57 0 0 0 0 0 74488800 75479617 355835904 281474617443361 281474617443626 281474617443626 281474617458556 0\n", stderr: "" }, ] -data listing_default_rw: String = "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n" -data listing_anon_read: String = "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- read-only access to topic gunbc-approvals\n- no access to any (other) topics (server config)\n" -data listing_pub_rw: String = "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- read-write access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_op_rw: String = "user gunbc-approval-operator (role: user, tier: none)\n- read-write access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_op_nogrant: String = "user gunbc-approval-operator (role: user, tier: none)\n- no topic-specific permissions\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_op_missing: String = "user gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_admin_extra: String = "user root (role: admin, tier: none)\n- read-write access to all topics (admin role)\nuser gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_wildcard_reader: String = "user eve (role: user, tier: none)\n- read-only access to topic gunbc-*\nuser gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-approval-publisher (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" -data listing_pub_admin: String = "user gunbc-approval-publisher (role: admin, tier: none)\n- read-write access to all topics (admin role)\nuser gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_default_rw: String = "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- read-write access to all (other) topics (server config)\n" +data listing_anon_read: String = "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- read-only access to topic gunbc-approvals\n- no access to any (other) topics (server config)\n" +data listing_pub_rw: String = "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- read-write access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_op_rw: String = "user briansrls (role: user, tier: none)\n- read-write access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_op_nogrant: String = "user briansrls (role: user, tier: none)\n- no topic-specific permissions\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_op_missing: String = "user gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_admin_extra: String = "user root (role: admin, tier: none)\n- read-write access to all topics (admin role)\nuser briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_wildcard_reader: String = "user eve (role: user, tier: none)\n- read-only access to topic gunbc-*\nuser briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser gunbc-broker (role: user, tier: none)\n- write-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" +data listing_pub_admin: String = "user gunbc-broker (role: admin, tier: none)\n- read-write access to all topics (admin role)\nuser briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\nuser * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)\n" fn run_of(fx: List, name: String) -> ProcessRunOutcome { match first(filter(fx, x => x.read == name)) { @@ -1163,7 +1163,7 @@ test fn an_operator_without_the_grant_refuses() -> Bool { test fn a_missing_operator_refuses() -> Bool { match refused_with(v: verdict_of(fx: with_listing(listing: listing_op_missing))) { - Present { value: AclPrincipalMissing { user: u } } => u == "gunbc-approval-operator" + Present { value: AclPrincipalMissing { user: u } } => u == "briansrls" _ => false } } @@ -1213,21 +1213,21 @@ test fn a_star_row_at_role_anonymous_is_the_accepted_control() -> Bool { // each is a listing that does not parse, naming the user, never a user holding both. test fn an_admin_line_after_topic_grants_refuses() -> Bool { match ntfy_access_read(stdout: replace(s: stdout_of(fx: fx_exact, name: "access_list"), from: "- write-only access to topic gunbc-approvals\n", to: "- write-only access to topic gunbc-approvals\n- read-write access to all topics (admin role)\n")) { - NtfyAccessUnparseable { line: _, cause: c } => string_contains(s: c, pattern: "admin line after topic grants for user 'gunbc-approval-publisher'") + NtfyAccessUnparseable { line: _, cause: c } => string_contains(s: c, pattern: "admin line after topic grants for user 'gunbc-broker'") _ => false } } test fn a_topic_grant_after_the_admin_line_refuses() -> Bool { match ntfy_access_read(stdout: replace(s: listing_pub_admin, from: "- read-write access to all topics (admin role)\n", to: "- read-write access to all topics (admin role)\n- write-only access to topic gunbc-approvals\n")) { - NtfyAccessUnparseable { line: _, cause: c } => string_contains(s: c, pattern: "topic grant after the admin line for user 'gunbc-approval-publisher'") + NtfyAccessUnparseable { line: _, cause: c } => string_contains(s: c, pattern: "topic grant after the admin line for user 'gunbc-broker'") _ => false } } test fn a_duplicated_principal_refuses() -> Bool { - match refused_with(v: verdict_of(fx: with_listing(listing: stdout_of(fx: fx_exact, name: "access_list") + "user gunbc-approval-operator (role: user, tier: none)\n- read-only access to topic gunbc-approvals\n"))) { - Present { value: AclPrincipalDuplicated { user: u } } => u == "gunbc-approval-operator" + match refused_with(v: verdict_of(fx: with_listing(listing: stdout_of(fx: fx_exact, name: "access_list") + "user briansrls (role: user, tier: none)\n- read-only access to topic gunbc-approvals\n"))) { + Present { value: AclPrincipalDuplicated { user: u } } => u == "briansrls" _ => false } } From 739eebb36e927dd16e78cf2ed6bdfcdc5668958a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 29 Sep 2026 11:31:06 +0000 Subject: [PATCH 4/5] Broker install ensures the approval device store (operator-owned, owner-only), closing a hand step approval_device_store_root (/var/lib/gunbc/approval-devices) was created by nothing in the model; the first issued enrolment code (srv1, 2026-09-29) needed it made by hand as briansrls 0700. gunbc.auth.approval_device_redemption approval_device_store_directory declares it as a ManagedDirectory (owner and sole dependent = fleet_posix_operator_user, so the derived mode is owner-only; Ensured so a retract keeps the enrolled devices), and approval_broker_dark_install_release_steps ensures it first. The broker closure does not grow (every new import was already in it). Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/auth/approval_device_redemption.dag | 26 ++++++++++++++++++- dag/gunbc/live_deploy/emit.dag | 9 +++++-- .../approval_broker_helper_grant_test.dag | 14 +++++++--- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/dag/gunbc/auth/approval_device_redemption.dag b/dag/gunbc/auth/approval_device_redemption.dag index 824516f9781..6dc21002c57 100644 --- a/dag/gunbc/auth/approval_device_redemption.dag +++ b/dag/gunbc/auth/approval_device_redemption.dag @@ -1,7 +1,11 @@ module gunbc.auth.approval_device_redemption import std.decl_ref { decl_ref } -import std.types { NonEmptyStr, String, Timestamp, Int, List } +import gunbc.managed_directory { ManagedDirectory, DirectoryDependent, EntriesUnmanaged } +import gunbc.ownership { Ensured } +import std.effect_grant { Read, Write, Execute } +import gunbc.fleet_posix_accounts { fleet_posix_operator_user } +import std.types { NonEmptyStr, String, Timestamp, Int, List, FilePath } import std.logic { Bool } import std.dissolution { DissolutionCondition, unbound_dissolution } import extdeps.crypto.signature { VerifyingKey, SignatureVerification, SignatureVerified } @@ -505,6 +509,26 @@ fn redeem_device_over_store( // an EnrolmentAdmitted value, under a root no other principal can write. data approval_device_store_root: NonEmptyStr = "/var/lib/gunbc/approval-devices" +// THE STORE DIRECTORY IS CONVERGED BY THE BROKER INSTALL, and until this row nothing in the model +// created it. The first issued enrolment code (srv1, 2026-09-29) needed proud-deer-538 to create it by +// hand as briansrls:briansrls 0700. That hand step is closed by this row: the broker's dark install +// ensures it (gunbc.live_deploy.emit approval_broker_dark_install_release_steps). The owner is the +// operator's account, because the verbs that read and write it (approval_device_routes, the +// enrolment code issue) run as that account and nothing else needs it, so the derived mode is +// owner-only. Ownership is Ensured, not Owned: the directory holds enrolled devices, which must +// survive a retract of the deployment that created it. +fn approval_device_store_directory() -> ManagedDirectory { + ManagedDirectory { + member: "approval-device-store" as NonEmptyStr, + path: approval_device_store_root as String as FilePath, + owner: fleet_posix_operator_user, + group_principal: fleet_posix_operator_user, + dependents: [DirectoryDependent { who: fleet_posix_operator_user, needs: [Read, Write, Execute] }], + ownership: Ensured, + entries: EntriesUnmanaged, + } +} + type DeviceStoreWrite = DeviceStoreWritten | DeviceStoreSlotOccupied diff --git a/dag/gunbc/live_deploy/emit.dag b/dag/gunbc/live_deploy/emit.dag index 7c97071f627..7ac90d0fe7a 100644 --- a/dag/gunbc/live_deploy/emit.dag +++ b/dag/gunbc/live_deploy/emit.dag @@ -1,5 +1,6 @@ module gunbc.live_deploy.emit +import gunbc.auth.approval_device_redemption { approval_device_store_directory } import gunbc.live_deploy.release_locus { ReleaseRevisionBinding, RevisionBoundAtEmission, @@ -27,6 +28,7 @@ import std.evaluation_budget { EvaluationLimit, LimitSet, LimitUnset } import std.types { String, List, Bool, NonEmptyStr, Int, FilePath, CommitSha, Port } import gunbc.managed_directory { ManagedDirectory, + managed_directory_admit, ManagedDirectoryAdmission, ManagedDirectoryAdmitted, ManagedDirectoryRefused, managed_directory_mode_octal, attempt_state_directory_at, @@ -3133,8 +3135,11 @@ fn live_deploy_wholesale_refused_poison(refusals: List List { concat( - approval_broker_tree_copy_steps(spec: spec, revision: revision), - approval_broker_helper_grant_steps(spec: spec, revision: revision), + [deploy_raw(command: ensure_managed_directory_command(admission: managed_directory_admit(d: approval_device_store_directory())))], + concat( + approval_broker_tree_copy_steps(spec: spec, revision: revision), + approval_broker_helper_grant_steps(spec: spec, revision: revision), + ), ) } diff --git a/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag b/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag index d58c6c7631f..089ab46eb57 100644 --- a/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag +++ b/dag/test/claim/live_deploy/approval_broker_helper_grant_test.dag @@ -242,8 +242,9 @@ test fn only_the_renamed_file_is_ever_included_by_sudo() -> Bool { } // THE DARK INSTALL CARRIES THIS RELEASE'S GRANT, inspected at the composition the dark install's -// intent consumes (gunbc.live_deploy.emit approval_broker_dark_install_release_steps): the release -// tree's steps, then exactly the eighteen snapshot and grant steps, ending with retirement of every +// intent consumes (gunbc.live_deploy.emit approval_broker_dark_install_release_steps): the device +// store's ensure (the operator's 0700 directory), the release tree's steps, then exactly the eighteen +// snapshot and grant steps, ending with retirement of every // other release's snapshot. The whole orchestrated script is still rendered by the existing dark-install // claims in test.claim.live_deploy.emit, which now carry these steps. Rendering it again here cost // more than the new-witness budget (110041 and 100982 eval steps against 72300). @@ -251,8 +252,13 @@ test fn the_dark_install_composes_this_releases_grant() -> Bool { let spec = deployment_spec_srv1() let steps = approval_broker_dark_install_release_steps(spec: spec, revision: rev_a()) let tree = count(approval_broker_tree_copy_steps(spec: spec, revision: rev_a())) - count(steps) == tree + 18 - && match steps |> skip(n: tree + 13) |> first { + count(steps) == tree + 19 + && match steps |> first { + Present { value: st } => string_contains(s: pipeline_step_text(st: st), pattern: "/var/lib/gunbc/approval-devices") + && string_contains(s: pipeline_step_text(st: st), pattern: "0700") + Absent => false + } + && match steps |> skip(n: tree + 14) |> first { Present { value: st } => string_contains(s: pipeline_step_text(st: st), pattern: "'/usr/bin/mv' '-T'") Absent => false } From a0c54732e1c4091804dee9264762be470ee291b1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 29 Sep 2026 12:11:46 +0000 Subject: [PATCH 5/5] Enrolment root wet claim: compare the store root's existence instead of assuming it absent The claim required approval_device_store_root to be absent on the runner; srv1 now has it (created by hand for the first issued code, and ensured by the broker install from this PR), so it went red on srv1-09 (run 36562259065) while the verb refused correctly. It now asserts the refusal and that the call leaves the store's existence unchanged -- host-independent, and still red for a verb that proceeds to the store on a host without one. Co-Authored-By: Claude Opus 5.5 (1M context) --- ...al_ntfy_access_readback_wet_witness_test.dag | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/dag/test/claim/approval_ntfy_access_readback_wet_witness_test.dag b/dag/test/claim/approval_ntfy_access_readback_wet_witness_test.dag index c10b7ccab95..12f06aae5fd 100644 --- a/dag/test/claim/approval_ntfy_access_readback_wet_witness_test.dag +++ b/dag/test/claim/approval_ntfy_access_readback_wet_witness_test.dag @@ -21,8 +21,7 @@ import gunbc.auth.approval_ntfy_runtime_observe { // passes is never installed, so the one read that can bind the process -- the per-release helper at // /opt/gunbc/approval-broker-helpers/ -- cannot run on any host, and the verdict over // every reading is refusal: a failed or inactive unit, any runtime gap, or that failed helper read. -// So the claim holds on ANY host: the verb refuses before any code is minted and the store root is -// never created. Forcing the verdict (the verb proceeding without its +// So the claim holds on ANY host: the verb refuses before any code is minted and creates no store root. Forcing the verdict (the verb proceeding without its // reading) reds it: it then goes on to the clock, the entropy and the store. Every refusal arm is // witnessed hermetically over supplied readings in // test.claim.approval_ntfy_access_readback_witness_test; this is the one claim that runs the real @@ -33,15 +32,23 @@ fn path_exists(path: String) -> Bool { run_shell_command_observe(command: posix_sh_program_command(script: "test -e '" + path + "'\n"), transport: LocalExec).exit_code == 0 } +// +// THE STORE'S EXISTENCE IS COMPARED, NOT ASSUMED ABSENT. This claim once required that the store root +// not exist on the runner. That premise came from the host, not the subject, and srv1 falsified it on +// 2026-09-29: the root was created there by hand for the first issued code, and the broker install now +// ensures it (gunbc.auth.approval_device_redemption approval_device_store_directory). The claim went red +// on srv1-09 (#12614 floor run 36562259065) while the verb behaved correctly. The subject is that the +// verb refuses before minting and creates nothing. So it asserts the refusal, and that the call leaves +// the store's existence as it found it. That holds on a host with a store and on one without, and a +// verb that went on to the store still reds it on a host without one. test fn the_root_refuses_before_minting_on_this_host_by_real_execution() -> Bool { let before = path_exists(path: approval_device_store_root as String) let r = issue_device_enrolment_code(revision: never_installed_revision) - !before - && (match r { + (match r { CliWireUnprintable { cause: c } => string_contains(s: c as String, pattern: "refused before any code was minted") _ => false }) - && !path_exists(path: approval_device_store_root as String) + && path_exists(path: approval_device_store_root as String) == before } // THE HELPER'S REAL OUTPUT READS THROUGH THE REAL CODEC. The helper entry