Watch
0
0
Fork
You've already forked hyperhive
0

agents keep their github token in /run, fetched on every start, not in the state dir

This commit is contained in:
atlas 2026-11-15 02:04:04 +01:00 • committed by mara
commit 4bd02ea14d
2 changed files with 310 additions and 78 deletions

View file

@ -1,7 +1,8 @@
# `checks.script-test-agent-bao-fetch` — runs the agent units that log in to
# the swarm secret store and fetch a secret, ../agent-modules/forge-token.nix,
# ../agent-modules/bao.nix and ../agent-modules/egress-proxy.nix, against a stub
# `bao`. The cases are in ./agent-bao-fetch.sh.
# ../agent-modules/github-token.nix, ../agent-modules/bao.nix and
# ../agent-modules/egress-proxy.nix, against a stub `bao`. The cases are in
# ./agent-bao-fetch.sh.
#
# What runs is the unit's rendered `ExecStart`, on the unit's own `PATH` with
# the stub in front. The one edit is the unit's `/run/<unit>/` prefix, moved
@ -41,6 +42,9 @@ let
"${prefix}_BAO_ADDR" = u.environment.BAO_ADDR;
};
# The `gh` wrapper and the git credential helper ./github.nix installs.
systemPackage = n: lib.findFirst (p: (p.name or "") == n) null machine.environment.systemPackages;
# Answers `login` and `kv get` from `FAKE_BAO_*` variables and logs every
# call. A `kv` call without the token `login` printed fails, so a script that
# drops `BAO_TOKEN` between the two cannot pass. A login presenting the
@ -76,10 +80,13 @@ in
pkgs.runCommand "hyperhive-script-test-agent-bao-fetch"
(
unitEnv "FORGE" "hive-agent-forge-token"
// unitEnv "GITHUB" "hive-agent-github-token"
// unitEnv "IDENT" "hive-agent-bao-identity"
// unitEnv "PROXY" "hive-agent-proxy-credential"
// {
FAKE_BAO_BIN = "${fakeBao}/bin";
GH_WRAPPER = "${systemPackage "gh"}/bin/gh";
GIT_CRED_HELPER = "${systemPackage "git-credential-hive-github"}/bin/git-credential-hive-github";
}
)
''

View file

@ -593,6 +593,137 @@ go
expect_rc 0
expect_file "$rt/proxy.env" "$(proxy_url=http://a1:the-secret@10.9.8.7:3129 proxy_env localhost,127.0.0.1,::1,10.9.8.7,x.t.hive,y.t.hive)"
# The GitHub token fetch. The token goes to `$rt/token`, the unit's own
# directory under `/run`, and the unit has no other place to write it.
gh_path=secret/swarm/agents/a1/github-token
gh_kv_call="kv get -format=json $gh_path"
gh_answer() { printf '{"data":{"data":{"value":"%s"}}}' "$1"; }
setup GITHUB "a first fetch writes the token 0400 under the unit's runtime directory"
kv_out=$(gh_answer ghp_fake)
go
expect_rc 0
expect_out "fetched this agent's GitHub token"
expect_calls "$login_call" "$gh_kv_call"
expect_file "$rt/token" ghp_fake
if [ "$(stat -c %a "$rt/token")" != 400 ]; then fail "token is mode $(stat -c %a "$rt/token"), expected 400"; fi
expect_no_file "$rt/token.new"
expect_no_file "$rt/account.json"
expect_no_file "$rt/bao.err"
# The control for the next case: this script names the runtime directory.
if ! grep -qF "$rt/token" "$dir/script"; then fail "the script does not name $rt/token"; fi
setup GITHUB "the script names no state directory"
if ! grep -qF "$rt/token" "$dir/script"; then fail "the script does not name $rt/token"; fi
if grep -qF "/state" "$dir/script"; then fail "the script names a state directory"; fi
setup GITHUB "a store that does not answer at start leaves no token and exits 1 for the retry"
login_rc=2
login_err="Error authenticating: Put \"$bao_addr/v1/auth/cert/login\": dial tcp: lookup bao.t.local: no such host"
go
expect_rc 1
expect_err "this agent runs without a GitHub token until it does"
expect_err "no such host"
expect_calls "$login_call"
expect_no_file "$rt/token"
expect_no_file "$rt/token.new"
expect_no_file "$rt/account.json"
setup GITHUB "a read that fails at start leaves no token and does not fail the unit"
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 0
expect_err "this agent runs without one until the store has one"
expect_err "connection refused"
expect_calls "$login_call" "$gh_kv_call"
expect_no_file "$rt/token"
expect_no_file "$rt/account.json"
setup GITHUB "no identity delivered leaves no token and does not fail the unit"
: >"$creds/hive-agent-bao-cert"
go
expect_rc 0
expect_err "runs without a GitHub token"
expect_calls
expect_no_file "$rt/token"
setup GITHUB "an answer without a string value leaves no token"
kv_out='{"data":{"data":{"value":7}}}'
go
expect_rc 0
expect_err "holds no string value"
expect_no_file "$rt/token"
expect_no_file "$rt/token.new"
setup GITHUB "a token fetched earlier in the run stays when the store stops answering"
printf old >"$rt/token"
chmod 0400 "$rt/token"
kv_rc=2
kv_err="dial tcp: connect: connection refused"
go
expect_rc 0
expect_file "$rt/token" old
# `UMask=0377` makes every file the script creates 0400, and a redirect into
# a 0400 file fails.
setup GITHUB "0400 files left by a killed run do not block the fetch"
for f in bao.err account.json token.new; do
printf stale >"$rt/$f"
chmod 0400 "$rt/$f"
done
kv_out=$(gh_answer ghp_fake)
go
expect_rc 0
expect_out "fetched this agent's GitHub token"
expect_no_err "Permission denied"
expect_file "$rt/token" ghp_fake
expect_no_file "$rt/account.json"
setup GITHUB "an unchanged token is left in place"
printf 'ghp_fake\n' >"$rt/token"
chmod 0400 "$rt/token"
before=$(stat -c %i "$rt/token")
kv_out=$(gh_answer ghp_fake)
go
expect_rc 0
expect_out "is unchanged"
if [ "$(stat -c %i "$rt/token")" != "$before" ]; then fail "the unchanged token was replaced"; fi
setup GITHUB "a rotated token replaces the old one"
printf old >"$rt/token"
chmod 0400 "$rt/token"
kv_out=$(gh_answer ghp_new)
go
expect_rc 0
expect_out "fetched this agent's GitHub token"
expect_file "$rt/token" ghp_new
expect_no_file "$rt/token.new"
# The readers, with the token path and the state directory moved under the
# build directory the way the units' `/run` is. The state directory holds a
# token no reader may use.
setup GITHUB "the gh wrapper and the credential helper read the runtime file and not the state directory"
mkdir -p "$dir/state"
printf legacy >"$dir/state/github-token"
reader() {
sed -e "s|/run/$unit/|$rt/|g" -e "s|/agents/a1/state/|$dir/state/|g" -e 's|^exec .*|printenv GH_TOKEN|' "$1" >"$dir/reader"
chmod +x "$dir/reader"
}
reader "$GH_WRAPPER"
if ! grep -qF "$rt/token" "$dir/reader"; then fail "the gh wrapper does not name $rt/token"; fi
git_cred() { env -i PATH="$unit_path" "$dir/reader" get; }
printf ghp_fake >"$rt/token"
chmod 0400 "$rt/token"
if [ "$(env -u GH_TOKEN "$dir/reader")" != ghp_fake ]; then fail "the gh wrapper did not use the runtime token"; fi
reader "$GIT_CRED_HELPER"
if ! grep -qF "$rt/token" "$dir/reader"; then fail "the credential helper does not name $rt/token"; fi
if [ "$(git_cred)" != $'username=x-access-token\npassword=ghp_fake' ]; then fail "the credential helper did not answer with the runtime token: $(git_cred)"; fi
rm "$rt/token"
if [ -n "$(git_cred)" ]; then fail "the credential helper answered with no token fetched: $(git_cred)"; fi
reader "$GH_WRAPPER"
if [ -n "$(env -u GH_TOKEN "$dir/reader" 2>&1)" ]; then fail "the gh wrapper exported a token with none fetched"; fi
if [ "$failures" -ne 0 ]; then
echo "script-test-agent-bao-fetch: $failures failed assertions over $count runs" >&2
exit 1