1380 字
7 分鐘
實測 boxd.sh 的 GitHub Token 注入:gh 走 cache,git 走 metadata

最近在替 mikan 設計 secret 注入機制時遇到一個問題。

mikan 是一個 coding agent,會在隔離的 sandbox 環境裡執行使用者交辦的任務。這類 agent 幾乎都需要存取外部服務:push 程式碼到 GitHub、呼叫部署 API、讀取私有套件庫……這些操作都需要帶 credential(認證金鑰)。

問題是:credential 要怎麼安全地交到 sandbox 手上?

最直覺的做法是直接把 token 塞進環境變數(export GITHUB_TOKEN=xxx),但環境變數會被所有子 process 繼承,agent spawn 出來的每個工具、腳本、甚至 env 指令都能看到它。另一個選項是把 token 寫進 workspace,但 agent 一不小心就可能讀到、commit 進去,或印到 log 裡。

所以我開始看其他 sandbox runtime 怎麼解這個問題。

boxd.sh 是什麼#

exe.devboxd.sh 都是提供持久性 Linux VM 的服務,面向開發者和 AI agent。兩者都能透過 SSH 連進去,VM 的狀態會保留,重新連線時環境還在。功能定位相近,是直接競品。

這類服務通常會替使用者預先設定好 GitHub 授權,讓 VM 裡的 ghgit 指令不需要手動登入就能直接存取 GitHub。

我想弄清楚的是:它怎麼把 GitHub token 交給 VM?


連進免費試用的 VM 之後,我原本以為 token 只從一個地方來。實測後要修正:

boxd credential injection flow

gh CLI 走 /run/boxd/github-token 這個本地 cache;git 的 credential helper 則直接向 metadata API 取 token。

這是兩條不同的整合路徑,不能混為一談。

第一條路:gh CLI#

boxd-github-token:帶 cache 的取 token 工具#

VM 裡有一個二進位工具 /usr/local/bin/boxd-github-token,工作是拿到當下有效的 GitHub token:

/usr/local/bin/boxd-github-token
CACHE=/run/boxd/github-token
mkdir -p /run/boxd
if [ -f "$CACHE" ]; then
AGE=$(( $(date +%s) - $(stat -c %Y "$CACHE" 2>/dev/null || echo 0) ))
if [ "$AGE" -lt 300 ] && [ -s "$CACHE" ]; then
cat "$CACHE" # cache hit:直接輸出檔案
exit 0
fi
fi
# cache miss:向 metadata API 拿新 token,寫回 cache 再輸出
TOKEN=$(wget -qO- http://10.20.0.1:9002/v1/github/token 2>/dev/null) || exit 1
if [ -n "$TOKEN" ]; then
printf '%s' "$TOKEN" > "$CACHE"
printf '%s' "$TOKEN"
else
exit 1
fi

邏輯:

  • cache hit/run/boxd/github-token 存在且距上次寫入不超過 300 秒(5 分鐘),直接輸出檔案內容
  • cache miss:向 http://10.20.0.1:9002/v1/github/token 取新 token,寫回 cache 再輸出

10.20.0.1:9002 是 boxd 的 metadata API——類似 AWS EC2 的 169.254.169.254,是 VM host 開放給 VM 查詢自己 metadata(包含臨時 credential)的內部端點,只有 VM 內部能打到。

NOTE

/run/boxd/github-token 本身只是一個 cache 檔案,不是 token 的來源。真正的上游是 metadata API。

gh 怎麼用這個 helper#

gh CLI 被 /etc/profile.d/boxd-github.sh 包了一層 shell function:

/etc/profile.d/boxd-github.sh
gh() {
_token=$(boxd-github-token 2>/dev/null)
if [ -z "$_token" ]; then
command gh "$@"
return $?
fi
GH_TOKEN="$_token" command gh "$@" # token 只注入這一個指令的環境
unset _token
}

也就是說,在 login shell 裡執行 gh repo view,實際的路徑是:

gh function → boxd-github-token → GH_TOKEN=<token> command gh ...

注意:GH_TOKEN 不是預先 export 在整個 session 的環境變數,而是只在執行那一個 gh command 時才臨時注入。在 shell 裡跑 env,不會看到 GH_TOKEN

第二條路:git#

Git 有一套叫做 credential helper 的機制:當 git 需要對 HTTPS URL 做認證時,它會呼叫設定好的 helper 程式,把 protocolhost 傳進去,helper 再回傳 usernamepassword

boxd 透過 /etc/gitconfig 設定讓所有 GitHub 操作都走自己的 helper:

/etc/gitconfig
[credential "https://github.com"]
helper = boxd
[url "https://github.com/"]
insteadOf = [email protected]: # SSH URL 自動轉為 HTTPS,讓 credential helper 也能接管

helper 的核心邏輯:

/usr/local/bin/git-credential-boxd
if [ "$GITHUB" = "1" ] && [ "$HTTPS" = "1" ]; then
TOKEN=$(wget -qO- http://10.20.0.1:9002/v1/github/token 2>/dev/null)
printf 'protocol=https\nhost=github.com\nusername=x-access-token\npassword=%s\n\n' "$TOKEN"
fi
NOTE

這裡沒有讀 /run/boxd/github-token,也沒有寫 cache。每次 git 需要 GitHub credential,helper 都直接打 metadata API 拿新 token。

兩條路徑的比較#

gh CLIgit over HTTPS
進入點/etc/profile.d/boxd-github.sh(shell function)/etc/gitconfig credential.helper
取 tokenboxd-github-token(有 cache)git-credential-boxd(無 cache)
cache/run/boxd/github-token,TTL 5 分鐘
上游來源metadata API(cache miss 時)metadata API(每次直接打)

這是這次實測最重要的結論:/run/boxd/github-token 不是整個 VM 唯一的 GitHub token 輸出點,它只是 gh 這條路徑的本地 cache。

值得注意的安全點#

1. /run/boxd/github-token 是 644#

drwxr-xr-x boxd boxd /run/boxd
-rw-r--r-- boxd boxd /run/boxd/github-token

Owner 是 boxd,但 mode 644 代表同機其他 user 也能讀這個檔案。如果 VM 裡只有一個使用者,影響可能有限;如果存在多個 user 或不受信任的 process,這個設計值得留意。

2. token 不在預設 env#

一般 shell session 裡,GH_TOKEN 不是常駐的環境變數。這比直接 export GITHUB_TOKEN=xxx 好:token 不會被所有子 process 自動繼承,只有在 gh function 執行的那一刻才存在於環境中。

3. ghgit 行為不同,容易誤判#

只看 /run/boxd/github-token 這個檔案,只能理解 gh 這條路徑。git clonegit push 走的是另一條完全獨立的機制,不會經過這個 cache。

4. log 洩漏仍然是最大的攻擊面#

不管 token 從 cache 來還是 metadata API 來,最後都會進到某個指令的環境或 credential response 裡。

WARNING

最直接的風險是把 token 印出來。常見的觸發方式:

Terminal window
set -x # 會把每個指令和它的參數都輸出到 stderr

任何會把 token 帶進 stdout / stderr / CI log / agent transcript 的操作,才是實際上最需要防守的地方。

小結#

boxd.sh 把 GitHub token 注入 VM 的方式有兩條路:

  • gh CLI:透過 shell function 呼叫 boxd-github-token,後者用 5 分鐘 cache 減少 metadata API 請求
  • git:透過 credential helper 直接打 metadata API,每次都拿新 token

兩條路共用同一個 metadata endpoint 作為上游來源,但 cache 策略完全不同。設計 sandbox 的 credential 注入機制時,要把這兩條路分開考慮,不能只看其中一條。

實測 boxd.sh 的 GitHub Token 注入:gh 走 cache,git 走 metadata
https://geminixiang.xyz/posts/boxd-github-token-injection/
作者
Ying Xiang Zhao
發佈於
2026-06-28
許可協議
CC BY-NC-SA 4.0