概要
GitHub上に2つのアカウントを所有し、それぞれのリモートリポジトリにSourceTreeからpushした際に認証エラーが発生した。原因と解決法を以下に書き残す。
再現
環境
- macOS 10.14.2
- SourceTree 3.0.1
手順
GitHubからログアウトし、SourceTree上でConnect Accountをクリックする。
ブラウザが起動し、GitHubのログイン画面が表示されるため、登録したいアカウントでログインする。
Generate KeyをクリックしてSSHキーを生成する。必要に応じてパスフレーズを設定し、Saveをクリックして登録を完了させる。これを登録したいアカウント分繰り返す。
各アカウント上の適当なリモートリポジトリをcloneして変更をpushすると、一つのアカウントでは成功するが、その他のアカウントでは以下のようなエラーが発生して失敗する。
原因
リモートリポジトリを更新する際、別アカウントの秘密鍵を使って公開鍵認証を試みたため、認証エラーが発生した。アカウントと秘密鍵が正しく紐づいていないことが原因である。
解決法
SSH接続チェック(秘密鍵指定なし)
まず、SSH接続が可能であることを確認するため、以下のコマンドを実行する。
$ ssh -T git@github.com
Hi {アカウントA}! You've successfully authenticated, but GitHub does not provide shell access.
SSH接続は可能であるが、秘密鍵を指定しないとアカウントAが使われた。これは、デフォルトの秘密鍵としてアカウントAのものが選ばれたためである。デフォルトの秘密鍵がどのように決まるかは、#秘密鍵を指定しない場合の挙動についてにて後述する。
エージェントキー削除
SourceTree上でSSHキーを生成した際に、エージェントキーに追加される秘密鍵を削除する。以下のコマンドでは -D
オプションで全てのキーを削除しているが、個別に削除する場合は -d
オプションを使うこと。
$ ssh-add -l
4096 SHA256:{ハッシュ} Generated by Sourcetree on macOS for {アカウントA}-GitHub (RSA)
4096 SHA256:{ハッシュ} Generated by Sourcetree on macOS for {アカウントB}-GitHub (RSA)
$ ssh-add -D
All identities removed.
$ ssh-add -l
The agent has no identities.
SSH接続チェック(秘密鍵指定あり)
秘密鍵を指定してアカウントを使い分けるため、-i
オプションを付けて再試行する。秘密鍵は ~/.ssh
配下に配置されており、SourceTree上で生成した場合は {アカウント名}-GitHub
のようなファイル名になっている。
$ ls ~/.ssh
{アカウントA}-GitHub {アカウントB}-GitHub config
{アカウントA}-GitHub.pub {アカウントB}-GitHub.pub known_hosts
$ ssh -T git@github.com -i ~/.ssh/{アカウントA}-GitHub
Hi {アカウントA}! You've successfully authenticated, but GitHub does not provide shell access.
$ ssh -T git@github.com -i ~/.ssh/{アカウントB}-GitHub
Hi {アカウントB}! You've successfully authenticated, but GitHub does not provide shell access.
秘密鍵を指定することで、それぞれのアカウントでSSH接続することができた。
~/.ssh/config
の修正
~/.ssh/config
に設定を記述することで、前述のコマンドと同じことをスマートに実現できる。以下の記述例にて、sshコマンド実行時に接続先ホストしか指定していない点に注目する。
$ cat ~/.ssh/config
Host {接続先名}
HostName github.com
User git
IdentityFile ~/.ssh/{アカウント名}-GitHub
$ ssh -T {接続先名}
上記を踏まえて、~/.ssh/config
を以下のように修正する。
# --- Sourcetree Generated ---
Host {アカウント名}-GitHub
HostName github.com
- User {アカウント名}
+ User git
PreferredAuthentications publickey
IdentityFile /Users/thara/.ssh/{アカウント名}-GitHub
UseKeychain yes
AddKeysToAgent yes
# ----------------------------
エージェントキー追加
エージェントキーから削除した秘密鍵を再登録する。
$ ssh-add ~/.ssh/{アカウントA}-GitHub
Identity added: /Users/thara/.ssh/{アカウントA}-GitHub (Generated by Sourcetree on macOS for {アカウントA}-GitHub)
$ ssh-add ~/.ssh/{アカウントB}-GitHub
Identity added: /Users/thara/.ssh/{アカウントB}-GitHub (Generated by Sourcetree on macOS for {アカウントB}-GitHub)
$ ssh-add -l
4096 SHA256:{ハッシュ} Generated by Sourcetree on macOS for {アカウントA}-GitHub (RSA)
4096 SHA256:{ハッシュ} Generated by Sourcetree on macOS for {アカウントB}-GitHub (RSA)
SSH接続検証
接続先ホストのみを指定してsshコマンドを実行し、#SSH接続チェック(秘密鍵指定あり)と同じ結果が得られることを確認する。接続先が git@github.com
から {アカウント名}-GitHub
に置き換わっているところが要点である。
$ ssh -T {アカウントA}-GitHub
Hi {アカウントA}! You've successfully authenticated, but GitHub does not provide shell access.
$ ssh -T {アカウントB}-GitHub
Hi {アカウントB}! You've successfully authenticated, but GitHub does not provide shell access.
リモートリポジトリの修正
SourceTree上にて、push先の各リモートリポジトリのURLを#SSH接続検証の要点を踏まえて以下のように修正する。
リモートリポジトリをcloneする場合は、同様にURLを修正してcloneする。
動作確認
各リモートリポジトリへ変更をpushし、正常に完了することを確認する。
秘密鍵に関わる挙動について
SSH接続の際に秘密鍵を指定しない場合はデフォルトで ~/.ssh/id_rsa
が使われるが、自分の環境では同ファイルが存在しないのにアカウントAがデフォルトとして用いられた。デフォルトの秘密鍵は複数存在し得るのだが、それを知らず理解に苦しんだため、調査結果を備忘録として以下に書き残す。
秘密鍵を指定せず git@github.com
へSSH接続をした時のログを -v
オプションを付与して詳細に出力すると、秘密鍵の候補の一覧が確認できる。候補の詳細はopensshのソースコード中のコメントに記載されている。
$ ssh -vT git@github.com
OpenSSH_7.9p1, LibreSSL 2.7.3
...
debug1: rekey after 134217728 blocks
debug1: Will attempt key: Generated by Sourcetree on macOS for {アカウントA}-GitHub RSA SHA256:{ハッシュ} agent
debug1: Will attempt key: Generated by Sourcetree on macOS for {アカウントB}-GitHub RSA SHA256:{ハッシュ} agent
debug1: Will attempt key: /Users/thara/.ssh/id_rsa
debug1: Will attempt key: /Users/thara/.ssh/id_dsa
debug1: Will attempt key: /Users/thara/.ssh/id_ecdsa
debug1: Will attempt key: /Users/thara/.ssh/id_ed25519
debug1: Will attempt key: /Users/thara/.ssh/id_xmss
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: Generated by Sourcetree on macOS for {アカウントA}-GitHub RSA SHA256:{ハッシュ} agent
debug1: Server accepts key: Generated by Sourcetree on macOS for {アカウントA}-GitHub RSA SHA256:{ハッシュ} agent
debug1: Authentication succeeded (publickey).
...
上記のログより、秘密鍵の候補として最上位に選出されたアカウントAのエージェントキーで認証を行い、成功したことが分かる。エージェントキーが存在する時に秘密鍵を指定せずSSH接続を行なった場合は、登録されている順にエージェントキーで認証を試み、失敗した場合に ~/.ssh/id_rsa
が用いられる。
まとめ
~/.ssh/config
の設定を修正し、リモートリポジトリのURLを変えることで、複数のGitHubのアカウントを共存させることができた。最後に、問題発生当初に実施していた手順と処理の流れを以下に示す。
SourceTree上でアカウントAとアカウントBを登録した。
↓
両アカウントの公開鍵と秘密鍵が生成された。
同時に、エージェントキーが追加された。
↓
両アカウントのリモートリポジトリを git@github.com:{アカウント名}/{リポジトリ名}
の形式でcloneした。
↓
git@github.com:アカウントA/リポジトリ
へ変更をpush。
秘密鍵が指定されていないためエージェントキーを検索し、先に登録されたアカウントAの秘密鍵が選出される。アカウントと秘密鍵が正しく紐づいているため、処理が正常に完了した。
↓
git@github.com:アカウントB/リポジトリ
へ変更をpush。
上記と同様にアカウントAの秘密鍵が選出される。アカウントと秘密鍵が紐づいていないため、認証エラーが発生して処理が失敗した。