SlideShare a Scribd company logo
Dockerセキュリティ:
今すぐ役に立つテクニックから,次世代技術まで
須田 瑛大 (Docker Tokyo / NTT)
1
Japan Container Days v18.12 (2018/12/05)
https://www.slideshare.net/AkihiroSuda
自己紹介
2
● Docker Tokyo Meetupオーガナイザ
○ 次回開催は近日中に https://dockerjp.connpass.com/ にて告知
● 所属: 日本電信電話株式会社 ソフトウェアイノベーションセンタ
● コンテナ関連OSSのメンテナ(いわゆるコミッタ)を務めている
○ Moby (≒Docker)
■ Dockerの元になっているOSSプロジェクト
○ BuildKit
■ 次世代 docker build
○ containerd
■ Kubernetesなどで利用できる次世代コンテナランタイム
はじめに
3
● Dockerを使うことで,アプリケーションに脆弱性があっても,その実際
の影響範囲を限定することが出来る
● Dockerに関係するセキュリティ技術を理解し,応用することで,更にセ
キュリティを強化することが出来る
○ 完全に理解する必要はない
■ そもそも不可能
○ 複数の技術を組み合わせて使うと,その内1つに脆弱性や理解の誤りがあって
も,他の技術でカバー出来ることがある
お話する内容
4
第1章 イメージ
● docker build --secret
● docker build --ssh
● Clair
● Microscanner
第3章 ランタイム
● Seccomp / Apparmor / SELinux
● バインドマウント時の注意
● docker run --user
● dockerd --userns-remap
● Rootlessモード
● Falco
第4章 代替ランタイム
● Kata Containers
● gVisor
● Nabla Containers
第2章 Dockerソケット
● TLS
● SSH
● AuthZ
第1章 イメージ
5
プライベートリポジトリを用いるビルド
6
● Dockerfileの中から,プライベートなGitリポジトリやS3にアクセスする
にはどうすればよいか?
● 鍵ファイルをCOPY/ADDして,git等を実行し,後で鍵を削除すれば,イ
メージに鍵を残さずにビルドできそうに思える
FROM ...
COPY id_rsa ~/.ssh
RUN git clone ssh://...
RUN rm –f ~/.ssh/id_rsa
プライベートリポジトリを用いるビルド
7
● Dockerfileの中から,プライベートなGitリポジトリやS3にアクセスする
にはどうすればよいか?
● 鍵ファイルをCOPY/ADDして,git等を実行し,後で鍵を削除すれば,イ
メージに鍵を残さずにビルドできそうに思えるが,駄目
FROM ...
COPY id_rsa ~/.ssh
RUN git clone ssh://...
RUN rm –f ~/.ssh/id_rsa
Dockerfile1行ごとにtarレイヤが作られる
鍵を含むレイヤが作られる
鍵を隠す(whiteout)情報を含んだレイヤが
作られるが,鍵を含むレイヤ自体を消しては
くれない!
自身以外が参照可能なレジストリにイメージをpushすると,鍵が漏れる!
プライベートリポジトリを用いるビルド
8
● Docker 18.09から利用可能な docker build --secret 及び docker
build --ssh を使うとよい
● --secret: 鍵ファイルをビルド用コンテナ中にマウントできる.鍵ファ
イルは出力イメージ内には残らない.
● --ssh: --secretに似ているがSSH秘密鍵に特化.ssh-agentと組み合わ
せて,パスフレーズ付きの秘密鍵を使うことも可能.
docker build --secret
9
# syntax = docker/dockerfile:1.0-experimental
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials 
aws s3 cp s3://... ...
$ export DOCKER_BUILDKIT=1
$ docker build --secret id=aws,src=$HOME/.aws/credentials ...
BuildKitモードを有効化
secretのIDとパスを指定
Docker 18.09ではexperimental扱いなので,1行目に # syntax = ...の指定が必要
上で指定したsecretをtargetにマウント
(出力イメージには残らない)
補足: export DOCKER_BUILDKIT=1 について
10
● クライアント側でexport DOCKER_BUILDKIT=1するとBuildKitモードが有
効になる
○ あるいはデーモン側の /etc/docker/daemon.jsonに
{"features":{"buildkit":true}} と記述しても有効化できる
● BuildKit: セキュリティ,並行性,キャッシュ効率を重視した次世代
docker build バックエンド
○ --secret 及び --ssh をサポート
○ Dockerfileの命令間の依存性をDAGとして表現し,可能な限り各命令
を同時実行
○ キャッシュ機能を強化
● 詳しくは https://github.com/moby/buildkit 参照
docker build --ssh
11
# syntax = docker/dockerfile:1.0-experimental
FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && 
ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh git clone github.com/プライベートなrepo
$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(パスフレーズ入力)
$ export DOCKER_BUILDKIT=1
$ docker build --ssh default=$SSH_AUTH_SOCK
ssh-agentのソケットまたは秘密鍵ファイルを指定可能
(ssh-agentのソケットを使うなら,パスフレーズつきの秘密
鍵を扱える)
Docker 18.09ではexperimental
古いバージョンのDockerを用いている場合
12
● docker build --secret 及び docker build --ssh はDocker
18.09から利用可能
● 今すぐ18.09にアップグレードできない場合のワークアラウンド
○ マルチステージビルド (右図)
○ docker build --squash
(レイヤを1つにまとめる)
○ historyに残らない特殊なbuild-arg
(FTP_PROXYなど)を目的外利用する
https://github.com/moby/moby/pull/36443
● ただし,古いバージョンのDockerは
メンテナンスが終了しているため,
なるべく早期にDocker 18.09に移行すべき
FROM ...
COPY id_rsa ~/.ssh
RUN git clone ssh://… /foo
FROM ...
COPY --from=0 /foo /foo
イメージスキャナ
13
● コンテナイメージ中に,古いパッケージが含まれていると攻撃されること
がある
● Clair, Microscanner, Anchore, Dagdaなどのスキャナを用いると,既知の
脆弱性を含むパッケージがイメージに含まれていないか検査できる
● スキャナで全ての脆弱性が見つかるわけではない
● スキャナが警告を出したとしても,必ずしも問題ではない
○ 当該の脆弱性を含む機能を使っていないかも知れない
○ コンテナ化している場合は,実用上問題ない脆弱性かも知れない
○ パッケージのバージョン番号が古く見えても,ディストリビュータが修正
パッチをバックポートしているかも知れない
● CoreOS (現Red Hat)が開発するOSSのイメージスキャナサーバ
● QuayやGitLab CI/CDにも採用されている
○ GitLab設定例: https://docs.gitlab.com/ee/ci/examples/container_scanning.html
● Clair本体の他に,クライアントが必要
○ clairctl, klar, reg, clair-scannerなど
○ Harborもクライアントとして動作できる
Clair
14
Clair
PostgreSQL
クライアント
Alpine/Red Hat/Ubuntu等
のCVEデータベース
レジストリ
RESTによるリクエスト
イメージ取得 脆弱性情報取得
脆弱性情報等格納
Microscanner
15
● Aqua Securityが提供するイメージスキャンサービス
○ 無償ではあるがプロプライエタリ,要ユーザ登録
○ マルウェアスキャンなど,全ての機能を使うには有償製品の購入が必要
● Dockerfileに1行付け足すだけでスキャンできる
○ Clairと異なり,自分でサーバを立てなくてよい
● CircleCI用のOrbも提供されている
RUN apk add --no-cache ca-certificates && update-ca-certificates && 
wget -O /microscanner https://get.aquasec.com/microscanner && 
chmod +x /microscanner && /microscanner <token> && rm -rf /microscanner
その他のイメージスキャナ
16
● Anchore Engine
○ アーキテクチャはClairに似ているが,公式クライアント及び公式Jenkinsプラ
グインが用意されており,使いはじめやすい
● Dagda
○ 公式クライアントあり
○ ClamAVを用いたマルウェアスキャンも出来る
● Vuls
○ コンテナ専用スキャナではないが,(イメージそのものではなく)実行中コン
テナもスキャンできる
● OpenSCAP
○ コンテナ専用スキャナではないが,コンテナイメージもスキャンできる
○ RHELイメージ専用 (CentOS不可)
○ サーバ不要
● その他,有償・プロプラではTwistlockなど
イメージスキャナ比較
17
● kong:1.0.0rc1 に対する2018年秋のスキャン結果
https://kubedex.com/follow-up-container-scanning-comparison/
busybox libressl curl
画像出典: 上記URL
イメージスキャナ比較
18
● どれがよいとは一概には言えない
● False positive (誤検出) もある
● イメージスキャナを過信せず,
なるべく依存パッケージを減らして
シンプルなイメージを作るのがよい
○ 脆弱性を踏みにくくなるだけでなく
False positiveで悩むことも減る
画像出典: 前ページ記載URL
第2章 Dockerソケット
19
Dockerソケット ( /var/lib/docker.sock )
20
● DockerデーモンがREST APIを待ち受けるソケット
● デフォルトでの所有権は root:docker
● docker グループに一般ユーザを追加すると,一般ユーザでも docker
クライアントを実行できるようになる
● Dockerソケットへアクセス権を与えることは,ホストのroot権限を与える
ことに等しい
○ docker run --privileged を実行すると一切の保護が無効
○ docker run -v /:/host … を実行すればホスト上の任意のファイルを
root権限で読み書きできる
Dockerソケット ( /var/lib/docker.sock )
21
● インターネットへ向けてTCPでlistenするなら,TLSの設定が必須
● TLSを設定できていないDockerホストを対象とした攻撃が知られている
● 仮想通貨を採掘するコンテナを立ち上げる攻撃が近年は盛ん
● ホストのroot権限を奪われるので,ホスト上の任意のファイルを読み
書きされたり,同一ネットワーク上のパケットを盗聴されたりする
● 他のサイトへの攻撃の踏み台としても使われうる
Dockerソケットへの攻撃事例
22
出典: https://blog.trendmicro.co.jp/archives/19773
TLSを用いたDockerソケットの保護
23
TLSの設定方法: https://docs.docker.com/engine/security/https/
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
..................................................................
..................................................................
........................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 out
ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be
incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name
or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker
Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:Sven@home.org.au
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.............................
........................................++
....................................
.............................................................++
e is 65537 (0x10001)
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem
-out server.csr
$ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >>
extfile.cnf
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem
-CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile
extfile.cnf
Signature ok subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth >> extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem
-CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile
extfile.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ rm -v client.csr server.csr
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
$ dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem
--tlskey=server-key.pem -H=0.0.0.0:2376
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem
--tlskey=key.pem -H=$HOST:2376 version
出典: 上記URL
TLSを用いたDockerソケットの保護
24
TLSの設定方法: https://docs.docker.com/engine/security/https/
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
..................................................................
..................................................................
........................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 out
ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be
incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name
or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker
Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:Sven@home.org.au
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.............................
........................................++
....................................
.............................................................++
e is 65537 (0x10001)
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem
-out server.csr
$ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >>
extfile.cnf
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem
-CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile
extfile.cnf
Signature ok subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth >> extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem
-CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile
extfile.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ rm -v client.csr server.csr
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
$ dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem
--tlskey=server-key.pem -H=0.0.0.0:2376
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem
--tlskey=key.pem -H=$HOST:2376 version
出典: 上記URL
SSHを用いたDockerソケットの保護
25
● TLSを正しく設定するのは難しい
● Docker 18.09では,export DOCKER_HOST=ssh://ユーザ@ホスト する
とTLSの代わりにSSHを用いてリモートDockerホストに接続できる
○ 古いバージョンのDockerでも,自分で ssh -L
foo.sock:/var/run/docker.sock プロセスを管理すればSSH接続可
■ ssh -L には接続をkeep-aliveできるメリットもある
● SSHはホストにDockerをインストールする前に何れにせよ設定するだろ
うから,TLSと違って追加の手間が発生しない
● 単に `ssh -l ユーザ ホスト – docker` を実行する場合と異なり,ク
ライアントの ~/.docker/config.json に保存されているレジストリ
認証情報や,ビルドコンテキストにアクセスすることが可能
AuthZプラグイン
26
● DockerのAuthZプラグインを使うと,ソケットを用いて可能な操作を限定
できる
○ REST APIのHTTP Method, URIに応じてフックをかけることが出来る
○ https://docs.docker.com/engine/extend/plugins_authorization/#api-schema-an
d-implementation
● プラグインの例: Casbin AuthZ Plugin, HBM (Harbormaster), Twistlock
AuthZ Broker
○ いずれも要素技術を提供するのみで,完成したソリューションではない
● Docker Enterprise EditionのUniversal Control Plane (UCP)を用いると,
似たことを簡単に出来る (有償・プロプラ)
● KubernetesやOpenShiftのほうが,AuthN・AuthZが充実している
第3章 ランタイム
27
コンテナの仕組み
28
● コンテナは基本的にはNamespaces,Capabilities,Cgroups で出来ている
● Namespaces (例: Mount NS, Network NS, PID NS…)
○ ファイルシステムやネットワークなどを隔離
● Capabilities (例: CAP_CHOWN, CAP_NET_ADMIN, CAP_SYS_ADMIN…)
○ root権限を細かいフラグに分割したもの
● Cgroups (例: CPU controller, Memory controller, Device controller…)
○ CPUやメモリの使用量や,デバイスファイルへのアクセスを制限
● 加えて,SeccompやApparmrorなどを用いて更にセキュリティを強化
○ Dockerではデフォルトで有効になっているが,Kubernetesではデフォルトで
は無効なので要注意
Seccomp
29
● 呼び出し可能なシステムコールを制限
● NamespacesでもCapabilitiesでも制限できないが,Seccompで制限できる
システムコールとしては keyctl (カーネル キーリング)などがある
● デフォルトより厳しい設定を与えてコンテナを起動すると,コンテナや
カーネルに脆弱性があっても影響を軽減することができる
○ docker run --security-opt seccomp=PROFILE.json
○ デフォルトのプロファイル:
https://github.com/moby/moby/blob/master/profiles/seccomp/default.json
● 後述するNabla Containersはseccompを用いて呼び出し可能なシステム
コールを7つにまで絞っている
Apparmor
30
● アクセス可能なファイルを制限できる
● デフォルトのプロファイルでは /proc や /sys へのアクセスを制限
○ https://github.com/moby/moby/tree/master/profiles/apparmor
○ OCI Runtime Specで定義されているMaskedPathと似ている部分もあるが,
より強力
● より厳しく制限する例:
docker run --security-opt apparmor=PROFILE
https://docs.docker.com/engine/security/apparmor/#nginx-example-profile
● コンテナだけではなくDockerデーモン自体がアクセス可能なファイルの
制限も可能であるが,プロファイルは3年近く更新されていない
○ https://github.com/moby/moby/tree/master/contrib/apparmor
○ コントリビューション募集中
SELinux
31
■ Red Hat系でApparmorの代わりに使われている機構
○ Apparmorがファイルのパスを対象としてアクセスを制御するのに対し
,SELinuxは「タイプ」などのラベルを対象とする
○ SELinuxはデフォルトでは有効にならない ( /etc/docker/daemon.json
に {“selinux-enabled”: true} を設定する必要がある)
● SELinuxが有効になっていると,コンテナは container_tタイプで
,Dockerデーモン自体はcontainer_runtime_t タイプで実行される
○ /proc/$PID/attr/current で確認できる
○ ポリシーの詳細: https://github.com/containers/container-selinux
バインドマウント時の注意
32
● docker run -v /hostpath:/containerpath を用いると,ホスト
のファイルシステムをコンテナ内にマウントできる
● 予期しないファイル改竄を防ぐには,read-onlyでマウントするのが良い
○ docker run -v /hostpath:/containerpath:ro
● ただしread-onlyはsubmountを再帰的にread-onlyにしないことに注意
○ /mnt/foo にマウントされているファイルシステムをコンテナに読ませたい
時,docker run -v /mnt:/mnt:ro すると,/mnt/foo はread-onlyに
ならない
○ Docker 19.03では,再帰的マウントの無効化が可能になる予定
■ docker run --mount
type=bind,src=/hostpath,target=/containerpath,ro,bind-nonrecursive
■ 再帰的なread-onlyマウントのサポートには時間がかかりそう (カーネルの仕様の
ため)
バインドマウント時の注意
33
● SELinuxが有効化されている場合,ホストのファイルシステムをバインド
マウントしても,ほとんど読み書きできない
○ /usr および /etc の一部を読めるくらい
● バインドマウントしたファイルを読み書きするには 対象ファイルに
chcon -Rt container_file_t してからdocker runする
○ 読むだけならcontainer_share_t
○ 永続化には semanage fcontext と restorecon を用いる
○ あるいはコンテナ側のタイプを変える( docker run --security-opt
label=type:TYPE )
○ docker run -v /hostpath:/containerpath:z (及び:Z) は非推奨
● SELinuxを無効化するには --security-opt label=disable
○ “石川さん&Walshさん ごめんなさい” https://stopdisablingselinux.com/
コンテナ内の実行ユーザ (docker run --user)
34
● ` docker run -user ユーザ名(またはUID)` を用いると,コンテナ
内のユーザを非rootに変更することができる
○ Dockerfileの USER 命令でデフォルトのユーザを指定することも出来る
● バインドマウントしたファイルシステムへのアクセスを容易に制御できる
● コンテナランタイムにバグがあっても,ホストへの影響を軽減できる
● 注意: su, sudoの類が無効になっているか,確認すること
○ Alpine 3.8の場合,Busyboxのsuはsetuidされていないため無効であるが
,shadowパッケージに含まれるsuはパスワード無しで実行可能
■ passwd -l でrootアカウントをロックするか,suを削除すると良い
備考: Kubernetes (1.12)での設定方法
35
● Seccomp: container.seccomp.security.alpha.kubernetes.io/… アノテーション
● Apparmor: container.apparmor.security.beta.kubernetes.io/… アノテーション
● SELinux: securityContext.seLinuxOptions
● --user: securityContext.runAsUser
● 詳細は
https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ や
https://kubesec.io/ など参照
● PodSecurityPolicy (beta)を用いると,ネームスペース内やクラスタ内の
全てのPodに対してポリシーを設定できる
○ https://kubernetes.io/docs/concepts/policy/pod-security-policy/
dockerd --userns-remap
36
● userns-remap モードでDockerデーモンを実行すると,全てのコンテナが
非rootユーザで実行される
○ Dockerデーモン自体はrootで実行される
● User Namespaceを用いているので,コンテナの中からは,あたかもroot
であるように見える
● /etc/subuid,subgid に dockremap:100000:65536 と書いておくと
コンテナ内のUID 0→ コンテナ外のUID 100000,
コンテナ内のUID 1→ コンテナ外のUID 100001,...
コンテナ内のUID 65535→ コンテナ外のUID 165535 にマップされる
● Kubernetesでは1.14ころで利用可能となる見込み
○ https://github.com/kubernetes/enhancements/issues/127
Docker自体を信頼できるのか?
37
● Docker自体,Kubernetes自体,及び関連コンポーネントに,過去にさま
ざまな脆弱性が見つかっている
● Docker “Shocker”(2014)
○ 不正なコンテナに,ホストのファイルシステムにアクセスされる (余計な
CAP_DAC_READ_SEARCHがデフォルトで与えられていた)
● Docker CVE-2014-9357
○ 不正なDockerfileをビルドすると,ホストのroot権限で任意のバイナリを実行
される (LZMAアーカイブの扱いのバグ)
● containerd #2001
○ 不正なイメージをpullすると,イメージを実行しなくてもホストの /tmp が削
除される
Docker自体を信頼できるのか?
38
● Kubernetes CVE-2017-1002101, CVE-2017-1002102
○ 不正なボリュームを通じてホストのファイルシステムにアクセスされる
● Kubernetes CVE-2018-1002105 (2018/12/4 公表)
○ 不正なAPI呼び出しを通じてcluster-adminを奪取される (→privilegedコンテ
ナを通じてホストのrootを奪取される)
● Git CVE-2018-11235
○ 不正なリポジトリをcloneすると,ホストのroot権限で任意のバイナリを実行
される
○ KubernetesのgitRepoボリュームを通じてトリガ可能
https://staaldraad.github.io/post/2018-06-03-kubernetes-root-with-gitrepo-
volume/
Rootlessモード
39
● コンテナのみならず,Dockerデーモンも含め,全てを非rootユーザで実行
する
○ Dockerデーモンやcontainerd,runcに脆弱性があったとしても,実際の影響
を軽減できる
■ 例えば,カーネルを書き換えられて検出不可能なマルウェアを仕込まれたり
,ARPスプーフィングされたりする危険性を減らせる
■ 複数ユーザを用いることで,複数テナントを安全に隔離して同一ノードで実行す
ることも可能
● Docker 19.03ないし19.09から利用可能となる見込み
○ https://github.com/moby/moby/pull/38050
○ “Usernetes” プロジェクトの一部としてバイナリ入手可能
https://github.com/rootless-containers/usernetes
■ 一般ユーザのホームディレクトリにインストールできる
■ Kubernetesも動く
Rootlessモード
40
● Overlayストレージドライバには非対応
○ Ubuntuでは例外的に対応 (カーネルにパッチが当てられている)
○ 将来的には,FUSEによるOverlayFS実装に対応予定 (要カーネル4.18)
○ reflinkを用いたCopy-on-Writeには対応 (要XFS等)
● Cgroups関連の設定は今のところは無視される
○ 将来的にはpam_cgfs.soやCgroups v2 nsdelegateなどを用いて対応予定
● ネットワークが遅い
○ Bridgeとvethを用いず,slirp4netnsを用いてユーザモードでエミュレートす
るため
■ qemu -netdev userのコードが元になっている
■ Unprivileged LXCと異なり,SETUID/SETCAPバイナリ不要
○ Travis上でのiperf3を用いたベンチマークでは9.21 Gbpsほど
https://github.com/rootless-containers/slirp4netns#benchmarks
Falco
41
● コンテナの予期しない動作を監視するツール (CNCF Sandbox)
○ コンテナ内の予期しないプロセス(シェルなど)の起動,デバイスファイルへ
のアクセス,privilegedコンテナの起動などを検知
○ Slack, Fluentd, NATSなどにアラートを通知
○ BPFを用いて実装されている
● 例: コンテナ内でのbash起動を検知
https://github.com/falcosecurity/falco/wiki/Falco-Examples
- rule: run_shell_in_container
condition: container and proc.name = bash and
spawned_process and proc.pname exists and not proc.pname
in (bash, docker)
...
第4章 代替ランタイム
42
OCIランタイム
43
● コンテナの機能自体は,DockerではなくOCIランタイムに実装されている
● OCIランタイムは,Linux Foundation傘下のOpen Container Initiative
(OCI)が策定するOCI Runtime Specを実装している
● デフォルトではruncが使われるが,Kata, gVisor, Nablaなど他のランタイ
ムにも簡単に切り替えることが出来る (docker run --runtime=...)
Docker
containerd
OCIランタイム
OCIランタイム
44
● OCIランタイムはKubernetesでも用いられる
● KubernetesとOCIランタイムの間には,CRIランタイムが挟まる
○ CRIランタイム: dockershim, containerd, CRI-Oなど
○ イメージやスナップショットの管理など,OCIランタイムの外回りの機能を実
装
Docker
containerd
OCIランタイム
Kubernetes
CRIランタイム
Kata Containers
45
● OpenStack Foundationが開発するOCIランタイム
○ Intel Clear ContainersとrunVとが合流して発足
● QEMU/KVMを用いて強固なアイソレーションを実現
○ ファイルシステムは9pでマウント
○ クラウドにデプロイする場合は,nested virtualization対応またはベアメタル
のインスタンスを使う必要がある
■ EC2: i3.metal がベアメタル対応
■ Azure: Dv3, Ev3がnested virtualization対応
■ GCE: nested virtualizationにはbetaとして対応
● 起動にやや時間がかかる (せいぜい1秒-2秒程度)
その他のVM系ランタイム
46
● runq
○ IBM Cloudの一部サービスで使われているOCIランタイム
○ QEMU/KVMベースだがシンプルな構成
● Firecracker
○ AWS LambdaやFargateで使われている
○ ChromeOS crosvmに由来する軽量なVMを使用.125msで起動するとのこ
と.Rustで書かれている.
○ 今のところOCIランタイムとしては動作しないが,containerdプラグインが用
意されている
■ Firecrackerの上でruncを動かす設計
● Frakti, Virtlet
○ CRIランタイム
gVisor (runsc)
47
● Googleが開発するOCIランタイム.App Engineにて使われている.
● Linuxカーネルをユーザランドで再実装することで,セキュリティを強化
○ User Mode Linuxに似ているが,Linuxのコードを使わずにGoでスクラッチか
ら実装している
○ Ptraceによる実装と,KVMによる実装とがある
○ ホストのカーネルに対して呼び出すシステムコールは50程度
● 起動時間は早いが,システムコール呼び出しのオーバヘッドが大きい
● 互換性の問題がある
Nabla Containers (runnc)
48
● IBMが開発するOCIランタイム
● NetBSDベースのユニカーネルを用いることで,コンテナが呼び出すシス
テムコール数を削減し,カーネルの脆弱性を踏む可能性を削減
○ read, write, exit_group, clock_gettime, ppoll, pwrite64,
pread64の7つに制限
● Linux用のELFバイナリをそのまま実行できるわけではなく,Nabla用に移
植する必要がある
● 起動時間は短い.runcに勝ることもある.
○ https://www.slideshare.net/KoheiTokunaga/ss-123664087
ランタイムの比較
49
出典: https://schd.ws/hosted_files/kccncchina2018english/ad/k8sChina2018-nabla.pdf
Nabla開発者によるベンチマーク
ランタイムの比較
50
出典: https://schd.ws/hosted_files/kccncchina2018english/ad/k8sChina2018-nabla.pdf
ランタイムの比較
51
利点 欠点
runc ● 速い ● アイソレーションが弱い
Kata Containers ● アイソレーション
が極めて強い
(VM分離)
● やや起動が遅い
● 仮想化をサポートするホ
ストが必要
gVisor ● アイソレーション
が強い
(カーネル分離)
● 遅い
● Linuxカーネルとの互換
性が低い
Nabla Containers ● アイソレーション
が強い
(カーネル分離)
● コンテナの移植が必要
● Linuxカーネルとの互換
性が低い
ランタイムの比較
52
利点 欠点
runc ● 速い ● アイソレーションが弱い
Kata Containers ● アイソレーション
が極めて強い
(VM分離)
● やや起動が遅い
● 仮想化をサポートするホ
ストが必要
gVisor ● アイソレーション
が強い
(カーネル分離)
● 遅い
● Linuxカーネルとの互換
性が低い
Nabla Containers ● アイソレーション
が強い
(カーネル分離)
● コンテナの移植が必要
● Linuxカーネルとの互換
性が低い
許容できるならKataがよさそう?
(もしくはFirecracker)
まとめ
53
まとめ
54
● イメージ
○ イメージ中にGitHubやAWSなどの鍵が紛れ込まないよう,注意する
○ docker build --secret,docker build --sshが安全で便利
○ Clair, Microscannerなどのスキャナを用いて脆弱性を検知できる
○ イメージはなるべくシンプルに保つ
● Dockerソケット
○ Dockerソケットへのアクセス権を与えることは,ホストのroot権限を与える
ことと同じ
○ TLSまたはSSHを用いて厳重にアクセス制限する必要がある
まとめ
55
● ランタイム
○ Seccomp, Apparmor, SELinuxなどを使って,コンテナの動作を厳しく制限で
きる
○ docker run --user や dockerd --userns-remap を用いることで,コ
ンテナを非rootユーザで実行できる
○ 次期バージョンからはDockerデーモン自体も非rootユーザで実行できるよう
になる
■ https://github.com/rootless-containers/usernetes
● 代替ランタイム
○ 代替ランタイムを用いることで,より強いアイソレーションを実現可能
○ Kata Containers: VMを利用
○ gVisor, Nabla Containers: ユーザモードカーネルを利用

More Related Content

Dockerセキュリティ: 今すぐ役に立つテクニックから,次世代技術まで

  • 1. Dockerセキュリティ: 今すぐ役に立つテクニックから,次世代技術まで 須田 瑛大 (Docker Tokyo / NTT) 1 Japan Container Days v18.12 (2018/12/05) https://www.slideshare.net/AkihiroSuda
  • 2. 自己紹介 2 ● Docker Tokyo Meetupオーガナイザ ○ 次回開催は近日中に https://dockerjp.connpass.com/ にて告知 ● 所属: 日本電信電話株式会社 ソフトウェアイノベーションセンタ ● コンテナ関連OSSのメンテナ(いわゆるコミッタ)を務めている ○ Moby (≒Docker) ■ Dockerの元になっているOSSプロジェクト ○ BuildKit ■ 次世代 docker build ○ containerd ■ Kubernetesなどで利用できる次世代コンテナランタイム
  • 3. はじめに 3 ● Dockerを使うことで,アプリケーションに脆弱性があっても,その実際 の影響範囲を限定することが出来る ● Dockerに関係するセキュリティ技術を理解し,応用することで,更にセ キュリティを強化することが出来る ○ 完全に理解する必要はない ■ そもそも不可能 ○ 複数の技術を組み合わせて使うと,その内1つに脆弱性や理解の誤りがあって も,他の技術でカバー出来ることがある
  • 4. お話する内容 4 第1章 イメージ ● docker build --secret ● docker build --ssh ● Clair ● Microscanner 第3章 ランタイム ● Seccomp / Apparmor / SELinux ● バインドマウント時の注意 ● docker run --user ● dockerd --userns-remap ● Rootlessモード ● Falco 第4章 代替ランタイム ● Kata Containers ● gVisor ● Nabla Containers 第2章 Dockerソケット ● TLS ● SSH ● AuthZ
  • 7. プライベートリポジトリを用いるビルド 7 ● Dockerfileの中から,プライベートなGitリポジトリやS3にアクセスする にはどうすればよいか? ● 鍵ファイルをCOPY/ADDして,git等を実行し,後で鍵を削除すれば,イ メージに鍵を残さずにビルドできそうに思えるが,駄目 FROM ... COPY id_rsa ~/.ssh RUN git clone ssh://... RUN rm –f ~/.ssh/id_rsa Dockerfile1行ごとにtarレイヤが作られる 鍵を含むレイヤが作られる 鍵を隠す(whiteout)情報を含んだレイヤが 作られるが,鍵を含むレイヤ自体を消しては くれない! 自身以外が参照可能なレジストリにイメージをpushすると,鍵が漏れる!
  • 8. プライベートリポジトリを用いるビルド 8 ● Docker 18.09から利用可能な docker build --secret 及び docker build --ssh を使うとよい ● --secret: 鍵ファイルをビルド用コンテナ中にマウントできる.鍵ファ イルは出力イメージ内には残らない. ● --ssh: --secretに似ているがSSH秘密鍵に特化.ssh-agentと組み合わ せて,パスフレーズ付きの秘密鍵を使うことも可能.
  • 9. docker build --secret 9 # syntax = docker/dockerfile:1.0-experimental FROM python:3 RUN pip install awscli RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://... ... $ export DOCKER_BUILDKIT=1 $ docker build --secret id=aws,src=$HOME/.aws/credentials ... BuildKitモードを有効化 secretのIDとパスを指定 Docker 18.09ではexperimental扱いなので,1行目に # syntax = ...の指定が必要 上で指定したsecretをtargetにマウント (出力イメージには残らない)
  • 10. 補足: export DOCKER_BUILDKIT=1 について 10 ● クライアント側でexport DOCKER_BUILDKIT=1するとBuildKitモードが有 効になる ○ あるいはデーモン側の /etc/docker/daemon.jsonに {"features":{"buildkit":true}} と記述しても有効化できる ● BuildKit: セキュリティ,並行性,キャッシュ効率を重視した次世代 docker build バックエンド ○ --secret 及び --ssh をサポート ○ Dockerfileの命令間の依存性をDAGとして表現し,可能な限り各命令 を同時実行 ○ キャッシュ機能を強化 ● 詳しくは https://github.com/moby/buildkit 参照
  • 11. docker build --ssh 11 # syntax = docker/dockerfile:1.0-experimental FROM alpine RUN apk add --no-cache openssh-client RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts RUN --mount=type=ssh git clone github.com/プライベートなrepo $ eval $(ssh-agent) $ ssh-add ~/.ssh/id_rsa (パスフレーズ入力) $ export DOCKER_BUILDKIT=1 $ docker build --ssh default=$SSH_AUTH_SOCK ssh-agentのソケットまたは秘密鍵ファイルを指定可能 (ssh-agentのソケットを使うなら,パスフレーズつきの秘密 鍵を扱える) Docker 18.09ではexperimental
  • 12. 古いバージョンのDockerを用いている場合 12 ● docker build --secret 及び docker build --ssh はDocker 18.09から利用可能 ● 今すぐ18.09にアップグレードできない場合のワークアラウンド ○ マルチステージビルド (右図) ○ docker build --squash (レイヤを1つにまとめる) ○ historyに残らない特殊なbuild-arg (FTP_PROXYなど)を目的外利用する https://github.com/moby/moby/pull/36443 ● ただし,古いバージョンのDockerは メンテナンスが終了しているため, なるべく早期にDocker 18.09に移行すべき FROM ... COPY id_rsa ~/.ssh RUN git clone ssh://… /foo FROM ... COPY --from=0 /foo /foo
  • 13. イメージスキャナ 13 ● コンテナイメージ中に,古いパッケージが含まれていると攻撃されること がある ● Clair, Microscanner, Anchore, Dagdaなどのスキャナを用いると,既知の 脆弱性を含むパッケージがイメージに含まれていないか検査できる ● スキャナで全ての脆弱性が見つかるわけではない ● スキャナが警告を出したとしても,必ずしも問題ではない ○ 当該の脆弱性を含む機能を使っていないかも知れない ○ コンテナ化している場合は,実用上問題ない脆弱性かも知れない ○ パッケージのバージョン番号が古く見えても,ディストリビュータが修正 パッチをバックポートしているかも知れない
  • 14. ● CoreOS (現Red Hat)が開発するOSSのイメージスキャナサーバ ● QuayやGitLab CI/CDにも採用されている ○ GitLab設定例: https://docs.gitlab.com/ee/ci/examples/container_scanning.html ● Clair本体の他に,クライアントが必要 ○ clairctl, klar, reg, clair-scannerなど ○ Harborもクライアントとして動作できる Clair 14 Clair PostgreSQL クライアント Alpine/Red Hat/Ubuntu等 のCVEデータベース レジストリ RESTによるリクエスト イメージ取得 脆弱性情報取得 脆弱性情報等格納
  • 15. Microscanner 15 ● Aqua Securityが提供するイメージスキャンサービス ○ 無償ではあるがプロプライエタリ,要ユーザ登録 ○ マルウェアスキャンなど,全ての機能を使うには有償製品の購入が必要 ● Dockerfileに1行付け足すだけでスキャンできる ○ Clairと異なり,自分でサーバを立てなくてよい ● CircleCI用のOrbも提供されている RUN apk add --no-cache ca-certificates && update-ca-certificates && wget -O /microscanner https://get.aquasec.com/microscanner && chmod +x /microscanner && /microscanner <token> && rm -rf /microscanner
  • 16. その他のイメージスキャナ 16 ● Anchore Engine ○ アーキテクチャはClairに似ているが,公式クライアント及び公式Jenkinsプラ グインが用意されており,使いはじめやすい ● Dagda ○ 公式クライアントあり ○ ClamAVを用いたマルウェアスキャンも出来る ● Vuls ○ コンテナ専用スキャナではないが,(イメージそのものではなく)実行中コン テナもスキャンできる ● OpenSCAP ○ コンテナ専用スキャナではないが,コンテナイメージもスキャンできる ○ RHELイメージ専用 (CentOS不可) ○ サーバ不要 ● その他,有償・プロプラではTwistlockなど
  • 18. イメージスキャナ比較 18 ● どれがよいとは一概には言えない ● False positive (誤検出) もある ● イメージスキャナを過信せず, なるべく依存パッケージを減らして シンプルなイメージを作るのがよい ○ 脆弱性を踏みにくくなるだけでなく False positiveで悩むことも減る 画像出典: 前ページ記載URL
  • 20. Dockerソケット ( /var/lib/docker.sock ) 20 ● DockerデーモンがREST APIを待ち受けるソケット ● デフォルトでの所有権は root:docker ● docker グループに一般ユーザを追加すると,一般ユーザでも docker クライアントを実行できるようになる ● Dockerソケットへアクセス権を与えることは,ホストのroot権限を与える ことに等しい ○ docker run --privileged を実行すると一切の保護が無効 ○ docker run -v /:/host … を実行すればホスト上の任意のファイルを root権限で読み書きできる
  • 21. Dockerソケット ( /var/lib/docker.sock ) 21 ● インターネットへ向けてTCPでlistenするなら,TLSの設定が必須 ● TLSを設定できていないDockerホストを対象とした攻撃が知られている ● 仮想通貨を採掘するコンテナを立ち上げる攻撃が近年は盛ん ● ホストのroot権限を奪われるので,ホスト上の任意のファイルを読み 書きされたり,同一ネットワーク上のパケットを盗聴されたりする ● 他のサイトへの攻撃の踏み台としても使われうる
  • 23. TLSを用いたDockerソケットの保護 23 TLSの設定方法: https://docs.docker.com/engine/security/https/ $ openssl genrsa -aes256 -out ca-key.pem 4096 Generating RSA private key, 4096 bit long modulus .................................................................. .................................................................. ........................................................++ ........++ e is 65537 (0x10001) Enter pass phrase for ca-key.pem: Verifying - Enter pass phrase for ca-key.pem: $ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 out ca.pem Enter pass phrase for ca-key.pem: You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]:Queensland Locality Name (eg, city) []:Brisbane Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc Organizational Unit Name (eg, section) []:Sales Common Name (e.g. server FQDN or YOUR name) []:$HOST Email Address []:[email protected] $ openssl genrsa -out server-key.pem 4096 Generating RSA private key, 4096 bit long modulus ............................. ........................................++ .................................... .............................................................++ e is 65537 (0x10001) $ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr $ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf $ echo extendedKeyUsage = serverAuth >> extfile.cnf $ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf Signature ok subject=/CN=your.host.com Getting CA Private Key Enter pass phrase for ca-key.pem: $ openssl genrsa -out key.pem 4096 Generating RSA private key, 4096 bit long modulus .........................................................++ ................++ e is 65537 (0x10001) $ openssl req -subj '/CN=client' -new -key key.pem -out client.csr $ echo extendedKeyUsage = clientAuth >> extfile.cnf $ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf Signature ok subject=/CN=client Getting CA Private Key Enter pass phrase for ca-key.pem: $ rm -v client.csr server.csr $ chmod -v 0400 ca-key.pem key.pem server-key.pem $ chmod -v 0444 ca.pem server-cert.pem cert.pem $ dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376 $ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$HOST:2376 version 出典: 上記URL
  • 24. TLSを用いたDockerソケットの保護 24 TLSの設定方法: https://docs.docker.com/engine/security/https/ $ openssl genrsa -aes256 -out ca-key.pem 4096 Generating RSA private key, 4096 bit long modulus .................................................................. .................................................................. ........................................................++ ........++ e is 65537 (0x10001) Enter pass phrase for ca-key.pem: Verifying - Enter pass phrase for ca-key.pem: $ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 out ca.pem Enter pass phrase for ca-key.pem: You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]:Queensland Locality Name (eg, city) []:Brisbane Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc Organizational Unit Name (eg, section) []:Sales Common Name (e.g. server FQDN or YOUR name) []:$HOST Email Address []:[email protected] $ openssl genrsa -out server-key.pem 4096 Generating RSA private key, 4096 bit long modulus ............................. ........................................++ .................................... .............................................................++ e is 65537 (0x10001) $ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr $ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf $ echo extendedKeyUsage = serverAuth >> extfile.cnf $ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf Signature ok subject=/CN=your.host.com Getting CA Private Key Enter pass phrase for ca-key.pem: $ openssl genrsa -out key.pem 4096 Generating RSA private key, 4096 bit long modulus .........................................................++ ................++ e is 65537 (0x10001) $ openssl req -subj '/CN=client' -new -key key.pem -out client.csr $ echo extendedKeyUsage = clientAuth >> extfile.cnf $ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf Signature ok subject=/CN=client Getting CA Private Key Enter pass phrase for ca-key.pem: $ rm -v client.csr server.csr $ chmod -v 0400 ca-key.pem key.pem server-key.pem $ chmod -v 0444 ca.pem server-cert.pem cert.pem $ dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376 $ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$HOST:2376 version 出典: 上記URL
  • 25. SSHを用いたDockerソケットの保護 25 ● TLSを正しく設定するのは難しい ● Docker 18.09では,export DOCKER_HOST=ssh://ユーザ@ホスト する とTLSの代わりにSSHを用いてリモートDockerホストに接続できる ○ 古いバージョンのDockerでも,自分で ssh -L foo.sock:/var/run/docker.sock プロセスを管理すればSSH接続可 ■ ssh -L には接続をkeep-aliveできるメリットもある ● SSHはホストにDockerをインストールする前に何れにせよ設定するだろ うから,TLSと違って追加の手間が発生しない ● 単に `ssh -l ユーザ ホスト – docker` を実行する場合と異なり,ク ライアントの ~/.docker/config.json に保存されているレジストリ 認証情報や,ビルドコンテキストにアクセスすることが可能
  • 26. AuthZプラグイン 26 ● DockerのAuthZプラグインを使うと,ソケットを用いて可能な操作を限定 できる ○ REST APIのHTTP Method, URIに応じてフックをかけることが出来る ○ https://docs.docker.com/engine/extend/plugins_authorization/#api-schema-an d-implementation ● プラグインの例: Casbin AuthZ Plugin, HBM (Harbormaster), Twistlock AuthZ Broker ○ いずれも要素技術を提供するのみで,完成したソリューションではない ● Docker Enterprise EditionのUniversal Control Plane (UCP)を用いると, 似たことを簡単に出来る (有償・プロプラ) ● KubernetesやOpenShiftのほうが,AuthN・AuthZが充実している
  • 28. コンテナの仕組み 28 ● コンテナは基本的にはNamespaces,Capabilities,Cgroups で出来ている ● Namespaces (例: Mount NS, Network NS, PID NS…) ○ ファイルシステムやネットワークなどを隔離 ● Capabilities (例: CAP_CHOWN, CAP_NET_ADMIN, CAP_SYS_ADMIN…) ○ root権限を細かいフラグに分割したもの ● Cgroups (例: CPU controller, Memory controller, Device controller…) ○ CPUやメモリの使用量や,デバイスファイルへのアクセスを制限 ● 加えて,SeccompやApparmrorなどを用いて更にセキュリティを強化 ○ Dockerではデフォルトで有効になっているが,Kubernetesではデフォルトで は無効なので要注意
  • 29. Seccomp 29 ● 呼び出し可能なシステムコールを制限 ● NamespacesでもCapabilitiesでも制限できないが,Seccompで制限できる システムコールとしては keyctl (カーネル キーリング)などがある ● デフォルトより厳しい設定を与えてコンテナを起動すると,コンテナや カーネルに脆弱性があっても影響を軽減することができる ○ docker run --security-opt seccomp=PROFILE.json ○ デフォルトのプロファイル: https://github.com/moby/moby/blob/master/profiles/seccomp/default.json ● 後述するNabla Containersはseccompを用いて呼び出し可能なシステム コールを7つにまで絞っている
  • 30. Apparmor 30 ● アクセス可能なファイルを制限できる ● デフォルトのプロファイルでは /proc や /sys へのアクセスを制限 ○ https://github.com/moby/moby/tree/master/profiles/apparmor ○ OCI Runtime Specで定義されているMaskedPathと似ている部分もあるが, より強力 ● より厳しく制限する例: docker run --security-opt apparmor=PROFILE https://docs.docker.com/engine/security/apparmor/#nginx-example-profile ● コンテナだけではなくDockerデーモン自体がアクセス可能なファイルの 制限も可能であるが,プロファイルは3年近く更新されていない ○ https://github.com/moby/moby/tree/master/contrib/apparmor ○ コントリビューション募集中
  • 31. SELinux 31 ■ Red Hat系でApparmorの代わりに使われている機構 ○ Apparmorがファイルのパスを対象としてアクセスを制御するのに対し ,SELinuxは「タイプ」などのラベルを対象とする ○ SELinuxはデフォルトでは有効にならない ( /etc/docker/daemon.json に {“selinux-enabled”: true} を設定する必要がある) ● SELinuxが有効になっていると,コンテナは container_tタイプで ,Dockerデーモン自体はcontainer_runtime_t タイプで実行される ○ /proc/$PID/attr/current で確認できる ○ ポリシーの詳細: https://github.com/containers/container-selinux
  • 32. バインドマウント時の注意 32 ● docker run -v /hostpath:/containerpath を用いると,ホスト のファイルシステムをコンテナ内にマウントできる ● 予期しないファイル改竄を防ぐには,read-onlyでマウントするのが良い ○ docker run -v /hostpath:/containerpath:ro ● ただしread-onlyはsubmountを再帰的にread-onlyにしないことに注意 ○ /mnt/foo にマウントされているファイルシステムをコンテナに読ませたい 時,docker run -v /mnt:/mnt:ro すると,/mnt/foo はread-onlyに ならない ○ Docker 19.03では,再帰的マウントの無効化が可能になる予定 ■ docker run --mount type=bind,src=/hostpath,target=/containerpath,ro,bind-nonrecursive ■ 再帰的なread-onlyマウントのサポートには時間がかかりそう (カーネルの仕様の ため)
  • 33. バインドマウント時の注意 33 ● SELinuxが有効化されている場合,ホストのファイルシステムをバインド マウントしても,ほとんど読み書きできない ○ /usr および /etc の一部を読めるくらい ● バインドマウントしたファイルを読み書きするには 対象ファイルに chcon -Rt container_file_t してからdocker runする ○ 読むだけならcontainer_share_t ○ 永続化には semanage fcontext と restorecon を用いる ○ あるいはコンテナ側のタイプを変える( docker run --security-opt label=type:TYPE ) ○ docker run -v /hostpath:/containerpath:z (及び:Z) は非推奨 ● SELinuxを無効化するには --security-opt label=disable ○ “石川さん&Walshさん ごめんなさい” https://stopdisablingselinux.com/
  • 34. コンテナ内の実行ユーザ (docker run --user) 34 ● ` docker run -user ユーザ名(またはUID)` を用いると,コンテナ 内のユーザを非rootに変更することができる ○ Dockerfileの USER 命令でデフォルトのユーザを指定することも出来る ● バインドマウントしたファイルシステムへのアクセスを容易に制御できる ● コンテナランタイムにバグがあっても,ホストへの影響を軽減できる ● 注意: su, sudoの類が無効になっているか,確認すること ○ Alpine 3.8の場合,Busyboxのsuはsetuidされていないため無効であるが ,shadowパッケージに含まれるsuはパスワード無しで実行可能 ■ passwd -l でrootアカウントをロックするか,suを削除すると良い
  • 35. 備考: Kubernetes (1.12)での設定方法 35 ● Seccomp: container.seccomp.security.alpha.kubernetes.io/… アノテーション ● Apparmor: container.apparmor.security.beta.kubernetes.io/… アノテーション ● SELinux: securityContext.seLinuxOptions ● --user: securityContext.runAsUser ● 詳細は https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ や https://kubesec.io/ など参照 ● PodSecurityPolicy (beta)を用いると,ネームスペース内やクラスタ内の 全てのPodに対してポリシーを設定できる ○ https://kubernetes.io/docs/concepts/policy/pod-security-policy/
  • 36. dockerd --userns-remap 36 ● userns-remap モードでDockerデーモンを実行すると,全てのコンテナが 非rootユーザで実行される ○ Dockerデーモン自体はrootで実行される ● User Namespaceを用いているので,コンテナの中からは,あたかもroot であるように見える ● /etc/subuid,subgid に dockremap:100000:65536 と書いておくと コンテナ内のUID 0→ コンテナ外のUID 100000, コンテナ内のUID 1→ コンテナ外のUID 100001,... コンテナ内のUID 65535→ コンテナ外のUID 165535 にマップされる ● Kubernetesでは1.14ころで利用可能となる見込み ○ https://github.com/kubernetes/enhancements/issues/127
  • 37. Docker自体を信頼できるのか? 37 ● Docker自体,Kubernetes自体,及び関連コンポーネントに,過去にさま ざまな脆弱性が見つかっている ● Docker “Shocker”(2014) ○ 不正なコンテナに,ホストのファイルシステムにアクセスされる (余計な CAP_DAC_READ_SEARCHがデフォルトで与えられていた) ● Docker CVE-2014-9357 ○ 不正なDockerfileをビルドすると,ホストのroot権限で任意のバイナリを実行 される (LZMAアーカイブの扱いのバグ) ● containerd #2001 ○ 不正なイメージをpullすると,イメージを実行しなくてもホストの /tmp が削 除される
  • 38. Docker自体を信頼できるのか? 38 ● Kubernetes CVE-2017-1002101, CVE-2017-1002102 ○ 不正なボリュームを通じてホストのファイルシステムにアクセスされる ● Kubernetes CVE-2018-1002105 (2018/12/4 公表) ○ 不正なAPI呼び出しを通じてcluster-adminを奪取される (→privilegedコンテ ナを通じてホストのrootを奪取される) ● Git CVE-2018-11235 ○ 不正なリポジトリをcloneすると,ホストのroot権限で任意のバイナリを実行 される ○ KubernetesのgitRepoボリュームを通じてトリガ可能 https://staaldraad.github.io/post/2018-06-03-kubernetes-root-with-gitrepo- volume/
  • 39. Rootlessモード 39 ● コンテナのみならず,Dockerデーモンも含め,全てを非rootユーザで実行 する ○ Dockerデーモンやcontainerd,runcに脆弱性があったとしても,実際の影響 を軽減できる ■ 例えば,カーネルを書き換えられて検出不可能なマルウェアを仕込まれたり ,ARPスプーフィングされたりする危険性を減らせる ■ 複数ユーザを用いることで,複数テナントを安全に隔離して同一ノードで実行す ることも可能 ● Docker 19.03ないし19.09から利用可能となる見込み ○ https://github.com/moby/moby/pull/38050 ○ “Usernetes” プロジェクトの一部としてバイナリ入手可能 https://github.com/rootless-containers/usernetes ■ 一般ユーザのホームディレクトリにインストールできる ■ Kubernetesも動く
  • 40. Rootlessモード 40 ● Overlayストレージドライバには非対応 ○ Ubuntuでは例外的に対応 (カーネルにパッチが当てられている) ○ 将来的には,FUSEによるOverlayFS実装に対応予定 (要カーネル4.18) ○ reflinkを用いたCopy-on-Writeには対応 (要XFS等) ● Cgroups関連の設定は今のところは無視される ○ 将来的にはpam_cgfs.soやCgroups v2 nsdelegateなどを用いて対応予定 ● ネットワークが遅い ○ Bridgeとvethを用いず,slirp4netnsを用いてユーザモードでエミュレートす るため ■ qemu -netdev userのコードが元になっている ■ Unprivileged LXCと異なり,SETUID/SETCAPバイナリ不要 ○ Travis上でのiperf3を用いたベンチマークでは9.21 Gbpsほど https://github.com/rootless-containers/slirp4netns#benchmarks
  • 41. Falco 41 ● コンテナの予期しない動作を監視するツール (CNCF Sandbox) ○ コンテナ内の予期しないプロセス(シェルなど)の起動,デバイスファイルへ のアクセス,privilegedコンテナの起動などを検知 ○ Slack, Fluentd, NATSなどにアラートを通知 ○ BPFを用いて実装されている ● 例: コンテナ内でのbash起動を検知 https://github.com/falcosecurity/falco/wiki/Falco-Examples - rule: run_shell_in_container condition: container and proc.name = bash and spawned_process and proc.pname exists and not proc.pname in (bash, docker) ...
  • 43. OCIランタイム 43 ● コンテナの機能自体は,DockerではなくOCIランタイムに実装されている ● OCIランタイムは,Linux Foundation傘下のOpen Container Initiative (OCI)が策定するOCI Runtime Specを実装している ● デフォルトではruncが使われるが,Kata, gVisor, Nablaなど他のランタイ ムにも簡単に切り替えることが出来る (docker run --runtime=...) Docker containerd OCIランタイム
  • 44. OCIランタイム 44 ● OCIランタイムはKubernetesでも用いられる ● KubernetesとOCIランタイムの間には,CRIランタイムが挟まる ○ CRIランタイム: dockershim, containerd, CRI-Oなど ○ イメージやスナップショットの管理など,OCIランタイムの外回りの機能を実 装 Docker containerd OCIランタイム Kubernetes CRIランタイム
  • 45. Kata Containers 45 ● OpenStack Foundationが開発するOCIランタイム ○ Intel Clear ContainersとrunVとが合流して発足 ● QEMU/KVMを用いて強固なアイソレーションを実現 ○ ファイルシステムは9pでマウント ○ クラウドにデプロイする場合は,nested virtualization対応またはベアメタル のインスタンスを使う必要がある ■ EC2: i3.metal がベアメタル対応 ■ Azure: Dv3, Ev3がnested virtualization対応 ■ GCE: nested virtualizationにはbetaとして対応 ● 起動にやや時間がかかる (せいぜい1秒-2秒程度)
  • 46. その他のVM系ランタイム 46 ● runq ○ IBM Cloudの一部サービスで使われているOCIランタイム ○ QEMU/KVMベースだがシンプルな構成 ● Firecracker ○ AWS LambdaやFargateで使われている ○ ChromeOS crosvmに由来する軽量なVMを使用.125msで起動するとのこ と.Rustで書かれている. ○ 今のところOCIランタイムとしては動作しないが,containerdプラグインが用 意されている ■ Firecrackerの上でruncを動かす設計 ● Frakti, Virtlet ○ CRIランタイム
  • 47. gVisor (runsc) 47 ● Googleが開発するOCIランタイム.App Engineにて使われている. ● Linuxカーネルをユーザランドで再実装することで,セキュリティを強化 ○ User Mode Linuxに似ているが,Linuxのコードを使わずにGoでスクラッチか ら実装している ○ Ptraceによる実装と,KVMによる実装とがある ○ ホストのカーネルに対して呼び出すシステムコールは50程度 ● 起動時間は早いが,システムコール呼び出しのオーバヘッドが大きい ● 互換性の問題がある
  • 48. Nabla Containers (runnc) 48 ● IBMが開発するOCIランタイム ● NetBSDベースのユニカーネルを用いることで,コンテナが呼び出すシス テムコール数を削減し,カーネルの脆弱性を踏む可能性を削減 ○ read, write, exit_group, clock_gettime, ppoll, pwrite64, pread64の7つに制限 ● Linux用のELFバイナリをそのまま実行できるわけではなく,Nabla用に移 植する必要がある ● 起動時間は短い.runcに勝ることもある. ○ https://www.slideshare.net/KoheiTokunaga/ss-123664087
  • 51. ランタイムの比較 51 利点 欠点 runc ● 速い ● アイソレーションが弱い Kata Containers ● アイソレーション が極めて強い (VM分離) ● やや起動が遅い ● 仮想化をサポートするホ ストが必要 gVisor ● アイソレーション が強い (カーネル分離) ● 遅い ● Linuxカーネルとの互換 性が低い Nabla Containers ● アイソレーション が強い (カーネル分離) ● コンテナの移植が必要 ● Linuxカーネルとの互換 性が低い
  • 52. ランタイムの比較 52 利点 欠点 runc ● 速い ● アイソレーションが弱い Kata Containers ● アイソレーション が極めて強い (VM分離) ● やや起動が遅い ● 仮想化をサポートするホ ストが必要 gVisor ● アイソレーション が強い (カーネル分離) ● 遅い ● Linuxカーネルとの互換 性が低い Nabla Containers ● アイソレーション が強い (カーネル分離) ● コンテナの移植が必要 ● Linuxカーネルとの互換 性が低い 許容できるならKataがよさそう? (もしくはFirecracker)
  • 54. まとめ 54 ● イメージ ○ イメージ中にGitHubやAWSなどの鍵が紛れ込まないよう,注意する ○ docker build --secret,docker build --sshが安全で便利 ○ Clair, Microscannerなどのスキャナを用いて脆弱性を検知できる ○ イメージはなるべくシンプルに保つ ● Dockerソケット ○ Dockerソケットへのアクセス権を与えることは,ホストのroot権限を与える ことと同じ ○ TLSまたはSSHを用いて厳重にアクセス制限する必要がある
  • 55. まとめ 55 ● ランタイム ○ Seccomp, Apparmor, SELinuxなどを使って,コンテナの動作を厳しく制限で きる ○ docker run --user や dockerd --userns-remap を用いることで,コ ンテナを非rootユーザで実行できる ○ 次期バージョンからはDockerデーモン自体も非rootユーザで実行できるよう になる ■ https://github.com/rootless-containers/usernetes ● 代替ランタイム ○ 代替ランタイムを用いることで,より強いアイソレーションを実現可能 ○ Kata Containers: VMを利用 ○ gVisor, Nabla Containers: ユーザモードカーネルを利用