EKSのOPAのワークショップの内容を確認した時のメモ。
ワークショップの内容だけだと理解が難しいため、実施している内容にコメントをつけて補足した。
言葉の定義などの理解が怪しいので、ご指摘あればコメント欄にて是非。
インストール
こちらより最新版のOPA Gatekeeperのバージョンを確認し、インストールする。
export OPA_GK_VERSION=3.9
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-${OPA_GK_VERSION}/deploy/gatekeeper.yaml
Podがあがっていることを確認する。
$ kubectl get pod -n gatekeeper-system
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-779ffb6c54-hshsd 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-57d77 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-pmqmv 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-zkqn9 1/1 Running 0 20s
制約の実施
kind: ConstraintTemplate
というカスタムリソースを利用し、Kubernetesリソースに対して制約を強制するためのテンプレートを作成することができる。
以下の例はprivileged=trueが含まれるとリクエストを拒否するテンプレートとなる。
cat > /tmp/constrainttemplate.yaml <<EOF
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8spspprivilegedcontainer
spec:
crd:
spec:
names:
kind: K8sPSPPrivilegedContainer
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spspprivileged
violation[{"msg": msg, "details": {}}] {
c := input_containers[_]
c.securityContext.privileged
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
}
input_containers[c] {
c := input.review.object.spec.containers[_]
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
}
EOF
Manifestを1つ1つ読み解いていく。
以下の部分はCRDを新しく作っている。
crd:
spec:
names:
kind: K8sPSPPrivilegedContainer
K8sPSPPrivilegedContainerというカスタムリソースを作成する時に上記のテンプレートが適用されることになる。制約発動の条件をCRDで定義したと思うと良さそう。
以下はRegoによって制約を定義している箇所になる。
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
targets.targetは説明が見当たらなかったため、一旦おまじないとして考えておく。
以下からRego本体になる。Regoを読み解くのにはOPA/Rego入門と公式ドキュメントを参考にした。
package k8spspprivileged
package k8spspprivileged
はこのregoの名前のようなもので、必ず定義する必要がある。
violation[{"msg": msg, "details": {}}] {
c := input_containers[_]
c.securityContext.privileged
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
}
violation
ではルール違反を定義する。[]
内はルール違反時に出力する文字列を設定している。
後述のinput_containersを呼び出し、k8sリソース内のspecのsecurityContext.privileged
がtrue
かどうかを確認している。
msg変数にはメッセージを設定する。
カッコ内の各行はそれぞれが評価式となり、AND条件で評価されるため、全てがTrueであればルール違反として判定される動きとなる。
以下はinput_containersの定義となる。
input_containers[c] {
c := input.review.object.spec.containers[_]
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
}
それぞれ、入力元(input.review.object
)のspec.containersおよびinitContainersを取得している。複数定義した場合、上書きするのではなく、それぞれで値を引っ張ってくる模様。なので、ここではinput_containersはk8sリソースのspec.containersとspec.initContainersを取得することになる。
なお、[_]
で複数取得(リストとかシーケンスの取得)ができるようになっている。
次に上記のテンプレートを利用して制約を適用する。制約を適用するには、先程作成したCRDを使う。
cat > /tmp/constraint.yaml <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
name: psp-privileged-container
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
EOF
ここの記載方法の詳細はこちらの公式ドキュメントが参考になる。
なお、作成したリソースは自分で設定したkindもしくはkind: Constraint
を参照することで確認できる。
$ kubectl get K8sPSPPrivilegedContainer
NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
psp-privileged-container 46
$ kubectl get constraint
NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
psp-privileged-container 46
describeで見ると、statusのところでVIOLATIONSの詳細として、どのPodがviolationとなったか、またRegoで定義したメッセージが確認できる。
$ kubectl describe constraint psp-privileged-container
:(省略)
Total Violations: 46
Violations:
Enforcement Action: deny
Group:
Kind: Pod
Message: Privileged container is not allowed: calico-node, securityContext: {"privileged": true}
Name: calico-node-2l6cl
Namespace: calico-system
Version: v1
Enforcement Action: deny
Group:
Kind: Pod
Message: Privileged container is not allowed: flexvol-driver, securityContext: {"privileged": true}
Name: calico-node-2l6cl
Namespace: calico-system
Version: v1
:(省略)
この状態でprivileged: true
を含むPodを作成する。
cat > /tmp/example.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: bad-nginx
labels:
app: bad-nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
EOF
kubectl create -f /tmp/example.yaml
実行結果として、以下のエラーが出力され、Podが作成できなかったことが分かる。
Error from server (Forbidden): error when creating "/tmp/example.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [psp-privileged-container] Privileged container is not allowed: nginx, securityContext: {"privileged": true}
チュートリアルにはなかったが、せっかくなのでInit Containerに対する制約も試してみる。
cat > /tmp/example.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: bad-nginx
labels:
app: bad-nginx
spec:
initContainers:
- name: nginx-init
image: nginx
securityContext:
privileged: true
containers:
- name: nginx
image: nginx
EOF
kubectl create -f /tmp/example.yaml
結果は先ほどと同じくPodの作成に失敗した。
Error from server (Forbidden): error when creating "/tmp/example.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [psp-privileged-container] Privileged container is not allowed: nginx-init, securityContext: {"privileged": true}
エラーメッセージより、Init Containerのコンテナであるnginx-initで失敗したことが分かる。