リモート開発メインのソフトウェア開発企業のエンジニアブログです

herdr remote 越しで macOS を操作してると gh auth がたまに使えなくなる原因を調べた

最近 herdr を導入して作業しているのですが、macOS に herdr remote (SSH) で接続していると、リモート側で gh auth や一部のコマンドが使えなくなることがありました。

調べてみると、原因は herdr 固有の問題ではなく、macOS の Keychain とプロセスが属する launchd セッションにあると言うことがわかったので今回はそれについてまとめます。また、通常の SSH でも同じ条件で発生します。

経緯

私は普段2台の Mac を使っていて、どちらもメインマシンから herdr で操作しています。サブマシンには sub-mac という SSH Host を設定しておき、

herdr --remote sub-mac

で接続しています。sub-mac 側にも herdr をインストールしているので、手元の Mac とほぼ同じ操作感でリモート環境を扱えます。

ある時、sub-mac 上で git fetch したところ、github.com への認証が通らなくなりました。

Git の認証には GitHub CLI (gh) の credential helper を使っていたので、まず gh auth status を確認します。

 » gh auth status
github.com
  X Failed to log in to github.com account issei-m (default)
  - Active account: true
  - The token in default is invalid.
  - To re-authenticate, run: gh auth login -h github.com
  - To forget about this account, run: gh auth logout -h github.com -u issei-m

一見するとトークンが無効になったように見えます。

ところが、Screen Sharing で sub-mac に入り、GUI のターミナルから同じコマンドを実行すると正常です。

 » gh auth status
github.com
  ✓ Logged in to github.com account issei-m (keyring)
  - Active account: true
  - Git operations protocol: https
  - Token: gho_************************************
  - Token scopes: 'gist', 'read:org', 'repo', 'workflow'

さらに奇妙なのが、別の日には herdr remote 越しでも普通に認証できることでした。つまり、トークンそのものが失効しているわけではなさそうです。

原因

原因は macOS の Keychain と launchd のセッションでした。

GitHub CLI は macOS ではデフォルトで認証トークンを Keychain に保存します。~/.config/gh/hosts.yml にはアカウントなどの設定は残りますが、secure storage を使っている場合、トークン本体は Keychain 側に保存されます。

ここで問題になるのが、コマンドをどのコンテキストから起動したかです。現在の launchd コンテキストは、次のコマンドで確認できます。

launchctl managername

Ghostty など、GUI から起動したターミナルで実行すると Aqua になります。

 » launchctl managername
Aqua

一方、SSH で接続したシェルでは Background になります。

 » launchctl managername
Background

Aqua は GUI ログインセッション側のコンテキストで、Background は SSH などから作られる非 GUI 側のコンテキストです。

このコンテキストは通常、そのプロセスから起動した子プロセスにも引き継がれます。

たとえば Ghostty (Aqua) から zsh、さらに gh を起動すれば、それらも Aqua 側で動きます。一方、SSH の sshd (Background) を起点にした場合、その下で起動するプロセスも Background 側になります。

ここで Keychain がロックされていたり、Keychain の ACL による確認が必要だったりすると、Background セッションでは GUI の確認ダイアログを表示できず、Keychain へのアクセスが失敗することがあります。代表的なエラーが errSecInteractionNotAllowed です。

今回も gh が Keychain からトークンを正常に取得できず、結果として gh auth status

The token in default is invalid.

と表示していました。エラーの内容から状況を把握するのが難しいですね・・・^^;

ともかく、これで「GUI のターミナルでは通るのに SSH 越しだと失敗する」という現象にも説明がつきます。

なお、Claude Code も同じ様なトークン管理をしているので同じ問題が発生します。

Moba Pro

解決策1: login keychain を unlock する

Keychain がロックされていることが原因であれば、SSH セッションから明示的に unlock できます。

security unlock-keychain ~/Library/Keychains/login.keychain-db

パスワードを入力すると login keychain が unlock され、その後の Keychain アクセスが可能になります。今回のように gh のトークン読み出しだけが問題であれば、これで解決です。

ただし、AWS の SSO の様なシェルからブラウザを経由したり、他にも何かしらの GUI 操作を必要とするツールで errSecInteractionNotAllowed が発生しがちです。サブマシンは Screen Sharing で GUI をすぐ操作できるので、どうせなら GUI のコンテキストで動かしたいところです。

解決策2: herdr server を Aqua セッションから起動する

自分の運用ではこちらを本命にしました。

今回「動くときと動かないときがある」理由も、herdr server がどこから起動されたかの違いでした。herdr remote はリモート側に server が起動していなければ、SSH 経由で server を起動します。

この場合、

sshd (Background)
  └─ herdr server
      └─ zsh
          └─ gh

という形になるため、herdr 内で起動したプロセスも Background 側になります。

一方、あらかじめ Ghostty など GUI のターミナルから herdr server を起動しておけば、

Ghostty.app (Aqua)
  └─ herdr server
      └─ zsh
          └─ gh

となり、herdr remote で後から接続しても server 自体は Aqua 側のコンテキストで動き続けます。

そのため、私は sub-mac 側の herdr server をあらかじめ GUI セッションから起動しておくことにしました。GUI から Ghostty.app などで herdr コマンドを実行すればサーバーも同時に起動します。

同じ考え方は tmux にも当てはまります。tmux server も最初にどのコンテキストから起動されたかによって、その後に作られる shell の実行コンテキストが変わります。

まとめ

今回の現象を整理すると、次のようになります。

  • macOS には Aqua / Background などの launchd セッションコンテキストがあり、launchctl managername で現在のコンテキストを確認できる
  • GUI ターミナルは通常 Aqua、SSH から起動した shell は Background になる
  • Background では GUI 操作を必要とする Keychain アクセスが失敗することがある
  • gh が Keychain からトークンを取得できない場合、gh auth status が「token is invalid」と表示するケースがあり、本当にトークンが失効しているとは限らない
  • Keychain がロックされているだけなら security unlock-keychain で回避できる
  • herdr を使う場合は、herdr server 自体を Aqua セッションから起動しておけば、その配下のプロセスも Aqua 側で動かせる
  • これは herdr 固有の問題ではなく、SSH や tmux など、長時間動く server process をどのコンテキストから起動するかという macOS 共通の問題

「ローカルでは認証できるのに SSH 越しだと失敗する」という現象に遭遇したら、まず launchctl managername と Keychain の状態を確認してみると原因を絞り込みやすそうです。

← 前の投稿

AWS MCP を調べてみた

次の投稿 →

コメントを残す