ここではGatorというOPAのPolicyチェックツールについて簡単に紹介する。なお、触り程度の解説なので、verify機能やkind: Suite
には触れていない。
OPA Gatekeeper利用時の課題
OPA Gatekeeperを使っていると、Manifest作成時にそのManifestがPolicy違反になっていないかを確認したくなることがある。
特にGitOpsでManifestをGitに登録する場合、Policy違反が判明するのはArgoCD等デプロイツールが反応したタイミングであり、CI/CDのスピード感を殺すものとなっている。
conftestで事前に刈り取ることも出来るが、conftestで書くRegoとGatekeeperのManifestは一元管理することが出来ず、Gatekeeper側のPolicy変更が入った場合、conftest側のRegoのメンテが漏れてCI/CDが止まってしまうことも考えられる。
この問題を解決するアプローチとして、以下が考えられる。
- RegoからGatekeeperのManifestを生成する
- GatekeeperのManifestをPolicyチェックに使う
前者についてはKonstraintというOSSを用いて実現することが出来る。
後者については本日紹介するGatorにて解決することが出来る。
Gatorとは
GatorはOPA Gatekeeperで提供されているツールで、Gatekeeper用Manifest(ConstraintTemplateとConstraint)を元に、ManifestがPolicy違反を起こしていないかをチェックしてくれるツールである。
Gatorで利用するGatekeeper用Manifestを生成する
ここはTanzu Mission ControlのPolicy作成機能を利用してGatekeeper用ManifestであるConstraintTemplateリソースとConstraintリソースを作成する。
Tanzu Mission Controlが利用できない人は、Gatekeeperのexampleやdemo、konstraintのexamplesあたりを参考に作成するとよい。特にkonstraintのexamplesはかなりサンプルが豊富なのでオススメ。
また、末尾の付録に今回作ったManifestを掲載しているので、それを活用してもらってもよい。
以下、Tanzu Mission Controlを使ってGatekeeper用Manifestを作成していく。
Tanzu Mission Controlにログインし、左サイドバーのPolicies
->Assignments
から自身のクラスタを選択し、CREATE SECURITY POLICY
をクリックする。
ここではBaselineを選択し、Policy nameに適当な名前を与えてCREATE POLICY
をクリックしてPolicyを作成する。
上記の画面を見てもらうと分かるが、Privilegedなコンテナ作成やhostNetworkの利用などがこれによりNGとなる。
実行後、クラスタでConstraintTemplateリソースが生成されていることが分かる(Gatekeeperもこの時にインストールされる)。
$ kubectl get constrainttemplate
NAME AGE
vmware-system-tmc-allowed-host-paths-v1 48s
vmware-system-tmc-allowed-procmount-types-v1 53s
vmware-system-tmc-allowed-users-v1 56s
vmware-system-tmc-allowed-volumes-v1 50s
vmware-system-tmc-block-host-namespace-v1 47s
vmware-system-tmc-block-privilege-escalation-v1 52s
vmware-system-tmc-block-privileged-container-v1 49s
vmware-system-tmc-enforce-host-networking-v1 46s
vmware-system-tmc-forbidden-sysctls-v1 54s
vmware-system-tmc-linux-capabilities-v1 55s
Privilegedに関するPolicyを確認する。
$ kubectl get constrainttemplate vmware-system-tmc-block-privileged-container-v1 -o yaml
:(省略)
crd:
spec:
names:
kind: vmware-system-tmc-block-privileged-container-v1
validation:
legacySchema: true
targets:
- 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[_]
}
target: admission.k8s.gatekeeper.sh
これより、以下が分かる。
- ConstaintのCRD名は
vmware-system-tmc-block-privileged-container-v1
(ConstraintTemplateと同名) - Policy違反時は
Privileged container is not allowed: %v, securityContext: %v
でコンテナ名とセキュリティコンテキストが表示される。
続いて、Constraintの方を確認する。
$ kubectl get vmware-system-tmc-block-privileged-container-v1
NAME AGE
tmc.cp.opa-baseline-test 5m28s
$ kubectl get vmware-system-tmc-block-privileged-container-v1 -o yaml tmc.cp.opa-baseline-test
:(省略)
match:
excludedNamespaces:
- kube-*
- vmware-*
- tanzu-*
- gatekeeper-system
- tkg-system
- istio-system
- velero
- wavefront
- kube-dns
- kube-node-lease
- kube-proxy
- kube-public
- kube-system
- tanzu-observability-saas
- tanzu-package-repo-global
- tanzu-system
- tanzu-system-registry
- tanzu-system-auth
- tanzu-system-ingress
- tanzu-system-logging
- vmware-system-auth
- vmware-system-cloud-provider
- vmware-system-csi
- vmware-system-tmc
- vmware-system-tsm
kinds:
- apiGroups:
- ""
kinds:
- Pod
namespaceSelector:
matchExpressions:
- key: e2e-run
operator: DoesNotExist
特定のNamespaceは例外とし、Podを対象としていることが分かる。
以下のサンプルを試しにapplyすると、期待していたエラーが表示された。
cat <<EOF > ./testpod-privileged.yaml
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
$ kubectl apply -f testpod-privileged.yaml -n default
Error from server ([tmc.cp.opa-baseline-test] Privileged container is not allowed: nginx, securityContext: {"privileged": true}): error when creating "testpod-privileged.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [tmc.cp.opa-baseline-test] Privileged container is not allowed: nginx, securityContext: {"privileged": true}
これで適切なConstraint,ConstraintTemplateがk8sリソースとして存在しているのが確認できたので、これをファイルとして取り出す。
mkdir privileged-policy/
kubectl get vmware-system-tmc-block-privileged-container-v1 -o yaml tmc.cp.opa-baseline-test > privileged-policy/constraint.yaml
kubectl get constrainttemplate -o yaml vmware-system-tmc-block-privileged-container-v1 > privileged-policy/constrainttemplate.yaml
なお、status
フィールドを残して出力しているが、特に残っていても問題ないようであり、動作確認時もstatus
のフィールドを残して確認した。
ただ、実利用時は保守面等からkubectl neatなどでキレイにしたものを使った方がよいと思われる。
Manifestの検証
作成したManifestを利用してさっそく検証する。
今回、検証として先程作った以下のManifestを利用する。
- 検証対象のManifest:testpod-privileged.yaml
- Policy定義:constraint.yaml、constrainttemplate.yaml
Gatorをインストールしていない人は、CLIをインストールする。goによるインストールとbrewによるインストールが提供されており、今回はbrewでインストールした。
brew install gator
インストールが終われば、早速動作確認する。
$ cat testpod-privileged.yaml | gator test -f privileged-policy/
Message: "Privileged container is not allowed: nginx, securityContext: {\"privileged\": true}"
適切にPolicy違反が検出できた。なお、戻り値は以下のようになっており、パイプラインに組み込んで処理を止めることも容易となる。
$ echo $?
1
付録:利用したManifest
ディレクトリ構造は以下となる。
.
├── privileged-policy
│ ├── constraint.yaml
│ └── constrainttemplate.yaml
└── testpod-privileged.yaml
それぞれのファイルの中身は以下となる。(kubectl neatで整形済み)
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: vmware-system-tmc-block-privileged-container-v1
metadata:
labels:
tmc.cloud.vmware.com/crd-type: tmc-managed
tmc.cloud.vmware.com/managed: "true"
name: tmc.cp.opa-baseline-test
spec:
match:
excludedNamespaces:
- kube-*
- vmware-*
- tanzu-*
- gatekeeper-system
- tkg-system
- istio-system
- velero
- wavefront
- kube-dns
- kube-node-lease
- kube-proxy
- kube-public
- kube-system
- tanzu-observability-saas
- tanzu-package-repo-global
- tanzu-system
- tanzu-system-registry
- tanzu-system-auth
- tanzu-system-ingress
- tanzu-system-logging
- vmware-system-auth
- vmware-system-cloud-provider
- vmware-system-csi
- vmware-system-tmc
- vmware-system-tsm
kinds:
- apiGroups:
- ""
kinds:
- Pod
namespaceSelector:
matchExpressions:
- key: e2e-run
operator: DoesNotExist
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
labels:
tmc.cloud.vmware.com/crd-type: tmc-managed
tmc.cloud.vmware.com/managed: "true"
name: vmware-system-tmc-block-privileged-container-v1
spec:
crd:
spec:
names:
kind: vmware-system-tmc-block-privileged-container-v1
validation:
legacySchema: true
targets:
- 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[_]
}
target: admission.k8s.gatekeeper.sh
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true