devcontainer の SSH エージェントフォワーディングを止める3つの方法と、それぞれの落とし穴
はじめに
VS Code で devcontainer を開くと SSH 認証用ソケットをはじめ複数のソケットが /tmp に作られることを確認しました。devcontainer CLI を使えばこれを避けられますが、VS Code から開く場合はソケットが生成されてしまいます。
今回は、生成されてしまったソケットを後から無効化する 3つの方法を試してみました。
前提
検証の前に、SSH エージェントがフォワードされていることを確認します。
ホストと同じ鍵がコンテナにあることを確認します。
今回は、GitHubのSSHを使います。ここでは、コンテナから認証が通ることを確かめました。
# ホスト
ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX user@host (ED25519)
# コンテナ内
ssh-add -l
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX user@host (ED25519)
ssh -T git@github.com
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
対策1:ssh-add -D
コンテナ内で ssh-add -D を実行し、SSH エージェントから全鍵を削除します。
SSH 認証を防ぐことができました。
ssh-add -D
All identities removed.
ssh-add -l
The agent has no identities.
ssh -T git@github.com
git@github.com: Permission denied (publickey).
抜け穴検証 - ホストで鍵を再登録する
ホストで鍵を再登録して、コンテナから再度 SSH を試みます。
# ホスト
ssh -T git@github.com
Enter passphrase for key '/path/to/your/key':
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
# コンテナ内
ssh-add -l
256 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx github-key (ED25519)
ssh -T git@github.com
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
コンテナ内でGitHubへの認証ができてしまいました。つまり、ホストで git 操作などを行いパスフレーズを入力した瞬間に SSH エージェントへ鍵が再登録され、コンテナ側にも設定が同期されます。自分の知らない間にコンテナ内でも鍵が使えるようになっていたということが起きそうです。
落とし穴
再登録という対策の抜け道だけでなく大きな問題があります。
ssh-add -D をコンテナ内で実行すると、ホストの SSH エージェントからも鍵が消えます。SSH エージェントのソケットがフォワードされているため、コンテナ内の操作がそのままホストに作用するためです。「コンテナを隔離する」つもりが「ホストを壊す」操作になっています。
対策2:remoteEnv に SSH_AUTH_SOCK: ""
devcontainer.json に以下を追加します。
"remoteEnv": {
"SSH_AUTH_SOCK": ""
}
コンテナで確認してみます。
echo $SSH_AUTH_SOCK
(空)
ssh -T git@github.com
git@github.com: Permission denied (publickey).
VS Code のターミナルでは SSH_AUTH_SOCK が空になり、SSH エージェント経由の認証ができなくなりました。
抜け穴検証 - ソケットファイルのパスを直接指定する
SSH_AUTH_SOCK は空ですが、ソケットファイル自体はコンテナ内の /tmp に残っています。ls でパスを特定して直接指定してみます。
ls /tmp/
vscode-ssh-auth-XXXXX.sock ...
SSH_AUTH_SOCK=/tmp/vscode-ssh-auth-XXXXX.sock ssh -T git@github.com
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
認証できてしまいました。
抜け穴検証 - docker exec 経由で実行
remoteEnv は VS Code経由のプロセスにのみ適用され、docker exec で直接コンテナに入れば SSH_AUTH_SOCK の影響を受けないのではと考えて試してみます。
docker exec -it <container_id> bash -c 'ssh -T git@github.com'
git@github.com: Permission denied (publickey).
突破できませんでした。
remoteEnv の影響ではなく、そもそも docker exec 経由でも SSH_AUTH_SOCK が設定されないか、設定されていないコンテナをただ再利用しているからだと考えます。ただし、前の抜け穴と同様にソケットファイルのパスを直接指定すれば話は別です。
docker exec <container_id> bash -c 'SSH_AUTH_SOCK=/tmp/vscode-ssh-auth-XXXX.sock ssh -T git@github.com'
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
ソケットファイルが残っている限り、経路を問わず迂回できてしまいます。
落とし穴
remoteEnv で環境変数を空にしても、ソケットファイル自体はコンテナ内に残ります。ファイルが存在する限り、パスを直接指定することで迂回できてしまいます。
対策3:SSH エージェントのソケットファイルを削除する
対策2の抜け道はソケットファイルが残っていることでした。ソケットファイルそのものを削除します。
rm /tmp/$(ls /tmp/ | grep vscode-ssh-auth)
ssh-add -l
Error connecting to agent: No such file or directory
ssh -T git@github.com
git@github.com: Permission denied (publickey).
SSH エージェントへの接続ごと切断できました。ホスト側への影響も確認します。
# ホスト
ssh-add -l
256 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx github-key (ED25519)
ホストは無事です。
抜け穴検証 - ターミナルを新しく開きソケットの再作成を試みる
ls /tmp/ | grep vscode-ssh-auth
(空)
VS Code上でターミナルを新しく開いてもソケットは再作成されませんでした。
落とし穴
いくつか確認できていない点があります。
- rebuild 以外の操作(ウィンドウの再接続、VS Code の再起動など)でソケットが再生成されるかどうかは未検証です
- ソケットを削除したことで VS Code の他の機能(Git 操作など)に影響が出るかどうかも調べきれていません
まとめ
| 方法 | 効果 | ホストへの副作用 | 恒久性 |
|---|---|---|---|
ssh-add -D |
防げる(ただしホストで再登録すれば即復元) | ホストの SSH エージェントも壊す | なし |
SSH_AUTH_SOCK: "" |
防げない(ソケット直接指定で迂回可能) | なし | あり(設定が残る限り有効) |
| ソケットファイルを削除 | 防げる | なし | なし(rebuild で再生成) |
ソケットファイルを削除する方法は使えるかもしれませんが、副作用や不明なことがまだあります。SSH エージェントフォワーディングを根本から無効化したい場合は、devcontainer の設定レベルではなく、ソケット自体を生成させない構成(devcontainer CLI の利用など)を検討するのが良さそうです。
今回はソケットの無効化を試みましたが、今後はFirewallでコンテナ内でのアウトバウンド通信を制限し、意図しないSSH接続を断つ方法も深掘りしてみたいと思います。