最近在家中的 mac mini 上部署了一些長時間執行的工作,習慣從筆電 SSH 進去操作,但一啟動 Claude Code 就出現要求 /login 的畫面,明明前一晚在 mac mini 前面用 Terminal 登入過、也正常用到一半才離開;換成 ssh 進來後從 tmux attach 回原本的 session,偶爾也會出現一樣的未登入狀況(機率比直接 ssh 低一些,但確實會發生)。一開始以為是 Claude Code 的 bug,測了幾次發現只要下一行 security unlock-keychain 就恢復正常。這其實不是 Claude Code 的問題,而是 macOS login keychain 的行為特性,任何透過 Keychain 存 token、憑證的工具都可能踩到。

先給結論:一行指令解決

SSH 進到 mac 後,輸入下面這行,它會提示我們輸入登入密碼,成功後再重新啟動 Claude Code 就能直接使用,不需要重新 /login

# 解鎖目前使用者的 login keychain
security unlock-keychain ~/Library/Keychains/login.keychain-db

# 也可以省略路徑,預設就是 login keychain
security unlock-keychain

如果想確認目前狀態,可以先用 security show-keychain-info 看閒置自動鎖定秒數,或用 security list-keychains 列出目前被掛載的 keychain。

為什麼 SSH 進來就會發生

macOS 的 login keychain 是加密檔案,裡面存著各種應用程式寫入的密碼、token、憑證。它的加密金鑰是使用者的登入密碼,所以必須「解鎖」才能讀寫內容。解鎖的時機通常是:在 mac 實體螢幕前輸入帳號密碼登入桌面時,由 loginwindow 自動用同一份密碼解鎖 login keychain;之後只要沒有登出、沒有睡眠到觸發自動鎖定,同一個使用者啟動的任何程式都能透過 Keychain Services API 讀到裡面的資料。

問題就出在 SSH 不走 loginwindow 這條路。sshd 透過 PAM 驗證帳號密碼或公鑰後就直接開一個 shell,中間完全沒有觸發 Keychain 解鎖的動作。於是 SSH session 雖然是正確的使用者身分,Claude Code 去讀 keychain 裡的登入 token 時,會因為 keychain 是鎖著的而讀不到,表現出來就是「要求重新 /login」。

至於 tmux 為什麼偶爾才會踩到:tmux server 是一個常駐行程,通常是最早那次在 GUI Terminal 啟動的,那時候 keychain 已經解鎖,所以從其他地方 attach 回來的 session 也能沿用解鎖狀態。但只要中途 keychain 被鎖上——例如閒置超過 show-keychain-info 顯示的秒數、手動上鎖、進入睡眠後依設定被鎖、或者 tmux server 整個重啟——再 attach 時就會跟純 SSH 一樣讀不到 token。

為什麼 security unlock-keychain 有效

security 是 macOS 內建操作 Keychain Services 的 CLI,unlock-keychain 會把指定的 keychain(預設是 login keychain)用密碼解鎖。解鎖是行程間共享的狀態——不是只對目前這個 shell 生效,而是對「目前這個使用者」的所有後續行程生效,所以我們在 SSH session 裡解鎖完,再啟動的 Claude Code 就讀得到 token 了。

幾個延伸指令:

# 查詢 keychain 自動鎖定設定(秒數、是否睡眠鎖)
security show-keychain-info ~/Library/Keychains/login.keychain-db

# 把閒置自動鎖定時間改成 2 小時、睡眠時不鎖
security set-keychain-settings -t 7200 ~/Library/Keychains/login.keychain-db

# 反向操作:手動上鎖
security lock-keychain ~/Library/Keychains/login.keychain-db

# 用 -p 一次帶密碼(會進 shell history,不建議)
security unlock-keychain -p 'your-password' ~/Library/Keychains/login.keychain-db

-p 搭配明文密碼很危險,會留在 shell history 與行程列表裡,建議改用互動輸入,或把指令包進讀取受保護檔案的 script,再透過 stdin 送進去。

不只 Claude Code 會踩到

只要是把 token、密碼、憑證存在 login keychain 的工具,在 SSH 或 keychain 被鎖定的 tmux session 裡都有可能出現同樣症狀。下面這張表整理常見會踩的工具,Google 搜尋遇到類似問題的朋友也許會比較容易找到這篇:

工具 / 情境踩到時的症狀備註
Claude Code啟動後顯示 /login,或 agent 呼叫 API 直接 401token 存於 login keychain
GitHub CLI(gh)gh auth status 顯示 not logged in、gh auth token 取不到若使用 --secure-storage 或 macOS 預設
git + osxkeychain helperHTTPS push/pull 一直跳出提示要密碼git config --global credential.helper osxkeychain
Docker Desktop / docker logindocker pull 私有映像 401 unauthorizedcredentials store 走 osxkeychain
AWS CLI / aws-vaultprofile 找不到 credentials、MFA token 無法讀aws-vault 預設用 keychain 當 backend
npm / yarn / pnpmnpm publish 401、registry auth token 讀不到走 keytar 的 Electron 工具同理
1Password CLI(op)session 失效、無法以生物辨識解鎖biometric unlock 需 GUI 協助
Xcode codesign / notarytool簽署或公證時找不到開發者憑證、errSecInternalComponentCI 用 SSH build 時最常見
fastlane match匯入憑證失敗或反覆要求密碼官方文件建議 CI 先 unlock-keychain
VS Code / Cursor Remote-SSHGitHub、Copilot、同步登入狀態遺失遠端那端的 Electron 讀不到 keychain
其他 Electron CLI(keytar)五花八門的 token 失效pnpm、Slack CLI、Firebase CLI 等

一勞永逸的幾種做法

做一個方便的 alias

如果只是想少打字,加到 ~/.zshrc 就好,遇到再手動解鎖:

alias unlock='security unlock-keychain ~/Library/Keychains/login.keychain-db'

拉長自動鎖定時間

若 mac mini 是自己專用的內網機器,可以把閒置鎖定時間拉長、或關閉睡眠時鎖定,減少觸發機率。注意這會降低安全性,公用環境不建議。

# -t 秒數:閒置多久鎖;不加 -l 代表不因睡眠自動鎖
security set-keychain-settings -t 28800 ~/Library/Keychains/login.keychain-db

讓 mac 維持 GUI 登入狀態

拿 mac 當伺服器用時,建議在「系統設定 → 使用者」打開自動登入,讓 loginwindow 開機後就先幫我們解鎖 login keychain,之後所有 SSH session 都能直接共享解鎖狀態。螢幕鎖定與 keychain 鎖定預設是兩件事,但閒置秒數與睡眠設定會互相影響,搭配上面那一行 set-keychain-settings 會比較穩。

SSH 時自動解鎖(要小心)

也可以在 ~/.zprofile 裡用 expect 或讀取受權限保護的檔案自動解鎖。這等於把登入密碼放在磁碟上,風險跟 keychain 原本要保護的東西等價,實務上除非是高度可控的單人機器,不然不建議。我們用過的折衷是:把密碼存在另一台機器上的 1Password,SSH 時用 op read 取出後 pipe 給 security unlock-keychain,讓密碼只在記憶體裡過一下。

小結

一句話記住:login keychain 只有 GUI 登入才會自動解鎖,SSH 不會。只要是透過 macOS Keychain 存 token 或憑證的工具,都有機會在 SSH、部分 tmux session 裡表現成「未登入/憑證失效」。遇到就先 security unlock-keychain,再搭配自動鎖定時間調整或自動登入等作法降低發生頻率。之後如果還想進一步了解 tmux session 的運作,可以參考我們寫過的 tmux 終端機多工教學

發佈留言