Claude Media
Claude CodeのコミットにSSH署名を付ける — パスフレーズで止まらない構成

Claude CodeのコミットにSSH署名を付ける — パスフレーズで止まらない構成

gpg.format=sshでコミットに署名し、鍵をssh-agentに載せておけばClaude Codeの実行中にパスフレーズ入力で止まりません。sandboxで署名が通る条件も示します。

Claude Codeにgit commitを任せるとき、コミットにSSH署名を付けるのはgit側の設定だけで済みます。gpg.formatをsshにして、署名に使う公開鍵を指定し、commit.gpgSignを有効にします。

詰まりやすいのはその先です。署名のたびに鍵のパスフレーズを聞かれるとClaude Codeの実行が止まり、sandboxを有効にしていると、鍵やエージェントに手が届かず署名が失敗します。この記事は、設定の最小形と、止まらない構成にする条件を順に扱います。

最小の設定:3つのキーでSSH署名になる

SSH署名はGit 2.34以降で使えます。GitHubのドキュメントも、SSH署名の検証にはGit 2.34以降が必要としています。設定するのは次の3つです。

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgSign true

鍵のファイル名は環境ごとに違います。ここではid_ed25519.pubを例にしています。

  • gpg.formatは署名の形式です。既定はopenpgpで、x509とsshを選べます
  • user.signingkeyには、gpg.formatがsshのとき、秘密鍵のパスか、エージェントを使う場合は公開鍵のパスを書けます
  • commit.gpgSignをtrueにすると、すべてのコミットに署名します。コマンド側で付けるならgit commit -Sで、署名を外すのは--no-gpg-signです

Claude Codeが実行するgit commitも、同じgitの設定を読みます。commit.gpgSignがtrueなら、Claudeが-Sを付けなくても署名されます。署名の有無はgitの設定で決まるので、CLAUDE.mdに「署名すること」と書くより、設定で揃えるほうが確実です。

署名とattributionは別の話

コミット本文のCo-Authored-Byを消したい、という話はここの対象ではありません。あれはsettings.jsonのattributionが制御するトレーラーで、暗号署名ではありません。切り分けはClaude Code attributionの設定で読めます。

パスフレーズで止まらないようにする:鍵はエージェントに載せる

gitの設定は、鍵の指し方で挙動が分かれます。

user.signingkeyの書き方署名に使われる秘密鍵
公開鍵のパス、またはkey::で始まる公開鍵署名に使われる秘密鍵ssh-agentにある鍵(エージェントに載っている必要がある)
秘密鍵のパス署名に使われる秘密鍵そのファイルの鍵
未設定署名に使われる秘密鍵gpg.ssh.defaultKeyCommandの出力(例:ssh-add -L)の最初の鍵

公開鍵を指定する形は、秘密鍵がエージェントに載っていることが前提です。載せたあとは署名のたびにパスフレーズを聞かれません。Claude Codeの外のシェルで、起動前に済ませます。

ssh-add ~/.ssh/id_ed25519   # ここでパスフレーズを入力する
ssh-add -l                   # 鍵が載ったことを確認する
claude

Claude Codeの中のBashツールでパスフレーズを聞かれる状況を作ると、入力待ちで止まるか、フルスクリーンの端末が壊れる報告があります。この症状と、BatchMode=yesで対話を禁じる回避策は、Claude Codeのsshでpassphraseを聞かれて固まる原因に詳しく書いてあります。署名の場合も、鍵を先にエージェントへ載せる点は同じです。

エージェントの有無はシェルごとに違います。ターミナルを開き直した直後や、sshで入ったリモートホストでは空のことがあります。署名が通らないときは、まずssh-add -lで鍵が見えているかを確かめます。

署名を確かめる:allowedSignersFileを用意する

署名を付けただけでは、手元のgitは「検証済み」とは扱いません。gpg.ssh.allowedSignersFileに、信頼する公開鍵を書いたファイルを指定します。ファイルは「プリンシパル 公開鍵」の行で構成します。

echo "you@example.com $(cat ~/.ssh/id_ed25519.pub)" \
  >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile \
  ~/.config/git/allowed_signers
git log --show-signature -1

メールアドレスはコミッターのものに置き換えます。ファイルの置き場所は任意です。gitのドキュメントによると、公開鍵がこのファイルにあると署名の信頼度はfullyになり、無いとundefinedになってgit verify-commitが失敗します。リポジトリの中に置く運用もできますが、その場合はトップレベルからの相対パスで指定します。

GitHub上で「Verified」と表示させるには、署名に使う公開鍵をGitHubのアカウントに登録します。GitHubのドキュメントは、既存の認証用の鍵を署名用にも登録できると説明しています。

sandboxを有効にしているときに署名が通る条件

ここからが、Claude Code固有の論点です。sandbox内で動くBashコマンドは、ファイルシステムとネットワークに制限がかかります。署名に関係するのは、次の3つの層です。

sandbox

署名を止める3つの層

  • エージェントのソケット

    ssh-agentとの通信はUnixソケットです。sandboxingのページは、エージェントのソケットをふさぐことがあると書いています。

  • 鍵ファイルの読み取り

    denyReadやcredentialsで~/.sshを隠していると、鍵ファイルを指す設定も読めません。

  • gitの設定の書き込み

    作業ディレクトリの.gitconfigと.git内のconfig・hooksへの書き込みは、sandboxが拒否します。

macOS:エージェントのソケットを許可する

macOSでは、sandbox.network.allowUnixSocketsが空だとsandboxがすべてのUnixソケットをふさぎます。エージェントを使う署名では、ソケットのパスを許可リストに足します。設定リファレンスの例は~/.ssh/agent-socketです。

{
  "sandbox": {
    "network": {
      "allowUnixSockets": ["~/.ssh/agent-socket"]
    }
  }
}

実際のパスは、手元でecho $SSH_AUTH_SOCKを実行して確かめます。例の値をそのまま使える環境とは限りません。この設定の詳細は、allowUnixSocketsの解説で扱っています。

Linux・WSL2:許可リストは無視される

Linux・WSL2では、allowUnixSocketsのリストは無視されます。seccompフィルターがソケットのパスを見られないためです。許可するならallowAllUnixSocketsをtrueにしますが、これはすべてのUnixソケットを開く設定です。

seccompフィルターが入っていない環境では、sandboxはUnixソケットの呼び出しをそもそもふさぎません。/sandboxのDependenciesタブで、フィルターの有無を確かめられます。

ソケットを開けたくないときの2つの道

ソケットの許可を広げたくない場合は、次の2つがあります。

  1. 秘密鍵のパスをuser.signingkeyに指定し、鍵にパスフレーズを付けない専用の署名鍵にする。エージェントのソケットは要りませんが、鍵ファイルがディスクに平文であるため、漏れたときの影響は大きくなります
  2. git commit *をexcludedCommandsに入れて、コミットだけをsandboxの外で動かす

2つ目の設定例です。

{
  "sandbox": {
    "excludedCommands": ["git commit *"]
  }
}

注意点は、excludedCommandsの解説にある制約がそのまま当てはまることです。

  • 末尾の *が必要です。引数なしのgit commitにしか一致しない書き方では、git commit -m "..."を拾えません
  • cdを含む呼び出しや、$(...)のコマンド置換を含む呼び出しは、sandboxに残ります
  • 除外したコマンドは、フルアクセスで動きます。git commitにはフックが付くため、フックのスクリプトも外で動きます

署名のためだけにgit commit全体をsandboxの外に出すのは、境界を広く緩める選択です。エージェントのソケットを1本許可するほうが、緩める範囲は小さく済みます。

方法緩める範囲向く状況
allowUnixSockets(macOS)緩める範囲指定したソケット1本向く状況エージェントで署名する、macOSのsandbox
allowAllUnixSockets(Linux・WSL2)緩める範囲すべてのUnixソケット向く状況seccompフィルターが入っている環境
excludedCommandsにgit commit *緩める範囲コミット処理全体向く状況ソケットの許可が使えない、または効かない
パスフレーズなしの専用鍵緩める範囲鍵ファイルの扱い向く状況署名のためにsandboxを触りたくない

Claudeに署名を外させない:設定とルールの置き場所

署名が失敗したとき、--no-gpg-signやgit -c commit.gpgSign=false commitで署名を外せば、コミット自体は作れてしまいます。Claudeにその回避をさせないための手は3つあります。

設定は自分のホームの~/.gitconfigに置く

sandbox内のコマンドは、作業ディレクトリの.gitconfigと.git内のconfigに書き込めません。ホームの~/.gitconfigも、sandboxの書き込み先に入っていなければ変更できません。commit.gpgSignをgit config --globalで自分で設定しておけば、sandbox内のClaudeがgit configで設定を書き換えることはできません。ただし、コマンドラインの-cはこの守りの外です。

denyルールで回避の書き方を止める

-cと--no-gpg-signは、settings.jsonのpermissions.denyで止められます。

{
  "permissions": {
    "deny": [
      "Bash(git commit --no-gpg-sign *)",
      "Bash(git -c commit.gpgSign=false *)"
    ]
  }
}

ただし、Bashの権限ルールは文字列の一致で動くため、引数を絞るルールは崩れやすいと公式のページが警告しています。オプションの順序を変えられたり、別の書き方で同じことをされたりすると、拾えません。確実な防御にはならず、抑止として置く位置づけです。

CLAUDE.mdに失敗時の振る舞いを書く

Claudeの判断に寄せる緩和策です。

## コミット
- コミットは署名付きで作る。署名に失敗したら `--no-gpg-sign` や
  `-c commit.gpgSign=false` で回避せず、失敗の内容を報告して止まる
- 失敗の原因が鍵のパスフレーズなら、ユーザーに `ssh-add` を頼む

署名が失敗したときの切り分け

署名に失敗するときは、sandboxの外のシェルで同じコミットが通るかを最初に見ます。通れば原因はsandbox側で、通らなければgitの設定か鍵の側です。

切り分け

署名できないときの切り分け

  1. Qsandboxの外のシェルで、同じコミットに署名できない?
    答えができる

    sandboxが原因です。ソケットの許可、~/.sshの読み取り拒否、excludedCommandsを見直します。

    答えがはい
  2. Qgpg.formatの値はsshになっている?
    答えがなっていない

    gpg.formatが未設定で、OpenPGPとしてgpgが呼ばれています。sshを設定します。

    答えがはい
  3. Qssh-add -lに署名用の鍵が見える?
    答えが見えない

    鍵がエージェントにありません。ssh-addで載せてから、Claude Codeを起動し直します。

    答えがはい

鍵とgit側の設定は揃っています

user.signingkeyのパスが公開鍵を指しているか、Gitが2.34以降かを確かめます。

commit.gpgSignを有効にしたままrebaseを回すと、書き換えたコミットすべてに署名が走ります。gitのドキュメントも、大量のコミットに署名される点と、パスフレーズを何度も打たずに済むようエージェントを使う便利さを挙げています。Claudeに履歴の整理を任せるときほど、鍵をエージェントに載せておく価値が出ます。

まとめ

設定の核心は、公開鍵をuser.signingkeyに指し、秘密鍵をエージェントに載せて起動することです。パスフレーズを聞かれる状況さえ作らなければ、Claude Codeの実行は止まりません。

sandboxを有効にしているなら、通る条件はOSで分かれます。macOSはallowUnixSocketsに1本、Linux・WSL2はallowAllUnixSocketsかフィルター無しの環境が前提です。ソケットを開けたくないなら、コミットをexcludedCommandsに回すか、専用鍵に切り替える選択になります。SSHリモートへのpushが落ちる別問題は、sandboxでgit SSHだけ失敗する原因で扱っています。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/sandboxing
  2. code.claude.com/docs/en/settings-reference
  3. code.claude.com/docs/en/permissions
  4. git-scm.com/docs/git-config
  5. git-scm.com/docs/git-commit
  6. docs.github.com/en/authentication/managing-commit-signature-verification/t…
この記事を共有:XはてブLinkedIn