GitHub Enterprise Cloud (GHEC) で Enterprise Managed Users (EMU) を利用している場合、同じ github.com で複数のアカウントを使い分ける必要があります。
検索すれば設定方法は見つかるのですが、何点かハマったのでその記録です。
環境は次のとおりです。
- Ubuntu 22.04
- OpenSSH_8.9p1
GitHub の世界
GitHub で SSH でレポジトリにアクセスする場合は git@github.com:XXXXX/XXXXX.git と指定します。即ち、SSH のユーザー名は git で固定されます。GitLab や Bitbucket も同じですね。
では GitHub 内でどうやってアカウントを判別しているかというと、SSH の鍵で判別しています。(他のアカウントに登録している SSH 鍵を別の GitHub アカウントで登録しようとすると Key is already in use とエラーが出ます)
つまり、 GitHub で複数アカウントを使い分けるには、別々の SSH 鍵を使い分ける 必要があります。
ssh の世界
SSH で同じ github.com へのアクセスで SSH 鍵を使い分けるには Host を変えて設定すればよいです。
Host github.com HostName github.com UpdateHostKeys yes IdentityFile ~/.ssh/id_ecdsa IdentitiesOnly yes Host oreno.github.com HostName github.com UpdateHostKeys yes IdentityFile ~/.ssh/oreno IdentitiesOnly yes
次のようにすれば、GitHub のアカウント名が確認できます。
ssh -T git@github.com # もしくは ssh -T git@oreno.github.com => Hi hirose31! You've successfully authenticated, but GitHub does not provide shell access.
利用頻度が高い方を github.com に、低い方を別名にするとよいでしょう。
これで、
git clone git@oreno.github.com:omaeno/darekano.git
で oreno の GitHub アカウントで clone できます。
なお、パブリックな GitHub のレポジトリは、認証不要なので HTTPS でアクセスすることにしました。
git の世界
oreno の場合は git の設定をちょっと変えたいとき、例えばコミットの Author を変えたいときは、このようなファイルを用意しておいて、
# ~/.gitconfig.oreno [user] email = oreno@example.com name = Oreno Mono
# ~/.gitconfig.oreno-org [user] email = oreno+soshiki@example.com name = Oreno Soshiki
~/repos/ の中に ホスト名/organization名/レポジトリ名 で clone している場合、~/.gitconfig にこのように設定すればできます。
# ~/.gitconfig # ~/repos/oreno.github.com/ の中にある、clone したレポジトリ全部に適用。 [includeIf "gitdir:~/repos/oreno.github.com/"] path = ~/.gitconfig.oreno # 更に、organizaion 名が `-oreno` で終わる場合に上書き設定。 [includeIf "gitdir:~/repos/oreno.github.com/*-oreno/"] path = ~/.gitconfig.oreno-org
git-config(1) に拠ると、gitdir: では * や ** といったワイルドカードが使えるので活用するとよいでしょう。
ちなみに gh
oreno で git clone した場合、.git/config に記載される remote.origin.url のホスト名は oreno.github.com になるので、GitHub CLI の gh browse なんかが動かないんじゃないかな?と思ったのですが、gh やあと hub も remote.origin.url は参照しておらず、GitHub API でレポジトリの情報を得て URL を組み立てているようで問題ありませんでした。
おハマり話
こんなスンナリ設定できたわけではなく、色々ハマったのでそのお話です。
複数の IdentityFile 指定
いくら設定を見直しても、ssh -T git@oreno.github.com で oreno じゃない方の GitHub アカウント扱いになっちゃってたんですよね。
で、
ssh_config(5) に拠ると、
Since the first obtained value for each parameter is used,
SSH の設定は最初にマッチした設定値が採用されるとあります。
ただし、IdentityFile は例外的に、
It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives).
複数指定可能で、その場合は試行する SSH 鍵のリストに追加されます。
さて、私の秘伝のタレ的な ~/.ssh/config では末尾にこういう設定を書いていました。
... Host * ...いろいろ... IdentityFile ~/.ssh/id_ecdsa ...いろいろ...
(記憶が曖昧ですが、id_ecdsa を優先的に使ってほしくて指定したんじゃないかと思います)
【試行する SSH 鍵の順番】は、複数の鍵が ssh-agent に登録されている場合、IdentityFile の設定順序とは関係なく、 id_rsa → id_ecdsa → id_ecdsa_sk → id_ed25519 → id_ed25519_sk → これら以外の名前の鍵 の順となるようです。
したがって、oreno.github.com に SSH アクセスすると、oreno と id_ecdsa が SSH 鍵の試行リストとなり、最初に id_ecdsa を試して成功するので oreno で期待した GitHub アカウントにならなかった次第です!
- 原則として
IdentityFileは指定しない。- 指定が無ければ【試行する SSH 鍵の順番】の順に試行するのでそれに任せる。
- 今回の
github.comの設定のように、どうしても個別で指定したい場合だけ指定する。IdentitiesOnly yesも指定する。
ように ~/.ssh/config を書き直しました。
ちなみに ssh-agent に複数の鍵が登録されている場合の施行順序は、 IdentitiesOnly の設定でも変化しこんな感じでした。
- 単一の
IdentityFileがあり、IdentitiesOnly yes- → 単一の
IdentityFileを試行する。
- → 単一の
- 複数の
IdentityFileがあり、IdentitiesOnly yes- → 複数の
IdentityFileを【試行する SSH 鍵の順番】の順に試行する。
- → 複数の
- 単一の
IdentityFileがあり、IdentitiesOnly no- → 単一の
IdentityFileを試行した後、ssh-agentに登録されている鍵を【試行する SSH 鍵の順番】の順に試行する。
- → 単一の
- 複数の
IdentityFileがあり、IdentitiesOnly no
鍵の順番を確認するには、ちょっとデバッグログが多いですが、ssh -v git@github.com するとよいです。
ControlPath
ControlPath で指定できるトークンの %h は、実際に接続するリモートホスト名です。つまり、Host の値ではなく HostName の値になるのでどちらも github.com になります。
なので、最初に ssh -T git@oreno.github.com すると、そのセッションが再利用されるので、後発で ssh -T git@github.com しても oreno の GitHub アカウントになります!
Host の設定を試行錯誤しているときにめちゃんこハマりました…
ControlPath では %n (コマンドラインで指定された元のリモートホスト名)を使い、試行錯誤している最中は ssh -S none して ControlPath を無効化するとよいと思います。
forward 先の ssh-agent
他のホストに ssh ログインして、そこで forward された ssh-agent の鍵を利用して GitHub にアクセスする場合、他のホストには SSH 鍵のファイルは存在しない(よね?)ので、IdentityFile が効きません。
「他のホストに ssh ログインするときの鍵」と「GitHub にアクセスするときの鍵」が同じなら問題ないのですが、深遠な理由で異なる場合、どうするか。
「他のホストに ssh ログインするときの鍵」と「GitHub にアクセスするときの鍵」の鍵の種類を異なるものして、PubkeyAcceptedAlgorithms でその種類を強制するようにしました。
Host github.com HostName github.com UpdateHostKeys yes ### 存在しないので書かない。 # IdentityFile ~/.ssh/id_ecdsa ### IdentityFile を指定していないので no 。 IdentitiesOnly no # ecdsa の鍵を使うように指定。 PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256
もっといい方法がありそうですが…