From d68d1b0f6b4bbea8d20cd846deed57e26f8328c0 Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Thu, 7 May 2026 15:42:20 +1200 Subject: [PATCH] fix(minimax,z-ai): write apiKeyHelper to agent home, mode 0444 for read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors agent-runtimes commit 166c19b. CRS serves these harness files to dispatchers, so this repo must match. Two fixes from the failing smoke test: 1. init.sh resolves the agent user's home via getent (init.sh runs as root, but claude runs as the agent user — different $HOME). 2. secrets_required mode "0400" → "0444" so the agent user can read the ESO-mounted secret via apiKeyHelper. The file is in pod-local tmpfs. Co-Authored-By: Claude Opus 4.7 --- harnesses/contexts/minimax/v1/harness.yaml | 7 +++++- harnesses/contexts/minimax/v1/init.sh | 25 ++++++++++++++++------ harnesses/contexts/z-ai/v1/harness.yaml | 5 ++++- harnesses/contexts/z-ai/v1/init.sh | 21 ++++++++++++++---- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/harnesses/contexts/minimax/v1/harness.yaml b/harnesses/contexts/minimax/v1/harness.yaml index 9789e83..21895c3 100644 --- a/harnesses/contexts/minimax/v1/harness.yaml +++ b/harnesses/contexts/minimax/v1/harness.yaml @@ -18,7 +18,12 @@ secrets_required: - name: minimax account_ref: "minimax" mount_path: /run/agent/secrets/minimax - mode: "0400" + # 0444 — readable by the agent user that runs `claude` (and thus + # apiKeyHelper). The pod has no fsGroup, so the kubelet mounts the + # secret as root:root; mode 0400 would block the legitimate read. + # The file lives in pod-local tmpfs — "world readable" only means + # readable by other processes in this same pod, which we control. + mode: "0444" scripts: init: ./init.sh diff --git a/harnesses/contexts/minimax/v1/init.sh b/harnesses/contexts/minimax/v1/init.sh index 72bb818..5e4d3ac 100755 --- a/harnesses/contexts/minimax/v1/init.sh +++ b/harnesses/contexts/minimax/v1/init.sh @@ -27,11 +27,21 @@ if [ ! -r "$API_KEY_FILE" ]; then exit 1 fi -# Resolve the agent's home and write settings.json there. CLAUDE_CONFIG_DIR -# overrides ~/.claude when set; honour it so non-default profiles work. -CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" +# init.sh runs as root in uid-wrapper.sh BEFORE gosu drops privileges to the +# agent user — so $HOME here is /root, not the agent home. Claude Code will +# run as the agent user and read its config from $AGENT_HOME/.claude. Resolve +# the agent home explicitly so settings.json lands where claude looks. +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 + +# CLAUDE_CONFIG_DIR overrides ~/.claude when set. +CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$AGENT_HOME/.claude}" mkdir -p "$CONFIG_DIR" -chmod 0700 "$CONFIG_DIR" +chown "$AGENT_USER:" "$CONFIG_DIR" 2>/dev/null || true +chmod 0755 "$CONFIG_DIR" SETTINGS_FILE="$CONFIG_DIR/settings.json" @@ -52,5 +62,8 @@ else EOF fi -chmod 0600 "$SETTINGS_FILE" -echo "minimax api_key wired via apiKeyHelper at $SETTINGS_FILE" +# settings.json holds the helper command (a path), not a credential value. +# It must be readable by the agent user that runs claude. +chown "$AGENT_USER:" "$SETTINGS_FILE" 2>/dev/null || true +chmod 0644 "$SETTINGS_FILE" +echo "minimax api_key wired via apiKeyHelper at $SETTINGS_FILE (agent_user=$AGENT_USER)" diff --git a/harnesses/contexts/z-ai/v1/harness.yaml b/harnesses/contexts/z-ai/v1/harness.yaml index cb9ca62..8d4dca5 100644 --- a/harnesses/contexts/z-ai/v1/harness.yaml +++ b/harnesses/contexts/z-ai/v1/harness.yaml @@ -15,7 +15,10 @@ secrets_required: - name: z-ai account_ref: "z-ai" mount_path: /run/agent/secrets/z-ai - mode: "0400" + # 0444 — readable by the agent user that runs `claude` (and thus + # apiKeyHelper). Pod-local tmpfs; "world readable" only means readable + # by processes in this pod. + mode: "0444" scripts: init: "./init.sh" diff --git a/harnesses/contexts/z-ai/v1/init.sh b/harnesses/contexts/z-ai/v1/init.sh index e842327..46620fe 100755 --- a/harnesses/contexts/z-ai/v1/init.sh +++ b/harnesses/contexts/z-ai/v1/init.sh @@ -27,9 +27,19 @@ if [ ! -r "$API_KEY_FILE" ]; then exit 1 fi -CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" +# init.sh runs as root in uid-wrapper.sh BEFORE gosu drops privileges to the +# agent user. $HOME here is /root, not the agent home — so resolve the agent +# user's home explicitly and write settings.json there. +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 + +CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$AGENT_HOME/.claude}" mkdir -p "$CONFIG_DIR" -chmod 0700 "$CONFIG_DIR" +chown "$AGENT_USER:" "$CONFIG_DIR" 2>/dev/null || true +chmod 0755 "$CONFIG_DIR" SETTINGS_FILE="$CONFIG_DIR/settings.json" @@ -49,5 +59,8 @@ else EOF fi -chmod 0600 "$SETTINGS_FILE" -echo "z-ai auth_token wired via apiKeyHelper at $SETTINGS_FILE" +# settings.json holds the helper command (a path), not a credential value. +# It must be readable by the agent user. +chown "$AGENT_USER:" "$SETTINGS_FILE" 2>/dev/null || true +chmod 0644 "$SETTINGS_FILE" +echo "z-ai auth_token wired via apiKeyHelper at $SETTINGS_FILE (agent_user=$AGENT_USER)"