fix(harnesses): stage tokens for agent UID, fix three broken auth paths

Mirrors agent-runtimes audit + fix. All three M22 Phase 9 wrapper-script
auth paths were broken since the cutover: each one ran as the agent UID
trying to `cat` a 0400 root-only ESO mount. Same root cause we hit on
minimax/z-ai earlier today.

Changes:

1. anthropic-cloud-paul-oauth/v1 (NEW in framework)
   - Mirrors agent-runtimes — was previously only present there.
   - init.sh stages oauth_token into ~/.claude/.credentials.json (Claude
     Code's native subscription-OAuth schema). No env, no apiKeyHelper,
     no wrapper. Restores the equivalent of what harness_init.py used to
     do for the legacy SOPS path.
   - The legacy `bin/anthropic-wrapper.sh` was dead code (never wired).

2. gitea-https/v1
   - init.sh stages the token to $HOME/.config/git/gitea-https-token
     (0600 agent:agent) and points the per-host git credential helper at
     the staged copy. Previously the helper `cat`d the ESO mount path
     and silently failed at every git invocation.

3. gitea-admin/v1
   - init.sh stages the token to $HOME/.config/gitea-admin/token
     (0600 agent:agent). Wrapper updated to read from the staged copy.
   - Removes stale `requires: anthropic-cloud/v1` (the only anthropic
     harness in agent-runtimes is anthropic-cloud-paul-oauth/v1).

Pattern matches gitea-ssh / minimax / z-ai: ESO mount stays root-only,
init.sh runs as root and `install -m 0600 -o agent -g agent`s a single
explicit copy. Per-secret enumeration; future ESO Secret keys remain
inaccessible by default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Paul O'Reilly
2026-05-07 21:05:33 +12:00
parent 12c2172835
commit f8c8805f26
6 changed files with 221 additions and 39 deletions

View File

@@ -1,31 +1,69 @@
#!/bin/bash
# Configure git to use git credential helper for gitea.oreillyit.nz via HTTPS.
# gitea-https init — stage the token for the agent user and configure the
# git credential helper to read from the staged copy.
#
# The token is read from /run/agent/secrets/gitea-https/token at every git
# invocation (not at init time). This avoids the token appearing in logs.
# Threat model: keep the ESO mount root-only (mode 0400). init.sh runs as
# root and `install`s a per-secret 0600 agent-owned copy at
# $AGENT_HOME/.config/git/gitea-https-token. The git credential helper
# reads from the staged copy at every git invocation; the ESO mount path
# is never accessed by the agent.
#
# This was previously a `cat /run/agent/secrets/gitea-https/token` from
# inside an init.sh-generated helper script, which silently failed because
# the helper runs as agent and the ESO file is root:root mode 0400. The
# stage-and-helper-points-at-stage pattern matches gitea-ssh / minimax /
# z-ai / anthropic-cloud-paul-oauth.
#
# Rotation handling: per-container init. Ephemeral container agents
# always pick up the latest mounted token. Long-running sessions need a
# future scripts.control_loop hook to re-stage between git invocations.
set -euo pipefail
ESO_TOKEN="/run/agent/secrets/gitea-https/token"
if [ ! -r "$ESO_TOKEN" ]; then
echo "ERROR: $ESO_TOKEN not readable. Check ESO ExternalSecret acct-<gitea-https-id>." >&2
exit 1
fi
AGENT_USER="${AGENT_USER:-agent}"
AGENT_HOME=$(getent passwd "$AGENT_USER" | cut -d: -f6)
if [ -z "$AGENT_HOME" ] || [ ! -d "$AGENT_HOME" ]; then
AGENT_HOME="/home/$AGENT_USER"
fi
# Stage the token into a per-secret path owned by agent, mode 0600.
STAGED_DIR="$AGENT_HOME/.config/git"
STAGED_TOKEN="$STAGED_DIR/gitea-https-token"
mkdir -p "$STAGED_DIR"
chown "$AGENT_USER:" "$STAGED_DIR" 2>/dev/null || true
chmod 0700 "$STAGED_DIR"
install -m 0600 -o "$AGENT_USER" -g "$AGENT_USER" "$ESO_TOKEN" "$STAGED_TOKEN"
# Generate the credential helper. It reads from the STAGED copy, not the
# ESO mount, so it works under the agent's UID.
CRED_HELPER="/opt/harness/contexts/gitea-https/v1/git-credential-gitea.sh"
# Create the credential helper script
cat > "$CRED_HELPER" << 'HELPER_EOF'
cat > "$CRED_HELPER" <<HELPER_EOF
#!/bin/bash
# Git credential helper that provides the token from a mounted file
# for gitea.oreillyit.nz.
# This reads the file at EVERY git invocation, not at init time.
# Git credential helper for gitea.oreillyit.nz — reads the agent-staged
# token. The ESO mount itself is root-only (0400); this helper would fail
# if pointed at it directly.
set -euo pipefail
SECRET_FILE=/run/agent/secrets/gitea-https/token
[ -r "$SECRET_FILE" ] || exit 1
TOKEN_FILE=$STAGED_TOKEN
[ -r "\$TOKEN_FILE" ] || exit 1
echo "protocol=https"
echo "host=gitea.oreillyit.nz"
echo "username=token"
echo "password=$(cat $SECRET_FILE)"
echo "password=\$(cat "\$TOKEN_FILE")"
HELPER_EOF
chmod +x "$CRED_HELPER"
# Configure git to use this credential helper for gitea.oreillyit.nz
git config --global credential.https://gitea.oreillyit.nz.helper "$CRED_HELPER"
# Configure git globally (per-host) to use the helper for gitea.oreillyit.nz.
# Run as agent so ~/.gitconfig is owned correctly; otherwise root would
# write into /root/.gitconfig and the agent's git wouldn't see it.
su - "$AGENT_USER" -c "git config --global credential.https://gitea.oreillyit.nz.helper '$CRED_HELPER'"
echo "Git HTTPS credential helper configured for gitea.oreillyit.nz"
echo "gitea-https: token staged at $STAGED_TOKEN (0600 $AGENT_USER:$AGENT_USER)"
echo "gitea-https: credential helper configured for gitea.oreillyit.nz"