[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クラスターを構築する上で設定しておきたい設定であると言えそうです。