[4/1/2020追記] ポリシー定義を詳細に書き直しました(参考情報)。
[注釈] Pod Security PoliciesはKubernetes 1.17および1.18ではBeta版の機能です。現在Kubernetes 1.19が開発版としてリリースされていますが、どこかのタイミングでGA機能になると思われます。GAになった場合に再検証が必要であると考えています。
はじめに
Kubernetes 1.17,1.18ではPod Security PoliciesがBetaになりました。 Pod Security Policiesを使うと、Podの作成や更新に対してセキュリティに関わるルールを定義し、それに従ったPodの運用が可能です。
Pod Security Policiesについてはこちらのブログで利用を推奨されています。
Pod Security Policiesは例えば特権コンテナーの可否、利用可能なボリューム、Linux capabilities、実行ユーザーやグループなどの指定が可能です。
Pod Security Policiesでは様々なことを設定できますが、今回はLinux capabilitiesを設定するのを試してみました。
Kube API ServerでPod Security Policyサポートを追加
Pod Security PolicyはまだBeta版のため、デフォルト設定では有効化されていません。Admission PluginとしてKube API Serverに追加する必要があります。
以下の方法で有効化できますが、あくまでBeta版であることを理解してからご利用ください。少なくとも安定稼働中のKubernetesクラスターの設定を変えて利用するのは筆者は推奨しませんし、設定変更で何らかの問題が発生しても保証いたしません。
# vi /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=172.17.28.119 - --allow-privileged=true - --authorization-mode=Node,RBAC - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy <-追記 - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
--enable-admission-plugins=
の行に PodSecurityPolicy
を追加します。
設定変更後にこのままKube API Serverを再起動すると、クラスターでPod Security Policyが適用されます。KubernetesのPod Security Policyはホワイトリスト式のセキュリティ機能であるため、このままではPodは一切作成できません。当然困るので「こういったルールに則ったPodであれば作成しても良い」というルールを定義します。
ルールの定義については下記公式のドキュメントに書かれています。
今回はサンプルをベースに少々書き換えたルールをシステムに定義してみます。
ポリシーテンプレートをダウンロード
いずれかのコマンドを使って、ポリシーテンプレートをダウンロードします。
# wget https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/policy/example-psp.yaml # curl -LO https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/policy/example-psp.yaml
ポリシーを追記
「requiredDropCapabilities」を追記して、SYS_CHROOTとNET_RAWを定義します。またこのポリシーには「privileged: false」が設定されているため、特権コンテナーの実行ができなくなります。
SYS_CHROOTを指定すると、コンテナーでchrootが利用できなくなります。また、NET_RAWを指定するとRAWソケットと PACKETソケットの利用に規制がかかり、例えばpingやtracerouteなどが利用不可になります。
LinuxのCapabilitiesについては次のドキュメントやシステムの「man capabilities」を実行して最新の情報を確認してください。
# vi example-psp.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: psp-example spec: privileged: false # Don't allow privileged pods! # The rest fills in some required fields. requiredDropCapabilities: <-追記 - SYS_CHROOT <-追記 - NET_RAW <-追記 seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny runAsUser: rule: RunAsAny fsGroup: rule: RunAsAny volumes: - '*' --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: psp:default namespace: default rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - psp-example --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: psp:default namespace: default roleRef: kind: Role name: psp:default apiGroup: rbac.authorization.k8s.io subjects: # Authorize all service accounts - kind: Group apiGroup: rbac.authorization.k8s.io name: system:serviceaccounts
ポリシーを適用
ポリシーを適用するには次のコマンドを実行します。
# kubectl apply -f example-psp.yaml
まだKube API Serverを再起動していない場合は再起動します。Masterノードを再起動しましょう。
Pod Security Policyをテストする
こういうYAMLを作ってみます。見るからに悪意のありそうなYAMLファイルです。ホストの/をPodの/rootfsにマウントしています。
今回は定義しませんでしたが、AllowedHostPathsで定義すれば、安全なパスを利用できると思います。
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: nginx:alpine tty: true name: test-container volumeMounts: - mountPath: /rootfs name: test-volume volumes: - name: test-volume hostPath: # directory location on host path: / # this field is optional type: Directory
先ほどのYAMLを使ってPodを作成してみます。
# kubectl create -f testpd.yaml
SYS_CHROOT
をdropしたことにより、chrootを実行して拒否されることを確認します。未設定の場合はシェルがホストに抜けてしまいます。rootユーザーでプロセスが動いているわけですし、DockerデフォルトのSecure computing modeにはchrootはパーミッションがあたっていないので当然です。
# kubectl exec -it test-pd -- chroot /rootfs chroot: can't change root directory to '/rootfs': Operation not permitted command terminated with exit code 1
NET_RAW
をdropしたことにより、pingやtracerouteが拒否されることを確認します。
pingやtracerouteを実行するとpermission deniedになることが確認できます。
/ # ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes ping: permission denied (are you root?) / # traceroute 8.8.8.8 traceroute: socket(AF_INET,3,1): Operation not permitted
しかしIPアドレスはCNIから取得できているため、パッケージアップデートなどは普通にできます。 example-psp.yamlのNET_RAWを除去して「kubectl apply」しなおせばping tracerouteは可能になりますが、常時全てのPodでそれができる必要はほぼ無いのでそのままの方が良いです。
/ # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 3: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP link/ether 22:0c:e7:7c:e6:ac brd ff:ff:ff:ff:ff:ff inet 10.244.0.8/24 scope global eth0 valid_lft forever preferred_lft forever / # apk update fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz v3.10.4-37-g4579131755 [http://dl-cdn.alpinelinux.org/alpine/v3.10/main] v3.10.4-38-g2185d3b8d2 [http://dl-cdn.alpinelinux.org/alpine/v3.10/community] OK: 10348 distinct packages available
コンテナーのeth0に設定されたIPアドレスにpingが通ることを確認します。コンテナーの中ではコマンド実行できませんが、外からはアクセスできることが確認できます。
# ping -c3 10.244.0.8 PING 10.244.0.8 (10.244.0.8) 56(84) bytes of data. 64 bytes from 10.244.0.8: icmp_seq=1 ttl=64 time=0.296 ms 64 bytes from 10.244.0.8: icmp_seq=2 ttl=64 time=0.084 ms 64 bytes from 10.244.0.8: icmp_seq=3 ttl=64 time=0.157 ms --- 10.244.0.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2049ms rtt min/avg/max/mdev = 0.084/0.179/0.296/0.087 ms
今回はKubernetes 1.17のPod Security Policies機能で設定できるセキュリティ機能のうち、Linux Capabilitiesの強制を試してみました。
Kubernetes 1.17のPod Security Policies機能を使えば、実行するPodにセキュリティ的なルールを定義できます。セキュアなKubernetesクラスターを構築する上で設定しておきたい設定であると言えそうです。