タイトルは半分ウソでDocker Desktop for Windows自体のTLSを有効にしているのではなく Expose daemon on tcp://localhost:2375 without TLS
に**チェックを入れずに(TLSなしにしないで)**WSLからdockerを使用する方法というのがこの記事の正しい内容です。(でも検索する人はTLSを有効にしたいと思って検索するでしょう?)TLSなしにすることがなぜ危険かは「[本当は怖いDocker for Windows のExpose daemon on tcp://localhost:2375 without TLS]
(https://qiita.com/shioshiota/items/fe0e085826bc2f19f154)」などを参照して下さい。
この記事はDocker Desktop for Windows 2.1.0.5で動作検証しています。またこの記事ででてくるWSLとはWSL1のことです。WSL2やDocker Desktop WSL 2では状況が違うかもしれません。
やり方は2通り
- npiperelay & docker-relay を使用する。(Unix socket + Named pipe)
- docker-remote-api-tls を使用する。(TLS + Unix socket)
1.はTLSそのものを使いません。TLSを使用したい場合は2.の方法を使います。今回はローカルマシンで使いたかっただけなので試していないですが、2.だと正しく設定すればネットワークごしに使えるはずです。その分セキュリティには気をつける必要があります。両方を併用することも可能です。
2020-01-22 追記 TLSを有効にできるかもしれない。詳細は「3. TLSを有効にする?」で
1. npiperelay & docker-relay
- https://devblogs.microsoft.com/commandline/cross-post-wsl-interoperability-with-docker/
- https://github.com/jstarks/npiperelay
仕組み
Docker client on WSL
↓ Unix socket
docker-relay(socat)
↓ stdio
npiperelay.exe
↓ Named pipe
Docker Desktop for Windows
docker-relay(socatを使ったシェルスクリプト)はサンプル的な扱いで、dockerだけではなくsocatで対応してるUnix socketやシリアルポート等をNamed pipeにリレーすることができます。他にはMySQLやシリアルコンソールのサンプルが付属しています。
使い方
バイナリは提供されてないのでビルドが必要です。基本的に公式のREADME.mdの通りですが少し簡略化してます。Ubuntu on WSL前提です。
$ # 準備
$ # Linux版のdockerをあらかじめインストールしておく(手順省略)
$ sudo adduser $USER docker # すでに自分をdockerグループに追加しているなら不要
$ sudo apt install golang socat # すでにインストールされているなら不要
$ # ビルド (ソースコードは、$HOME/go/src/github.com/jstarks/npiperelay以下にダウンロードされます)
$ GOOS=windows GOARCH=amd64 go get -d github.com/jstarks/npiperelay
$ GOOS=windows go build -o /mnt/c/Users/<ユーザー名>/bin/npiperelay.exe github.com/jstarks/npiperelay
$ # インストール
$ sudo ln -s /mnt/c/Users/<ユーザー名>/bin/npiperelay.exe /usr/local/bin/npiperelay.exe
$ sudo install $HOME/go/src/github.com/jstarks/npiperelay/scripts/docker-relay /usr/local/bin/docker-relay
$ # docker-relay起動
# unset DOCKER_HOST # 念の為。環境変数を設定していなければ不要
$ sudo docker-relay &
$ docker version # dockerが実行できてるはず
注意点
-
npiperelay.exe
はWindows側のディレクトリ(/mnt/c
以下)に生成する必要がある。 -
docker-relay
(socat
) を終了したい場合はsudo kill <socatのPID>
。 - (
docker-relay
が起動してないのに)socat[439] E "/var/run/docker.sock" exists
のようなエラーが出てdocker-relay
が起動できない場合は、前回の起動で正しく終了しておらず/var/run/docker.sock
が残っているためなので手動で削除する。
改良(任意)
基本的には上記の方法で良いのですが、このままだとOS再起動時に手動でdocker-relay
を実行しなければいけないので、bash起動時に自動的に実行するようにします。
#!/bin/sh -eu
PIDFILE=/var/run/docker-relay.pid
SOCK=/var/run/docker.sock
pgrep -F "$PIDFILE" -f "socat UNIX-LISTEN:$SOCK" >/dev/null 2>&1 && exit
pkill -f "socat UNIX-LISTEN:$SOCK" ||:
rm -f "$SOCK"
exec socat UNIX-LISTEN:$SOCK,fork,group=docker,umask=007 EXEC:"npiperelay.exe -ep -s //./pipe/docker_engine",nofork &
echo $! > "$PIDFILE"
$ # sudo docker-relay でパスワードを聞かれないようにする
$ echo '%docker ALL=(ALL) NOPASSWD: /usr/local/bin/docker-relay' | sudo tee /etc/sudoers.d/docker-relay
$ # .bashrcで自動起動するようにする
$ echo "sudo docker-relay" >> ~/.bashrc
※気のせいかもしれないですが、たまに docker-relay が落ちる気がします。その場合はsudo docker-relay
で再起動できます。改良版のdocker-relayはコマンド最後の &
は不要です。docker-relay の多重起動防止や正しく終了しなかった場合のケアもしてるのでそのまま実行できるはずです。
2. docker-remote-api-tls
仕組み
Docker client on WSL
↓ TLS
docker-remote-api-tls (nginx on docker container)
↓ Unix socket
Docker server on DockerDesktopVM
docker-remote-api-tls は TLS 通信を nginx でproxy_pass unix:/var/run/docker.sock
するだけの docker コンテナ
使い方
TLS 通信を行うには Docker 用に(オレオレ)証明書が必要です。それを簡単に生成するツール(create-certs.sh
)も docker-remote-api-tls に含まれています。さらに自動生成もしてくれます。なので適当なディレクトリに以下のようなYAMLを作成してdocker-composeで起動するだけです。
version: "3.4"
services:
remote-api:
image: kekru/docker-remote-api-tls:v0.2.0
ports:
- 2376:443
environment:
- CREATE_CERTS_WITH_PW=<任意のパスワード>
- CERT_HOSTNAME=localhost
volumes:
- ./certs:/data/certs
- /var/run/docker.sock:/var/run/docker.sock:ro
C:\適当なディレクトリ>docker-compose up -d
C:\適当なディレクトリ\certs
以下に証明書が作成されます。(以下はWSLから見た場合)
/mnt/c/適当なディレクトリ/certs
├── ca-cert.pem
├── ca-key.pem
├── client
│ ├── ca.pem
│ ├── cert.pem
│ └── key.pem
├── server-cert.pem
└── server-key.pem
WSLで以下の環境変数を設定します。(.bashrcに追加しましょう)
export DOCKER_CERT_PATH="/mnt/c/適当なディレクトリ/certs/client"
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://localhost:2376"
WSLでdocker
コマンドが使えるはずです。(Linux用のdockerをWSL上に別途インストールして下さい)
$ docker version
省略
3. TLSを有効にする?
2020-01-22 追記 試してないです。ただのメモです。
- Windows用のDockerサーバーの設定ファイルdaemon.jsonにTLSの設定項目があります。
- Dockerサーバーの仮想マシン(DockerDesktopVM)は再起動すると大部分の変更はリセットされますが、
/var/lib
以下だけはデータが残ります。 - つまり
/var/lib
以下にTLS関連のファイルを置いてdaemon.json
でそのファイルを指定すれば・・・
あとこれ Adding TLS certificates(これはクライアントの設定の話だよなぁ?もしかしてサーバー側も設定できたりするの?)
面倒なので今更やる気は起きませんが
おまけ
ここまで至る道
1. Docker Desktop for Windows上でTLSを有効にしたいと思い立つ。
- [本当は怖いDocker for Windows のExpose daemon on tcp://localhost:2375 without TLS]
(https://qiita.com/shioshiota/items/fe0e085826bc2f19f154) - Docker コンテナの設定不備を悪用し仮想通貨発掘マルウェアを拡散する攻撃を確認
2. 証明書を作成すれば動くんじゃね?と気軽に考える。
→ そう単純な話ではなかった。
3. docker-relayを見つける。
- https://devblogs.microsoft.com/commandline/cross-post-wsl-interoperability-with-docker/
- https://github.com/jstarks/npiperelay
→ が、ちゃんと手順通りにしておらず動かなかったので、さらに検索
4. docker-machineでHyperV上にDockerサーバーを作ってみる。
→ 動いた。TLS通信もできた。だけどDocker Desktop for Windowsとは別のDockerサーバーを使ってる。
5. そもそもDocker Desktop for Windowsはどのような仕組みで動いているのか?
→ HyperV上のLinux仮想マシン(DockerDesktopVM)上でDockerサーバーが動いている。
6. DockerDesktopVMにログインしてみる。
→ 仮想マシン名をダブルクリックしても仮想マシンに接続できない。
→ Dockerを利用してDockerDesktopVMにログインする方法を見つける。
- https://blog.jongallant.com/2017/11/ssh-into-docker-vm-windows/
- https://forums.docker.com/t/how-can-i-ssh-into-the-betas-mobylinuxvm/10991/7
パスとか変わってるようなので、今はこれ
docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock -v /usr/local/bin/docker:/usr/local/bin/docker alpine sh
docker run --net=host --ipc=host --uts=host --pid=host -it --security-opt=seccomp=unconfined --privileged --rm -v /:/host alpine /bin/sh
chroot /host
2020/01/22 追記 中を見るだけならこれで良い
docker run -it --rm -v /:/vm alpine chroot /vm
7. ここに証明書を置けば良いのか?
→ ファイルを置いてもDockerDesktopVMを再起動したら初期化される。
→ そもそも TLSポート(TCP:2376)が開いていない。
あとで気づいたが「Expose daemon on tcp://localhost:2375 without TLS」を有効にしても、Dockerサーバーは TCP:2375 ポートを開かない(開くのは unix:///var/run/docker.sock
のみ)Dockerサーバーへはポートフォワーディングで接続してるのではなく、com.docker.proxy.exe
(こいつがTCP:2375をオープンする)経由で接続していた。
8. Docker Desktop for WindowsでTLSポートのオープンや証明書を配置する設定を探す。
→ (現時点では)ない。できない事がわかるIssueは見つけた。
9. 上記のIssueより docker-remote-api-tls を発見。
→ 動いた
10. 再度 docker-relay に挑戦。
→ 動いた