diff --git a/nix/agent-modules/forge-token.nix b/nix/agent-modules/forge-token.nix index 91d54775..441bcbd8 100644 --- a/nix/agent-modules/forge-token.nix +++ b/nix/agent-modules/forge-token.nix @@ -164,9 +164,10 @@ in # 🩸 Degrades where ./bao.nix's check fails, because one policy stanza # governs both reads: the one that let the login read `bao-mtls` covers - # this path too, so a refusal here is a token not minted yet. The - # file already in place, if any, is kept: a store that is briefly - # unreachable must not take a working token away. + # this path too, so "No value found at " or a 403 here is a token + # not minted yet and exits 0. Any other failure (network, timeout, 5xx) + # exits 1 so the unit's restart policy retries it. The file already in + # place, if any, is kept either way. # # ⚠️ Written by redirect, never echoed: the field is the secret. The # staging file is removed first so the redirect creates it — a file @@ -174,9 +175,15 @@ in # writing. rm -f "$err" ${lib.escapeShellArg stagingFile} if ! bao kv get -field=value ${lib.escapeShellArg tokenPath} > ${lib.escapeShellArg stagingFile} 2>"$err"; then - echo "no forge token at ${tokenPath} yet; consumers keep using the state-dir token if there is one." >&2 + # bao prints `Code: ` only for an HTTP answer. + if [[ "$(<"$err")" == *"No value found at "* || "$(<"$err")" == *"Code: 403"* ]]; then + echo "no forge token at ${tokenPath} yet; consumers keep using the state-dir token if there is one." >&2 + if [ -s "$err" ]; then cat "$err" >&2; fi + exit 0 + fi + echo "the swarm secret store failed reading ${tokenPath}; consumers keep using the state-dir token if there is one:" >&2 if [ -s "$err" ]; then cat "$err" >&2; fi - exit 0 + exit 1 fi if cmp -s ${lib.escapeShellArg stagingFile} ${lib.escapeShellArg tokenFile}; then diff --git a/nix/script-tests/agent-bao-fetch.sh b/nix/script-tests/agent-bao-fetch.sh index 4082e71c..ab569a34 100644 --- a/nix/script-tests/agent-bao-fetch.sh +++ b/nix/script-tests/agent-bao-fetch.sh @@ -207,6 +207,40 @@ expect_err "No value found" expect_calls "$login_call" "$kv_call" expect_file "$rt/$secret_name" old +setup FORGE "a refused read (403) is a token not minted yet" +printf old >"$rt/$secret_name" +chmod 0400 "$rt/$secret_name" +kv_rc=2 +kv_err=$'Error reading secret/data/swarm/agents/a1: Error making API request.\n\nURL: GET https://bao.t.local:8200/v1/secret/data/swarm/agents/a1\nCode: 403. Errors:\n\n* 1 error occurred:\n\t* permission denied\n' +go +expect_rc 0 +expect_err "$missing_msg" +expect_calls "$login_call" "$kv_call" +expect_file "$rt/$secret_name" old + +setup FORGE "a read that fails on the network exits 1 and keeps the old token" +printf old >"$rt/$secret_name" +chmod 0400 "$rt/$secret_name" +kv_rc=2 +kv_err="Error reading secret/data/swarm/agents/a1: Get \"$bao_addr/v1/secret/data/swarm/agents/a1\": dial tcp: connect: connection refused" +go +expect_rc 1 +expect_err "the swarm secret store failed reading $path" +expect_err "connection refused" +expect_no_err "$missing_msg" +expect_calls "$login_call" "$kv_call" +expect_file "$rt/$secret_name" old +expect_no_file "$rt/token.new" + +setup FORGE "a read that the store answers with a 5xx exits 1" +kv_rc=2 +kv_err=$'Error reading secret/data/swarm/agents/a1: Error making API request.\n\nCode: 503. Errors:\n\n* Vault is sealed\n' +go +expect_rc 1 +expect_err "the swarm secret store failed reading $path" +expect_no_err "$missing_msg" +expect_no_file "$rt/$secret_name" + setup FORGE "a first fetch writes the secret 0400" go expect_rc 0