WSL2 + VSCode + DevcontainerでGitHubにSSHでpushする際にちょっとハマった
はじめに
久々に開発環境を構築しなおした際に、VS Code
とDevcontainer
を使ってWSL2
上で開発をしているのですが、GitHub
にSSH
でpush
する際にハマったのでメモしておきます。
環境
- OS: Windows 11 Pro
- WSL2 + Ubuntu 22.04
- VS Code 1.82.2
- Docker Desktop 4.23.0 (120376)
結論
とりあえず同じ問題にぶつかった人のために、先に結論を書いておきますね!
-
WSL2
上でkeychain
を導入する -
.bashrc
に以下のコードを追記する
# SSHの秘密鍵をロードする(※~/.ssh/id_rsaの場合は下記の通り)
/usr/bin/keychain -q --nogui $HOME/.ssh/id_rsa
source $HOME/.keychain/$(hostname)-sh
経緯
元々、WSL2
上で開発をしていたのですが、devcontainer
内でGitHub
にSSH
でpush
するために、元々設定されていた下記のような設定を何も考えずに引き継いで使っていました。
.devcontainer/compose.yml
volumes:
node_modules:
services:
node:
build:
context: ..
dockerfile: .devcontainer/node/Dockerfile
volumes:
- ..:/home/node/project:cached
- node_modules:/home/node/project/node_modules:delegated
- ~/.ssh:/home/node/.ssh:cached # ここでSSHの秘密鍵を指定する
- ~/.gitconfig:/home/node/.gitconfig:cached # ここでGitのグローバル設定を共有する
command: /bin/sh -c "while sleep 1000; do :; done"
user: node
ところが、この設定だと、devcontainer
内とWSL2
上で別ユーザーを使いたい場合に、SSH
の秘密鍵が読み込めないという問題が発生してしまいました。
衝撃の事実: そもそも公式で推奨されている方法と違っていた
拡張機能の公式ドキュメントの解説箇所を見てみると、このように書かれてます。
The Dev Containers extension provides out of the box support for using local Git credentials from inside a container. In this section, we'll walk through the two supported options.
If you do not have your user name or email address set up locally, you may be prompted to do so. You can do this on your local machine by running the following commands:
git config --global user.name "Your Name" git config --global user.email "your.email@address"
The extension will automatically copy your local .gitconfig file into the container on startup so you should not need to do this in the container itself.
つまり、devcontainer
内で~/.gitconfig
を共有する必要はなく、devcontainer
内でgit config
を実行することで、~/.gitconfig
が自動的にコピーされるようです。
この時点で、以下の行が完全に意味がないということがわかりました。
- ~/.gitconfig:/home/node/.gitconfig:cached # ここでGitのグローバル設定を共有する
さらに衝撃の事実: SSHの秘密鍵も自動的に共有される
さらにドキュメントを読み進めると、SSH
の秘密鍵を共有する方法も書かれていました。
There are some cases when you may be cloning your repository using SSH keys instead of a credential helper. To enable this scenario, the extension will automatically forward your local SSH agent if one is running.
You can add your local SSH keys to the agent if it is running by using thessh-add
command. For example, run this from a terminal or PowerShell:ssh-add $HOME/.ssh/github_rsa
On Windows and Linux, you may get an error because the agent is not running (macOS typically has it running by default). Follow these steps to resolve the problem:
なんと、ホストOSでssh-agent
が起動していて、SSH
の秘密鍵が登録されていれば、devcontainer
内でも同じSSH
の秘密鍵が使えるようです。
至れり尽くせりかよ!ここまで書いてくれてるのに何で誰も気づかなかったんだろう…。
意気揚々と試してみる
さっそく、WSL2
上でssh-agent
を起動して、SSH
の秘密鍵を登録してみました。
(プライバシーに関わる部分は伏せてます)
まず、ssh-agent
を使えるようにopenssh-client
やsocat
をインストールして…
$ sudo apt install openssh-client socat
$ eval `ssh-agent`
Agent pid 12345
$ ssh-add ~/.ssh/id_rsa
Identity added: /home/nayu/.ssh/id_rsa (nayu@DESKTOP-XXXXXXX)
ssh-agent
が起動して、SSH
の秘密鍵が登録できました。
この後、実際にdevcontainer
を起動して、git push
をしてみると…
$ git pull
Already up to date.
無事に接続できてる感じだ!!素晴らしい!
ssh-add
するのが面倒なので自動化しようとした
起動するたびにしかし、いろいろ調べているうちに、このままだとssh-agent
を起動するたびに、ssh-add
をする必要があることが判明しました。
そこで、下記のようなコードを.bashrc
に追記して、ssh-agent
を起動すると同時にssh-add
をするようにしました。
# 登録したいSSHキーのフルパスを配列で設定
# ユーザーのホームディレクトリは$HOMEで指定
SSH_KEY_PATHS=(
"$HOME/.ssh/github-first.pem"
"$HOME/.ssh/github-second.pem"
)
# SSHエージェントの環境変数が設定されていない場合
if [ -z "$SSH_AUTH_SOCK" ]; then
# 現在実行中のSSHエージェントがあるか確認
RUNNING_AGENT=$(ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]')
# SSHエージェントが実行中でない場合、新しいエージェントを起動
if [ "$RUNNING_AGENT" = "0" ]; then
eval $(ssh-agent -s)
fi
# SSH_AUTH_SOCKが設定されているか確認
if [ -n "$SSH_AUTH_SOCK" ]; then
# すでに登録されているキーを取得
REGISTERED_KEYS=$(ssh-add -l)
# 各SSHキーに対して処理
for KEY_PATH in "${SSH_KEY_PATHS[@]}"; do
# SSHキーの名前(ファイル名)を抽出
KEY_NAME=$(basename $KEY_PATH)
# 該当のキーがすでに追加されているか確認
KEY_EXIST=$(echo "$REGISTERED_KEYS" | grep "$KEY_NAME" | wc -l)
# キーが追加されていない場合、ssh-addで追加
if [ "$KEY_EXIST" = "0" ]; then
ssh-add $KEY_PATH
fi
done
else
echo "SSH_AUTH_SOCK is not set. Cannot proceed with ssh-add."
fi
fi
ふっ…また便利なスクリプトを書いてしまった…。
と、思ったのですが、シェルを起動した瞬間、私は凍り付きました。
SSH_AUTH_SOCK is not set. Cannot proceed with ssh-add.
$nayu@DESKTOP-XXXXXXX:~$
私:「待って。SSH_AUTH_SOCK
が設定されていないってことは、ssh-agent
が起動してないってことだよね??
いや、ssh-agent
は起動してるはずなんだけど…。」
自分で書いたエラーハンドリングなのに、自分で混乱しています。
じゃあ、ssh-agent
が起動しているか確認してみましょう。
$nayu@DESKTOP-XXXXXXX:~$ ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]'
1
私:「え??ssh-agent
は起動してるのに、SSH_AUTH_SOCK
が設定されてないってことは、ssh-agent
が起動してないってことだよね??」
もうこのへんで、私は完全に頭がおかしくなっています。
私:「じゃあ…手動でキーを追加してみるか…」
$nayu@DESKTOP-XXXXXXX:~$ ssh-add ~/.ssh/github-first.pem
Could not open a connection to your authentication agent.
私:「…。(失神)」
ssh-agent
は一味違うようだ
WSL2のそうこうしているうちに、以下の記事を見つけました。
以下、引用いたします。
このあとssh-addするとパスフレーズが記憶されます。しかし、一度ターミナルを抜けてもう一度開くと、ssh-agentプロセスは生きているのに、ssh-addができません。
そういうことだったのか…。
keychain
導入ですべて解決
上記の記事でも紹介されているように、keychain
を導入することで、ssh-agent
を起動するたびにssh-add
をする必要がなくなります。
sudo apt-get install keychain
keychain
を導入したら、~/.bashrc
| ~/.zshrc
に以下のコードを追記します。
# SSHの秘密鍵をロードする
/usr/bin/keychain -q --nogui $HOME/.ssh/id_rsa # ここでSSHの秘密鍵を指定する
source $HOME/.keychain/$(hostname)-sh
これで、WSL2
上でssh-agent
を起動するたびに、ssh-add
をする必要がなくなりました。
エラーも出なくなりました。
補足
同一ディレクトリに公開鍵がない場合、以下のような警告が出るみたいです。
* Warning: Cannot find separate public key for {秘密鍵のパス}
その場合は、ssh-keygen
で公開鍵を作成できます。
ssh-keygen -y -f {秘密鍵のパス} > {公開鍵のパス}.pub
まとめ
今回は、WSL2
上でVS Code
とDevcontainer
を使って開発をしている際に、GitHub
にSSH
でpush
する際にちょっとだけハマった話を書きました。
他にも解決方法はありそうですが、keychain
を使った方法はかなりシンプルだったので、今後はkeychain
を使うことにしました。
最初に紹介した古いdeccontainer
用のcompose.yml
もきれいになりました。
volumes:
node_modules:
services:
node:
build:
context: ..
dockerfile: .devcontainer/node/Dockerfile
volumes:
- ..:/home/node/project:cached
- node_modules:/home/node/project/node_modules:delegated
command: /bin/sh -c "while sleep 1000; do :; done"
user: node
それでは良きDevcontainer
ライフを!
参考
Discussion
SSHの秘密鍵も自動的に共有されるのはまさかすぎました!勉強になります!