fix(airouter): match ESO Secret schema (api_key) + drop dead wrapper
The airouter ESO ExternalSecret materialises a single key `api_key` (matching the provider schema in agent-runtimes M22 Phase 8e cutover, acct-59b7fb0b). The harness init script was checking for `auth_token` + `base_url` (an Anthropic-compat shape that never existed in real Vault state) and failing on every dispatch: ERROR: /run/agent/secrets/airouter/auth_token not readable. Check ESO ExternalSecret for airouter. Surfaced as the second blocker for the M16 Wave A1 dogfood (the first was the airouter dispatcher missing CRS sync; that fix went into agent-runtimes-deploy@0f11cd1). Same shape of bug as the minimax + gitea-ssh init scripts that landed during the same M22 phase — those were fixed at the time, airouter was not. Changes: - init.sh: verify the single `api_key` file (root-only, 0400 ESO mount). Stage to /var/agent-secrets/airouter/api_key with mode 0600 agent-owned (mirrors minimax pattern). H-SECRET-4 compliant — no exports. - harness.yaml: add OPENAI_API_KEY_FILE pointing at the staged path. Agentic runner reads the file at request time per entrypoint/runners/agentic.py:146 (OPENAI_API_KEY_FILE precedence). - Delete dead bin/anthropic-compat-wrapper.sh — confirmed unused per agent-runtimes/memory/log/2026-05-07.214249.md (post-M22-Phase-9 cleanup found these per-provider wrappers were never invoked; runner only prepends /opt/agent/claude-wrapper.sh). CRS picks this up automatically on next CP poll; no agent-runtimes image rebuild needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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 "$@"
|
|
||||||
@@ -7,11 +7,17 @@ provides: [agentic-runner]
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
OPENAI_BASE_URL: "https://api.airouter.ch/v1"
|
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:
|
secrets_required:
|
||||||
- name: airouter
|
- name: airouter
|
||||||
account_ref: "airouter"
|
account_ref: "airouter"
|
||||||
mount_path: /run/agent/secrets/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"
|
mode: "0400"
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
|
|||||||
@@ -1,29 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/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
|
# The airouter ESO ExternalSecret materialises a single key, `api_key`,
|
||||||
# (`bin/anthropic-compat-wrapper.sh`) reads them at exec time.
|
# 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
|
# Threat model: ESO mount is root-only (mode 0400) — agent CANNOT read
|
||||||
# file exported ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL from this script,
|
# /run/agent/secrets/airouter/api_key directly. init.sh runs as root
|
||||||
# which runs as root under uid-wrapper.sh — even though the export was
|
# (under uid-wrapper.sh, before the gosu drop) and installs a per-agent
|
||||||
# subshell-scoped, the secret was briefly resident in /proc/<pid>/environ of
|
# copy of the api_key at a fixed path the agentic runner reads via
|
||||||
# a root process. The wrapper's `exec env VAR=...` pattern is the only
|
# OPENAI_API_KEY_FILE. Same pattern as minimax/v1/init.sh.
|
||||||
# acceptable credential delivery point.
|
#
|
||||||
|
# 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
|
set -euo pipefail
|
||||||
|
|
||||||
SECRETS_DIR="/run/agent/secrets/airouter"
|
ESO_API_KEY="/run/agent/secrets/airouter/api_key"
|
||||||
|
|
||||||
if [ ! -d "$SECRETS_DIR" ]; then
|
if [ ! -r "$ESO_API_KEY" ]; then
|
||||||
echo "ERROR: Secret directory $SECRETS_DIR not found. ESO mount may have failed." >&2
|
echo "ERROR: $ESO_API_KEY not readable. Check ESO ExternalSecret acct-<airouter-id>." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for f in auth_token base_url; do
|
AGENT_USER="${AGENT_USER:-agent}"
|
||||||
if [ ! -r "$SECRETS_DIR/$f" ]; then
|
|
||||||
echo "ERROR: $SECRETS_DIR/$f not readable. Check ESO ExternalSecret for airouter." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user