GitHub や GitLab に認証できない SSH 鍵は、開発者が最もよく検索する問題の 1 つで、しかも半分のケースでは鍵は正常です。ただ診断を誤っているだけなのです。ですから、何かを作り直す前に、1 つのテストを実行し、その答えの読み方を身につけましょう。そのうえで、実際に原因になる頻度の高い順に、容疑者を片付けていきます。
「You’ve successfully authenticated, but GitHub does not provide shell access」
返ってきたのがこの行なら、手を止めてください。あなたの鍵はすでに効いています。 GitHub と GitLab はシェルを渡しません。SSH の上で git だけを話すので、あなたを認証したらすぐに切断します。「does not provide shell access」は、それを伝えているだけで、エラーではありません。GitLab の文言は少し違います(Welcome to GitLab, @you!)が、意味は同じです。鍵が効いているのに git push がまだパスワードを尋ねるなら、鍵は最初から問題ではありませんでした。下の HTTPS リモートの容疑者へ飛んでください。それがいつもの本当の原因です。
ステップ 0:そもそも壊れているかを教えてくれるテスト
鍵を認識しているかどうか、git ホストに直接尋ねます。
ssh -T git@github.com # GitHub
ssh -T git@gitlab.com # GitLab
さて、応答を 注意深く 読んでください。成功メッセージがエラーのように見えるからです。
Hi <username>! You've successfully authenticated, but GitHub does not
provide shell access.
これは成功です。 「Does not provide shell access」は失敗ではありません。GitHub と GitLab はシェルを渡さず、SSH の上で git だけを話すので、あなたを認証したら切断します。人は「does not provide」を見て、正常に動いている設定のトラブルシュートを始めてしまいます。その行に自分のユーザー名が見えるなら、鍵は効いています。問題は別の場所にあります(下の HTTPS リモート の容疑者へ。それがいつもの本当の問題です)。
本物の失敗は、代わりにこう見えます。
git@github.com: Permission denied (publickey).
これが、ここで直そうとしているものです。末尾に注目してください。Permission denied (publickey) は、提示したどの鍵もサーバーが受け入れなかったことを意味します。リストを上から順に片付けましょう。
容疑者 1:鍵がアカウントに追加されていない
最もよくある本当の原因です。ノート PC 上の鍵は、その公開鍵側が git ホストに登録されるまで、何もしません。公開鍵をコピーします。
cat ~/.ssh/id_ed25519.pub # note the .pub — this is the safe half to share
それを GitHub → Settings → SSH and GPG keys → New SSH key、または GitLab → Preferences → SSH Keys に貼り付けます。
例外のない唯一のルール。.pub のファイルを貼り付けること。.pub の付かないファイルは絶対に貼らないこと。 公開鍵は ssh-ed25519 AAAA…(または ssh-rsa AAAA…)で始まる 1 行です。これから貼ろうとしているものが -----BEGIN OPENSSH PRIVATE KEY----- で始まるなら、手を止めてください。それはあなたの秘密鍵、つまりあなたの身元そのものである秘密です。ウェブフォームに貼り付けることは、そのページを読める誰にでも認証情報を渡すことになります。公開鍵側は共有し、秘密鍵側は守ってください。
(まだ鍵がない? 作りましょう。ほとんどの場合は Ed25519(英語)で、ssh-keygen -t ed25519 -C "you@device"。)
容疑者 2:誤った鍵が提示されている
鍵は登録済みなのに、SSH が 別の 鍵を提示しています。複数の鍵を持ち歩いていて、エージェントがサーバーの拒む順に試すときによくあります。どの鍵が送られているかを正確に見ます。
ssh -vT git@github.com
Offering public key: の行を探します。登録した鍵がその中になかったり、誤った鍵が受け入れられていたりするなら、~/.ssh/config でホストごとに正しい鍵を固定します。
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
肝心なのは IdentitiesOnly yes の行です。これは、あなたが指定した IdentityFile だけ を提示し、エージェント内のあらゆる鍵をサーバーへ撒き散らすのをやめるよう SSH に指示します(これは Too many authentication failures も治します)。GitLab には Host gitlab.com で同じことをします。ファイルの構成については ssh_config ガイド(英語)に詳しくあります。
容疑者 3:エージェントが動いていない、または鍵が読み込まれていない
鍵にパスフレーズがあり、エージェントがそれを保持していないと、SSH は黙ってその鍵を飛ばすことがあります。エージェントが何を持っているかを確認します。
ssh-add -l
The agent has no identities(または Could not open a connection to your authentication agent)が出たら、エージェントを起動して鍵を読み込む必要があります。
eval "$(ssh-agent -s)" # start the agent in this shell
ssh-add ~/.ssh/id_ed25519 # load the key (prompts for the passphrase once)
これで ssh-add -l に鍵のフィンガープリントが並ぶはずです。(エージェントが実際には 何 で、どのように鍵を保持するかは、SSH エージェントとは何か(英語)をご覧ください。)
容疑者 4:SSH ではなく HTTPS のリモートになっている
これは「鍵は効いている(ssh -T は成功した)のに git push がまだパスワードを尋ねる」の背後にいる容疑者です。リポジトリが HTTPS を使うよう設定されており、HTTPS は SSH 鍵を完全に無視してトークンを要求します。確認します。
git remote -v
https://github.com/you/repo.git が見えたら、それが問題です。HTTPS はあなたの SSH 鍵に一切触れません。リモートを SSH に切り替えます。
git remote set-url origin git@github.com:you/repo.git # GitHub
git remote set-url origin git@gitlab.com:you/repo.git # GitLab
もう一度 git remote -v を実行し、git@ で始まることを確認します。この 1 つの修正だけで、「SSH 鍵が効かない」という報告のうち、実は鍵が最初から正常だった大きな割合が解決します。
容疑者 5:~/.ssh のパーミッション
SSH は、他者が改ざんできる鍵や設定を使うのを拒みます。詳細出力が不正なモードに言及していたり、明確な理由もなく鍵が無視されたりする場合は。
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 # private key
chmod 644 ~/.ssh/id_ed25519.pub # public key
chmod 600 ~/.ssh/config
Windows では、同期ドライブからやマシン間で鍵をコピーした後に表面化します。ファイルのセキュリティのプロパティから直すか、鍵をクラウド同期フォルダーの外に置いておきましょう。
容疑者 6:毎回パスフレーズを尋ねられる
すべての git 操作が鍵のパスフレーズを要求するなら、エージェントがそれをキャッシュしていません。ssh-add(英語)で一度読み込み(容疑者 3)、~/.ssh/config に追記してセッションをまたいで定着させます。
Host github.com gitlab.com
AddKeysToAgent yes
macOS では、ssh-add --apple-use-keychain ~/.ssh/id_ed25519 がパスフレーズを Keychain に保存するので、再起動後も残ります。
容疑者 7:1 つのホストに 2 つのアカウント
仕事用と個人用の GitHub アカウントは、1 本の鍵を共有できません。ある公開鍵は、ちょうど 1 つのアカウントに属します。それぞれに専用の鍵と、それを選ぶ Host エイリアスを与えます。
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/work_ed25519
IdentitiesOnly yes
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/personal_ed25519
IdentitiesOnly yes
あとは、ホストに エイリアス を使ってクローンするか、リモートを設定します。git@github-work:org/repo.git のように。SSH は github-work に一致させ、仕事用の鍵を選び、内部では接続先を github.com に書き換えます。
容疑者 8:組織の SSO が未承認(GitHub)
自分のリポジトリには問題なく認証できるのに、SAML SSO の背後にある組織のリポジトリでは Permission denied になる。鍵は存在しますが、その組織に対して承認されていません。Settings → SSH and GPG keys を開き、鍵を見つけ、Configure SSO をクリックして、その組織に対して承認します。GitLab にも並行するケースがあり、期限切れ の鍵が黙って拒否されます。これまで動いていた鍵が突然止まったら、SSH Keys で鍵の有効期限を確認してください。
それでもまだ Permission denied (publickey) のとき
リストを片付けてもなお同じ応答に行き当たるなら、本物の「鍵が受け入れられない」問題にまで絞り込めています。そのエラーそのものに対する体系的なループ(ssh -vvv の読み方、サーバーの方式一覧、提示された鍵と受け入れられた鍵の対比)は Permission denied (publickey) を素早く体系的に直す(英語)にあり、SSH エラーのより広い地図は SSH エラーの読み解きにあります。ただし git ホストに限れば、上の順番でほぼすべてを捕まえられます。公開鍵を登録し、正しい鍵を提示し、それをエージェントに読み込み、そして本当に SSH のリモートになっているかを確かめること。
クライアントがセットアップそのものをなくす場所
この摩擦の大半は帳簿付けです。どの鍵がどのホストのものか、読み込まれているか、そもそもリモートが SSH かどうか、といったこと。Termalin は、鍵を、それが属するホストとともに保持し、内蔵のエージェントで鍵を一度だけアンロックするのでパスフレーズを打ち直さずに済み(しかも、プロンプトをキャンセルしても鍵を黙って飛ばすことはありません)、さらに標準の ssh-agent プロトコルを話すので、git とあなたの CLI はその同じエージェントを介して署名できます。フィンガープリントは鍵ごとに見えるので、正しい公開鍵側を GitHub や GitLab に登録するのは、5 つの ~/.ssh フォルダーを探し回ることではなく、コピー&ペーストで済みます。上のアカウントとエイリアスのやりくりは、設定ファイルのパズルではなく、ホストごとの設定になります。
Termalin は無料のクロスプラットフォーム SSH クライアントで、git が署名に使える「一度のアンロック」エージェントで鍵を管理します。ダウンロード、または鍵を安全に扱う仕組みをご覧ください。