More than 1 year has passed since last update.

注意

本件記事ですが、私の不適切な行動(拾ったスクリプトを検証なく走らせる)が原因です。「dockerは(特に何もしなくとも)危険」との誤解を皆様に与えた点、ご迷惑をおかけいたしました。申し訳ございません。
拡散されている記事を削除するのはさらなる誤解を招きかねないと思いましたので、冒頭に注意を付記しております。以下の記事は、「自分が何してるかをきちんと検証できないとセキュリティホールを生み出す」という意味で参考にして頂ければ幸いです。

追記

Twitterやはてブで言及いただきました皆様、ありがとうございます。
本件はpullしてきたイメージが悪意ある開発者によるものかどうかにかぎらず、不適切な設定をしていると起こり得ます。
※コメント欄に質問への回答という形で、私がそのときに走らせていたイメージの一覧を挙げておりますが、どのイメージも評判あるものだと思います。
皆様におかれましては「あいつやらかしよったでー!」と言いつつ、そっとご自身の設定を見直して頂ければ幸いです。

環境:
ホストOS:Ubuntu 16.04 LTS
Docker:version 1.11.1

皆様お疲れ様です。

早速ですが、Docker使ってますか?
ホストの環境を変えずにポンポンとサービス立てて、要らなくなったらポイすればいいのだから、ホストの環境がぐっちゃぐちゃにならなくて嬉しいですよね。
使えるようになるまでちょっと時間がかかったのですが、今では個人サーバでもともと使っていたGitlabとかRedmineとかいろいろと立ててますよ。
便利ですよねー。

さて、そういう便利さを喜んでいたある日、ちょっと気づきました。
「あれ、勝手に知らんコンテナ立ってる……?」

何があったのか

どこかの誰かが、

  1. 何らかの手法で勝手にコンテナを立てて
  2. ホストにrootとしてSSHでアクセスしていた。

とりあえず気づいた時点でLANケーブル抜いて、後学のためにログだけ取ってクリーンインストールし直しました。

補足

後学のために取ったログとは

docker logs badguy
[root@151d6bf9fff2 /]# vi tmp/.ssh/authorized_keys
[root@151d6bf9fff2 /]#
[root@151d6bf9fff2 /]#

というあっさりしたものです。

何をどうされたのか。

Dockerコンテナ内からホストマシンのルートを取る具体的な方法(あるいは/var/run/docker.sockを晒すことへの注意喚起)
まんま、この手口です。
hub.docker.comからpullしたコンテナの一つに、docker.sockをマウントすることを要求するものがあったので、気軽に与えておりました。
恐らく、

  1. docker.sockをマウントしたコンテナA内のSSHサーバに外部からログイン
  2. コンテナAでdocker エンジンをインストールの上、別のコンテナBを立てる。この時、コンテナBでホストのルートディレクトリをコンテナB内の適当なディレクトリにマウントする。
  3. コンテナBでchrootを使い、2.の適当なディレクトリ(=ホストのルートディレクトリ)にアクセスする
  4. ホストのルートディレクトリ直下に.sshディレクトリを作成して自分のSSH公開鍵を残す
  5. SSHで直接ホストにrootでログイン、自由な遊び場!

ということでしょう。
私も(自分のサーバで)やってみたら、あっさりホストのrootになれました。

何をどうしておけばよかったのか。

ホストのSSHには公開鍵でのログインだけに限定してはいたのですが、rootでのログインを禁止できていませんでした。
あと、docker.sockを見知らぬコンテナに不用意に渡しちゃったのがまずかったですね。root権限のバトンを渡してるのと同じでした。

何をどうしたのか。

  1. ホストのクリーンインストール後、SSHの設定を見直して公開鍵のみ・rootログイン禁止......というより、自ユーザ以外はSSHでログインさせないように設定変更。
  2. fail2banを入れ直して、「root(その他のありがちなアカウントも)でのログインを試行する奴はみんな敵」として一度で永久banとしました。
  3. ホストの/直下に.sshフォルダが作られると気持ちわるいので定期的に削除させるようにcronを設定しています。
  4. 自分でコードを書いていないコンテナにはdocker.sockは渡さないことにしました。このあたりは今後の勉強を踏まえて「Officialのイメージをそのまま使うなら渡す」とかにするかもしれません。

みんなで気をつけること

サーバ立てておいて乗っ取られるとか、恥もいいところです。
これ書くときも「あー、こんなこと書いたら恥ずかしいよな、『おめえ、サーバ立てるの百年早いんだよ、引っ込んでろ』とか言われるのかな」と怯えつつ、ちょっとでもみなさんの役に立つと恥かく価値も少しあるかと思っております。
皆様におかれましては十分ご注意ください。

編集履歴

2016/06/06 10:48 後学のために取ったログを追加、「何をどうされたのか。」の一部がわかりにくい表現だったので、追記した。
2016/06/06 16:24 冒頭に「悪意のあるコンテナでなかったとしても、不適切な設定があれば起こりうる」旨追記。

394contribution

ホストのsshdが万全だったとしても、ホストのルートにアクセスされた時点で設定も変更できてしまうんではないですかね。
docker.sockを晒さないというのはもちろんですが、この場合最初の侵入経路であるコンテナAのsshdがまずかったんではないかと思います。

1182contribution

To pasela
ありがとうございます。
コメントのとおりですね。ホストのroot取れたら変更できます。
コンテナAのsshdについても、各コンテナが何のためにポートを開けているかを見ておくべきでした。

後出しですが、ホスト側のログを見てみると適当なポートに向けてsshが叩かれておりました。
これを「ホストのsshを探してるのかなー」と思って放置していたのですが、もしかしたらdockerのコンテナが持っているsshのポートを探していたのかもしれません。

どのDockerイメージを使ったのでしょうか?もし悪意のあるイメージなら公開停止したほうがいいですし。。

1182contribution

To yoshiokatsuneo@github
ありがとうございます。
入れていたのが、
shipyard(OFFICIAL)
mysql(OFFICIAL) …… redmine,gitlab,ttrssと各々別コンテナでリンク
redis(OFFICIAL) …… gitlabとリンク
redmine(OFFICIAL)
jwilder/nginx-proxy
sameersbn/gitlab
clue/ttrss
といったところです。(もしかしたら抜けがあるかもしれませんが、常時走っていたのはこれらですので)
あいにく、ログが不完全であったこともあって、結局のところどれが入口なのかは不明なのですが、ありそうなのは
・sameersbn/gitlabが立ち上がっているのを確認して、勝手にアカウントを追加(sameersbn/gitlabはデフォルトでは自由にアカウントを作成できるようになっています)。
→SSHのPubkeyを追加→上記の流れなのかなぁというところ
※gitlabが走っているコンテナから上記の流れに本当に持っていけるかどうかは検証していませんので、断定はできません。
※もしそうだったとしても、sameersbnが悪いわけではない。「お外に向いてるサーバでコンテナを走らせる前にきちんと確認しなかった」と言う点で全面的に私に責任があるのです。

docker.sockを公開したのはどのイメージかわかりますか?
不正確な情報で、docker怖いというデマ
が広がるのはあまり良くない気がします。
(インターネット怖い、と同じ程度の文脈で)

gitlabイメージはdocker.sock必要ないと思いますし。。。

1182contribution

明示でdocker.sockを与えたのはnginx-proxyだけだったかと思うのです。
ただ、shipyardもdocker.sockを見に行っているようです。

明示的に指定しない限り共有できないはずでは?

1182contribution

ありがとうございます。
うまく説明できなくて誤解招いてしまいましたか。
コマンドライン上でdocker run〜という形で共有を指定してなかったのですが、
http://shipyard-project.com/deployの「start_proxy()」のようにスクリプト内で書いてあるのには気づいてなかったのです。
それは私自身の認識としては明示してないのに! と思うところですけども、実際は共有されていた、という意味です。
※ただ、もちろんshipyardが悪いというわけではありません。

ネットに落ちてるスクリプトを何をするものか調べずに実行しただけですね。
sshをインストールしたらsshされた、と同じで、Dockerの安全性とは全く関係ないですね。

415contribution

貴重な情報ありがとうございました。可能であれば伺いたいのですが(ためしてほしいのですがw)、そのイメージをDocker Security Scanningにかけるとどうなりますか?
見た感じではCVEとの照合で脆弱性を洗い出すっぽいのですが、不適切な設定もみつけてくれるのかなーと興味を持ちました。
自分でやれよという感じですが、まだイメージを持ってたら是非w

そもそもShipyardが何かご存知なのでしょうか?。。。

1182contribution

yoshiokatsuneo@github
ありがとうございます。
そうですね、不適切な設定(sshでrootログインが可能だった)や行動(よく確認せずにスクリプトを走らせる)によるものなのでdockerのせいという表現はよくなかったなと思います。
私の書き方や思い込みで誤解させた方も多いですね。
本記事は拡散もされておりますので、削除も困難かと思います。
私にできるのは本記事の再編集(問題あるのはdocker自体ではなく、不適切な行動である点)と自身のツイートにて同様のツイートを行うことだと考えます。

ken5scal
ありがとうございます。
上の例でいうところのコンテナBの方であれば、centosのオフィシャルイメージですし、コンテナAの方はyoshiokatsuneo@githubさんとのやりとりの通り私の勘違いの可能性が高いです。
ですので、上記のテストについては可能ならば行いますが、さほど期待されるような結果は出ないかもしれません。

415contribution

おお、なんと...では、自分でためしてみます。ありがとうございます

1182contribution

yoshiokatsuneo@github
shipyardですが、コンテナ・イメージ管理を便利に行える仕組み、との理解でした。
コンテナの起動が出来るなどの特徴から、上のようなことは気付くべき(というより、使うべきではなかった)でしたね。
実際、その程度の認識だったから拾って走らせたわけです。

貴重な経験・失敗を公開されているのは素晴らしいと思います。
ただ、せっかくの貴重な知見なのに、変に誤解や推測が広まっていくように感じましたので
コメントさせていただきました。

1182contribution

yoshiokatsuneo@github
お付き合いいただき、ありがとうございます。
コメントのおかげで、私の考え方も変われたかと思います。このやりとりがなければ、私の注意力も大して改善されなかったでしょう。
今後ももしお気づきの点があれば、コメント頂ければ幸いです。

628contribution

もしかして、https://github.com/shipyard/shipyard の Quick Start をやられたのでは?
https://shipyard-project.com/deploy
これ見ると、start_proxy() で

    ID=$(docker run \
        -ti \
        -d \
        -p $PROXY_PORT:$PROXY_PORT \
        --hostname=$HOSTNAME \
        --restart=always \
        --name $PREFIX-proxy \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -e PORT=$PROXY_PORT \
        --volumes-from=$PREFIX-certs $TLS_OPTS\
        shipyard/docker-proxy:latest)

ってなってて、記事のような難しいことしなくても、
ホストのIP:2375 向けて docker コマンドが普通に使えてしまうような。。。

1182contribution

To A-I
ありがとうございます。
はい、そうですね。Quick Startのコマンドを入力しておりました。
>ホストのIP:2375 向けて docker コマンドが普通に使えてしまうような。。。
個人的には力量が不足しているため具体的な操作はわかりませんが、できるのかもしれません。

628contribution
$ export DOCKER_HOST=tcp://<host-ip>:2375
$ docker run -it --rm -v /:/host --privileged ubuntu bash

とかですね。
外向けのサーバー内で Docker デーモンを使う時は気をつけないといけません。
また、
curl -s https://shipyard-project.com/deploy | bash -s
のような外部のスクリプトを直接起動してしまうやり方も怖いです。

1959contribution

>ホストのIP:2375 向けて docker コマンドが普通に使えてしまうような。。。

そんな感じがしますね。

ホストはFirewall無しの運用で、インターネット上からここで-pで開けられたポートにアクセスできちゃってたのでしょうか?

628contribution

titilat さんの不注意や firewall も気になりますが、
ここで私が一番問題だと思ったのは Shipyard です。
たとえ、Docker デーモンをローカルの docker.sock のみや TLS で運用する設定にしていたとしても、素の(デフォルトの設定の)Shipyard のインストールスクリプトが Docker API の 2375 ポートを晒してしまう。
docker.sock をコンテナ経由で晒すよりももっと危険な Docker デーモン自体を直接晒している。
しかも、警告もなく、スクリプト内で気が付きにくい状態でやっている。Shipyard ヤバイです。

1182contribution

お二方のコメント、ありがとうございます。
to A-I
あいにく、サーバの外からのポートを制限したため外部の端末からのテストができないのですが、同一ホスト内でdockerコマンドの実行権限のない新規ユーザでご提示の手順を踏んだ場合、コンテナBとしてubuntuのbashが立ち上がりました。
chroot以降の手順でホストのrootとなれる点も同様です。

to NewGyu
件のサーバは当時UFWを立ち上げていたかと思うのですが、クリーンインストールにて環境を損なっており、確証がもてません。申し訳ございません。
件のサーバは外部からコンテナのコントロールができるようにしていたため、恐らく操作可能であったと思います。

1959contribution

@A-I その話になるとDocker Swarmもきな臭くなってしまう気がしますね。

https://docs.docker.com/swarm/plan-for-production/#network-access-control

Swarm nodes:
Inbound 80/tcp (HTTP). This allows docker pull commands to work. If you plan to pull images > from Docker Hub, you must allow Internet connections through port 80.
Inbound 2375/tcp. This allows Engine CLI commands direct to the Docker daemon.
Inbound 22/tcp. This allows remote management via SSH.

Shipyard は今回はじめて知ったのですけど、Dockerクラスタを扱うという目的で、管理対象ノードにTCP経由でのDocker APIの公開(マネージャー側からの)を求めているのですね。このやり口はDocker Swarmも似たようなものなので、やはり締めるポートは締めると言うところだったのかなぁと。

628contribution

@NewGyu
なるほど。
私も Shipyard がクラスタを管理できるのは初めて知りましたが、内部で Docker Swarm を使うために、このように無理矢理 2375 ポートを開けているのですね。
まさか、Swarm が未だに TLS で運用出来ないとは思ってませんでした。
(元々 Swarm は後付のムリクリ感満載なので使ってませんでしたw)
どちらにしてもポートを閉めたりIP制限をしないとならなかったということなんですね。

追記:
https://docs.docker.com/swarm/plan-for-production/#network-access-control

If your Swarm cluster is configured for TLS, replace 2375 with 2376, and 3375 with 3376.

628contribution

すみません。蛇足ですが、
Docker Hub にあるもので AUTOMATED BUILD は(Dockerfile が確定していて)比較的安心と思っておりましたが、
AUTOMATED BUILD の Dockerfile は最後に Auto で Build に成功した際のもので、
他のものと同様に実際にはリモートから docker push 出来てしまうので、
AUTOMATED BUILD は利便性だけで、安全性とはあまり関係無いようです。

119contribution

こちらの記事、だいぶ時間が経っていますが質問があります

コメントでのやりとりを見る限り、
一番問題だったのはdocker apiのポートにインターネット経由で外部からアクセスできるようになっていたのが問題だったのではと思いましたがどうでしょう?

rootログインの許可に関わらず、鍵認証のsshが突破されるのはちょっとかんがえにくいのかなと
また、docker.sock がマウントされていたとしても、そのコンテナ上で動くアプリケーション経由で任意のコマンドを叩けるような脆弱性をつく必要がありますよね

そう考えると単純に外部からポートスキャンをかけて2375ポートが空いていたらdocker apiのコマンドを送る、という方が攻撃としてはシンプルなのかなと思いました

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.