diff --git a/harnesses/contexts/airouter/v1/bin/anthropic-compat-wrapper.sh b/harnesses/contexts/airouter/v1/bin/anthropic-compat-wrapper.sh deleted file mode 100755 index 118dac8..0000000 --- a/harnesses/contexts/airouter/v1/bin/anthropic-compat-wrapper.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Anthropic-compat wrapper for airouter harness (Phase 9 ESO-managed secrets) -# Reads airouter credentials from /run/agent/secrets/airouter/ and passes them -# as env vars to the underlying Claude runner. -set -euo pipefail - -exec env \ - ANTHROPIC_AUTH_TOKEN="$(cat /run/agent/secrets/airouter/auth_token)" \ - ANTHROPIC_BASE_URL="$(cat /run/agent/secrets/airouter/base_url)" \ - claude "$@" \ No newline at end of file diff --git a/harnesses/contexts/airouter/v1/harness.yaml b/harnesses/contexts/airouter/v1/harness.yaml index 7e4b3d5..c67042d 100644 --- a/harnesses/contexts/airouter/v1/harness.yaml +++ b/harnesses/contexts/airouter/v1/harness.yaml @@ -7,11 +7,17 @@ provides: [agentic-runner] env: OPENAI_BASE_URL: "https://api.airouter.ch/v1" + # Agentic runner reads the api key from this file at request time. + # init.sh stages a 0600 agent-owned copy from the ESO mount to this path. + OPENAI_API_KEY_FILE: "/var/agent-secrets/airouter/api_key" secrets_required: - name: airouter account_ref: "airouter" mount_path: /run/agent/secrets/airouter + # 0400 (root-only) — defense in depth. The agent user CANNOT read this + # mount; init.sh runs as root and installs a 0600 agent-owned copy at + # OPENAI_API_KEY_FILE (above). Matches the minimax pattern. mode: "0400" scripts: diff --git a/harnesses/contexts/airouter/v1/init.sh b/harnesses/contexts/airouter/v1/init.sh index 0fc4919..9647818 100755 --- a/harnesses/contexts/airouter/v1/init.sh +++ b/harnesses/contexts/airouter/v1/init.sh @@ -1,29 +1,39 @@ #!/bin/bash -# airouter init — Phase 9 ESO-managed secret mount. +# airouter init — Phase 9 ESO-managed secret + agentic-runner staging. # -# Verifies the ESO-mounted secret files exist; the wrapper -# (`bin/anthropic-compat-wrapper.sh`) reads them at exec time. +# The airouter ESO ExternalSecret materialises a single key, `api_key`, +# matching the airouter provider schema. The previous version of this +# script verified `auth_token` + `base_url` (Anthropic-compat shape, dead +# code per the M22 Phase 9 cleanup) and never matched a real ESO Secret. # -# Per H-SECRET-4: NO `export` of credentials here. The earlier draft of this -# file exported ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL from this script, -# which runs as root under uid-wrapper.sh — even though the export was -# subshell-scoped, the secret was briefly resident in /proc//environ of -# a root process. The wrapper's `exec env VAR=...` pattern is the only -# acceptable credential delivery point. +# Threat model: ESO mount is root-only (mode 0400) — agent CANNOT read +# /run/agent/secrets/airouter/api_key directly. init.sh runs as root +# (under uid-wrapper.sh, before the gosu drop) and installs a per-agent +# copy of the api_key at a fixed path the agentic runner reads via +# OPENAI_API_KEY_FILE. Same pattern as minimax/v1/init.sh. +# +# Per H-SECRET-4: no `export` of the credential value here — the staged +# file path is referenced from harness env (OPENAI_API_KEY_FILE), and the +# agentic runner reads the file at request time. set -euo pipefail -SECRETS_DIR="/run/agent/secrets/airouter" +ESO_API_KEY="/run/agent/secrets/airouter/api_key" -if [ ! -d "$SECRETS_DIR" ]; then - echo "ERROR: Secret directory $SECRETS_DIR not found. ESO mount may have failed." >&2 +if [ ! -r "$ESO_API_KEY" ]; then + echo "ERROR: $ESO_API_KEY not readable. Check ESO ExternalSecret acct-." >&2 exit 1 fi -for f in auth_token base_url; do - if [ ! -r "$SECRETS_DIR/$f" ]; then - echo "ERROR: $SECRETS_DIR/$f not readable. Check ESO ExternalSecret for airouter." >&2 - exit 1 - fi -done +AGENT_USER="${AGENT_USER:-agent}" -echo "airouter secrets verified at $SECRETS_DIR" +# Stage to a fixed path that harness.yaml env can reference. Outside the +# read-only ESO mount so we can set ownership/mode. +STAGED_DIR="/var/agent-secrets/airouter" +STAGED_KEY="$STAGED_DIR/api_key" +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_API_KEY" "$STAGED_KEY" + +echo "airouter api_key staged at $STAGED_KEY (0600 $AGENT_USER:$AGENT_USER)" +echo "airouter OPENAI_API_KEY_FILE set via harness.yaml env"