AI

Claude Codeのsandboxが1Password SSHエージェントのUnixソケットを塞ぎ署名が失敗する

  • Claude Code
  • sandbox
  • 1Password
  • SSH
  • git

Claude Codeのsandboxed Bashから、1PasswordのSSHエージェントで署名するgit commitを実行すると失敗します。原因はUnixソケットへの接続がsandboxのネットワーク制限で遮断されていることで、sandboxを無効化しなくてもsandbox.network.allowUnixSocketsにエージェントのソケットパスを追加すれば解決します。

{
  "sandbox": {
    "network": {
      "allowUnixSockets": [
        "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
      ]
    }
  }
}

この記事では、原因の切り分け方と、sandboxそのものを外す対処との違いを扱います。

症状

gpg.format=sshと1PasswordのSSHエージェントで署名付きコミットをしている環境で、Claude CodeのsandboxedなBashからgit commitを実行すると次のエラーで失敗します。

error: 1Password: failed to fill whole buffer
fatal: failed to write commit object

条件によっては次のエラーになることもあります。

error: 1Password: agent returned an error

サブエージェント経由で実行した場合には「account is not signed in」という表示が出ることもありました。1Passwordには実際にサインインしたままだったので、この表示はソケットに到達できないことによる誤表示です。

もっとも分かりやすい手がかりは、同じコマンドをsandboxを無効化した状態で実行すると必ず成功したことでした。1Password側の設定や鍵そのものに問題があるのではなく、sandboxを経由するかどうかだけで結果が変わっています。

sandboxのネットワーク遮断を疑う

Claude Codeのsandboxed Bashは、ファイルシステムとネットワークの両方を既定で制限する仕組みです。許可していないドメインへの通信だけでなく、許可していないUnixドメインソケットへの接続も対象になります。1PasswordのSSHエージェントは~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sockのようなパスにUnixソケットを開いて待ち受けており、~/.ssh/configIdentityAgentにこのパスを指定することでgitの署名処理がここに接続します。

sandboxが遮断しているのはファイルの読み書きではなく、このソケットへの接続そのものです。~/.ssh/config自体は読めていても、そこに書かれたソケットパスへ実際につなぎに行った時点で拒否されます。エラーメッセージが1Password側の文言のまま出てくるので、最初はSSHの設定ミスや1Password側の不具合に見えますが、実体はsandbox層での遮断でした。

署名処理の経路。git commitからssh-keygenを経てIdentityAgentのソケットへ接続する段階でsandboxのネットワーク制限に遮断され、1Passwordには届かない

allowUnixSocketsで許可し動作を確認する

冒頭に載せた設定をsettings.jsonに追加します。設定を反映したうえで、段階的に動作を確認しました。まず鍵の一覧取得です。

SSH_AUTH_SOCK="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" ssh-add -l

エージェント経由で鍵が正しく取得できることを確認したら、次はコミット署名と同じコードパスを直接試します。

SSH_AUTH_SOCK="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" \
  ssh-keygen -Y sign -n git -f ~/.ssh/id_ed25519.pub

ここまで成功したのを確認してから、sandboxed Bashのまま実際にgit commitを実行し、署名が通ることと、コミットオブジェクトのgpgsigヘッダーにSSHシグネチャが正しく埋め込まれていることを確認しました。

設定が効かないという報告もある

allowUnixSocketsは万能ではありません。Claude Codeの公開リポジトリには、同じ設定を書いてもソケットへの接続が「Operation not permitted」で拒否され続けるというIssueがあります。ソケットの中継を担う仕組み自体の説明不足を指摘する別のIssueもあり、フォワーダーが何をきっかけに起動するのか、socatをユーザー側で叩く必要があるのか、ソケット側にどんな前提条件が要るのかがドキュメント化されておらず、許可リストに登録済みのソケットでも接続できない再現例が添えられています。

手元の環境では設定を反映して即座に解決しましたが、バージョンによって挙動が変わりうる設定だという前提で、反映後はssh-add -lのような軽い確認コマンドで実際につながっているかを確かめてから本番の操作に進むことをおすすめします。

sandboxを外す選択肢を取らなかった理由

同じ症状を調べると、gitsshのコマンドそのものをexcludedCommandsでsandboxの対象から外す、あるいはallowUnsandboxedCommandsでsandbox化されていない実行を許可する、という対処もよく見かけます。動作はしますが、この対処は該当コマンドについてsandboxの保護をまるごと外します。ネットワークの接続先だけでなく、ファイルシステムへの読み書き制限も一緒になくなるということです。

allowUnixSocketsなら、そのソケットパス1本だけを許可し、それ以外のファイルアクセスやネットワーク制限はそのまま残せます。1Passwordのエージェントに接続したいだけなら、必要なのはソケット1本の許可であって、gitsshを丸ごとsandboxの外に出す理由にはなりません。許可する範囲を絞っておいた分だけ、あとから設定を見直すときに確認する対象も少なくて済みます。

excludedCommandsとallowUnixSocketsで開く範囲の違い。excludedCommandsはファイルシステムとネットワークの両方の制限をまとめて無効にするが、allowUnixSocketsはソケット1本への経路だけを開き、他の制限は残る

ただし、allowUnixSocketsに登録したソケットなら何でも安全というわけではありません。公式ドキュメントは/var/run/docker.sockのような強い権限を持つソケットを例に挙げ、そういったソケットへのアクセスを許可すると結果的にホスト側への抜け道になりうると注意しています。1Passwordのエージェントソケットのように、用途が署名や鍵の取得に限られるものを個別に許可するのと、任意のシステムサービスへの経路を開けてしまうのとでは意味が違います。

Linux・WSL2での挙動は未確認

ここまでの検証はmacOS上のsandboxed Bashで行っています。Linux・WSL2はbubblewrapでファイルシステムの分離を実装しており、macOSのSeatbeltフレームワークとは別の仕組みです。Unixドメインソケットの遮断を担うのはbubblewrap本体ではなく、任意でインストールするseccompフィルター(npm install -g @anthropic-ai/sandbox-runtime)側で、このフィルターが入っていない環境ではソケットの遮断自体が働きません。本文で引いたIssue #48396の再現例もLinux環境(bubblewrapとsocat)によるもので、allowUnixSocketsの挙動を確かめる際はseccompフィルターが導入済みかどうかも合わせて確認が要ります。同じ設定キーが同じように機能するかは、macOS以外の環境ではまだ確認できていません。