Config Controller でポリシー制御をしながら Google Cloud のリソースを管理する
3-shake Advent Calendar 2022 の18日目の記事です。
Google Cloud の Config Controller について少し調べてみたので、この記事では Config Controller の概要や、実際に Config Controller でポリシー制御をしながら Google Cloud のリソースを管理をする方法を紹介していきます。
Config Controller の概要
Config Controller は Config Connector と Config Sync と Policy Controller をまとめたサービスで、これらが有効化されたマネージドな GKE クラスタを提供してくれます。
Config Connector とは
Config Connector は Kubernetes のアドオンで、Config Connector を用いることで Kubernetes 経由で Google Cloud のリソースを管理することができます。
具体的には Config Connector は CRD と controller を提供してくれます。CRD は Google Cloud のリソースを表現するためのもので、Google Cloud のリソースを作成したい場合はそのリソースに対応する Custom Resource をクラスタに apply します。すると、cnrm-system
ネームスペースに存在する controller が先ほど apply した Custom Resource を元に実際のリソースを作成してくれます。
Config Connector を用いることで、インフラも Kubernetes 内のアプリケーションと同様に YAML で管理することができ、また Kubernetes に apply することでデプロイするという統一されたフローで両方管理することができるので、管理や運用の複雑さを削減できるというのが狙いの一つのようです。
Config Connector がサポートしている Google Cloud リソースはこちらで確認できます。
Config Sync とは
Config Sync は GitOps を実現できるサービスで、Kubernetes クラスタに GitHub や GitLab などのリポジトリを登録しておくことで、定期的にリポジトリを確認し、差分があればクラスタに apply してくれます。Git だけでなく、コンテナイメージや Helm のリポジトリを登録することもできます。
sync するリポジトリの設定は RootSync や RepoSync といった Custom Resource を用いて設定します。
なお、Config Controller で作成される Kubernetes クラスタはプライベートなクラスタであるため、インターネット経由でリポジトリと通信する場合には、Cloud NAT を用意するなどして外に出られるようにする必要があります。
Policy Controller とは
Policy Controller は Gatekeeper ベースのサービスで、ポリシーを設定してポリシーに違反する API リクエストを監査したり、ブロックしたりすることができます。
ポリシーを作成する際は、ConstraintTemplate でポリシーの雛形を用意し、Constraint で実際にポリシーを作成します。
# 例. Cloud Storage のバケットの location を日本リージョンのみに制限
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: gcpstoragelocationconstraintv1
spec:
crd:
spec:
names:
kind: GCPStorageLocationConstraintV1
validation:
legacySchema: true
openAPIV3Schema:
properties:
exemptions:
description: A list of bucket names that are exempt from this constraint.
items:
type: string
type: array
locations:
description: A list of locations that a bucket is permitted to have.
items:
type: string
type: array
targets:
- rego: |
package gcpstoragelocationconstraintv1
allowedLocation(reviewLocation) {
locations := input.parameters.locations
satisfied := [good |
location = locations[_]
good = lower(location) == lower(reviewLocation)
]
any(satisfied)
}
exempt(reviewName) {
input.parameters.exemptions[_] == reviewName
}
violation[{"msg": msg}] {
bucketName := input.review.object.metadata.name
not input.review.object.spec.location
msg := sprintf("Cloud Storage bucket <%v> must include a location", [bucketName])
}
violation[{"msg": msg}] {
bucketName := input.review.object.metadata.name
bucketLocation := input.review.object.spec.location
not allowedLocation(bucketLocation)
not exempt(bucketName)
msg := sprintf("Cloud Storage bucket <%v> uses a disallowed location <%v>, allowed locations are %v", [bucketName, bucketLocation, input.parameters.locations])
}
violation[{"msg": msg}] {
not input.parameters.locations
bucketName := input.review.object.metadata.name
msg := sprintf("No permitted locations for Cloud Storage bucket <%v>", [bucketName])
}
target: admission.k8s.gatekeeper.sh
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: GCPStorageLocationConstraintV1
metadata:
name: only-japan
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [storage.cnrm.cloud.google.com]
kinds: [StorageBucket]
parameters:
locations:
- asia-northeast1
- asia-norteast2
ConstraintTemplate はデフォルトでもいくつか用意されているので、こちらを活用することもできます。
$ kubectl get constrainttemplates -l "configmanagement.gke.io/configmanagement=config-management"
NAME AGE
allowedserviceportname 4d2h
asmauthzpolicydisallowedprefix 4d2h
asmauthzpolicyenforcesourceprincipals 4d2h
asmauthzpolicynormalization 4d2h
asmauthzpolicysafepattern 4d2h
asmingressgatewaylabel 4d2h
asmpeerauthnstrictmtls 4d2h
asmrequestauthnprohibitedoutputheaders 4d2h
asmsidecarinjection 4d2h
destinationruletlsenabled 4d2h
disallowedauthzprefix 4d2h
gcpstoragelocationconstraintv1 4d2h
k8sallowedrepos 4d2h
k8sblockendpointeditdefaultrole 4d2h
k8sblockloadbalancer 4d2h
k8sblocknodeport 4d2h
k8sblockprocessnamespacesharing 4d2h
k8sblockwildcardingress 4d2h
k8scontainerlimits 4d2h
k8scontainerratios 4d2h
k8scontainerrequests 4d2h
k8sdisallowanonymous 4d2h
k8sdisallowedrolebindingsubjects 4d2h
k8sdisallowedtags 4d2h
k8semptydirhassizelimit 4d2h
k8sexternalips 4d2h
k8shttpsonly 4d2h
k8simagedigests 4d2h
k8slocalstoragerequiresafetoevict 4d2h
k8smemoryrequestequalslimit 4d2h
k8snoenvvarsecrets 4d2h
k8snoexternalservices 4d2h
k8spodsrequiresecuritycontext 4d2h
k8sprohibitrolewildcardaccess 4d2h
k8spspallowedusers 4d2h
k8spspallowprivilegeescalationcontainer 4d2h
k8spspapparmor 4d2h
k8spspautomountserviceaccounttokenpod 4d2h
k8spspcapabilities 4d2h
k8spspflexvolumes 4d2h
k8spspforbiddensysctls 4d2h
k8spspfsgroup 4d2h
k8spsphostfilesystem 4d2h
k8spsphostnamespace 4d2h
k8spsphostnetworkingports 4d2h
k8spspprivilegedcontainer 4d2h
k8spspprocmount 4d2h
k8spspreadonlyrootfilesystem 4d2h
k8spspseccomp 4d2h
k8spspselinuxv2 4d2h
k8spspvolumetypes 4d2h
k8sreplicalimits 4d2h
k8srequirecosnodeimage 4d2h
k8srequiredannotations 4d2h
k8srequiredlabels 4d2h
k8srequiredprobes 4d2h
k8srequiredresources 4d2h
k8srestrictlabels 4d2h
k8srestrictnamespaces 4d2h
k8srestrictrolebindings 4d2h
noupdateserviceaccount 4d2h
policystrictonly 4d2h
restrictnetworkexclusions 4d2h
sourcenotallauthz 4d2h
Policy Controller で Config Connector の Custom Resource に対してポリシーを設定し、Google Cloud リソースの管理を Config Connector に寄せることで、ポリシー制御を行いながら Google Cloud のリソースを管理することが可能になります。
Google Cloud リソースに対するポリシーは Organization policies という別のサービスでも設定することができますが、こちらは適用できるポリシーの種類が限られています。対して、Policy Controller は自由にポリシーを作成することができるため、Organization policies では不十分といった場合に利用を検討すると良さそうです。
費用に関して
Config Controller を使用すると、Anthos Config Management と GKE クラスタの費用がかかります。
2022年12月時点だと、
- Anthos Config Management : $0.10/h
- GKE クラスタ : $0.10/h + ノードの費用
となります。
実際にポリシー制御を行いながら Google Cloud のリソースを管理してみる
ここからは、実際に Policy Controller でポリシーを設定し、Config Connector で Google Cloud のリソースを管理してみます。
今回は Cloud SQL に関して、location を日本リージョンのみに限定するポリシーを作成し、実際に Config Connector でインスタンスを作成してみます。
Config Controller の準備
まずは、各種 API を有効化しておきます。
$ gcloud services enable serviceusage.googleapis.com \
krmapihosting.googleapis.com \
container.googleapis.com \
cloudresourcemanager.googleapis.com
次に、Config Controller のインスタンスを作成します。
東京リージョンで作成しようとするとエラーが出て作成できなかったので、今回は大阪リージョンで作成します。
$ gcloud anthos config controller create test --location asia-northeast2
Config Controller のインスタンス一覧や状態はこちらで確認できます。
$ gcloud anthos config controller list
NAME LOCATION STATE
projects/<PROJECT_ID>/locations/asia-northeast2/krmApiHosts/test asia-northeast2 RUNNING
作成された GKE クラスタの認証情報はこちらで取得できます。
$ gcloud anthos config controller get-credentials test --location asia-northeast2
作成された GKE クラスタの中身はこんな感じです。
$ kubectl get no
NAME STATUS ROLES AGE VERSION
gke-krmapihost-test-krmapihost-test-p-0d142341-cdqp Ready <none> 4d2h v1.24.5-gke.600
gke-krmapihost-test-krmapihost-test-p-3f89df88-x0x7 Ready <none> 4d2h v1.24.5-gke.600
gke-krmapihost-test-krmapihost-test-p-62a75b97-q2kr Ready <none> 4d2h v1.24.5-gke.600
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
cnrm-system cnrm-controller-manager-4s5hagzmxjow7rr4wtvq-0 2/2 Running 0 4d2h
cnrm-system cnrm-deletiondefender-0 1/1 Running 0 4d2h
cnrm-system cnrm-resource-stats-recorder-5cf496b66-l7m96 2/2 Running 0 4d2h
cnrm-system cnrm-unmanaged-detector-0 1/1 Running 0 4d2h
cnrm-system cnrm-webhook-manager-bc647699-6hvbv 1/1 Running 0 4d2h
cnrm-system cnrm-webhook-manager-bc647699-pqnpq 1/1 Running 0 4d2h
config-management-monitoring otel-collector-5bc7d4bb6d-f5drx 1/1 Running 0 4d2h
config-management-system config-management-operator-775dd7c54c-9j75j 1/1 Running 0 4d2h
config-management-system reconciler-manager-d67cf5469-4pmck 2/2 Running 0 4d2h
configconnector-operator-system configconnector-operator-0 1/1 Running 0 4d2h
gatekeeper-system gatekeeper-audit-d9d9b84b8-bs6hg 1/1 Running 0 4d2h
gatekeeper-system gatekeeper-controller-manager-5f4cc4475b-mcdrr 1/1 Running 0 4d2h
krmapihosting-monitoring krmapihosting-metrics-agent-26pbh 1/1 Running 0 4d2h
krmapihosting-monitoring krmapihosting-metrics-agent-dzzp4 1/1 Running 0 4d2h
krmapihosting-monitoring krmapihosting-metrics-agent-s5ln4 1/1 Running 0 4d2h
krmapihosting-system bootstrap-9d9db889f-99v9r 1/1 Running 0 2d
kube-system event-exporter-gke-857959888b-8xfmk 2/2 Running 0 4d2h
kube-system fluentbit-gke-5wtpr 2/2 Running 0 4d2h
kube-system fluentbit-gke-gvm64 2/2 Running 0 4d2h
kube-system fluentbit-gke-wxx5f 2/2 Running 0 4d2h
kube-system gke-metadata-server-n76hd 1/1 Running 0 4d2h
kube-system gke-metadata-server-xnthv 1/1 Running 0 4d2h
kube-system gke-metadata-server-xsk24 1/1 Running 0 4d2h
kube-system gke-metrics-agent-9khmc 1/1 Running 0 4d2h
kube-system gke-metrics-agent-rfss8 1/1 Running 0 4d2h
kube-system gke-metrics-agent-vvxx8 1/1 Running 0 4d2h
kube-system kube-dns-55d79c844b-bqndc 4/4 Running 0 4d2h
kube-system kube-dns-55d79c844b-s5zjl 4/4 Running 0 4d2h
kube-system kube-dns-autoscaler-9f89698b6-4sqp8 1/1 Running 0 4d2h
kube-system kube-proxy-gke-krmapihost-test-krmapihost-test-p-0d142341-cdqp 1/1 Running 0 4d2h
kube-system kube-proxy-gke-krmapihost-test-krmapihost-test-p-3f89df88-x0x7 1/1 Running 0 4d2h
kube-system kube-proxy-gke-krmapihost-test-krmapihost-test-p-62a75b97-q2kr 1/1 Running 0 4d2h
kube-system l7-default-backend-6dc845c45d-vmwcv 1/1 Running 0 4d2h
kube-system metrics-server-v0.5.2-6bf845b67f-g7924 2/2 Running 0 4d2h
kube-system netd-mg7s6 1/1 Running 0 4d2h
kube-system netd-mrzhb 1/1 Running 0 4d2h
kube-system netd-qjwpg 1/1 Running 0 4d2h
kube-system pdcsi-node-68xtw 2/2 Running 0 4d2h
kube-system pdcsi-node-bg9x7 2/2 Running 0 4d2h
kube-system pdcsi-node-plm9h 2/2 Running 0 4d2h
resource-group-system resource-group-controller-manager-7f89854f9-q2crg 2/2 Running 0 4d2h
Config Connector では Workload Identity を使用しますが、大方の設定は Config Controller がよしなにやってくれています。あとは、Config Connector が使用する Google Cloud の Service Account に Google Cloud リソースを管理できる権限を渡す必要があるので、ここでは roles/editor
の role を設定します。実際に使用する際には必要最低限の権限を渡すように設定してください。
$ kubectl -n config-control get ConfigConnectorContext -o jsonpath='{.items[0].spec.googleServiceAccount}'
service-<PROJECT_NUMBER>@gcp-sa-yakima.iam.gserviceaccount.com
$ gcloud projects add-iam-policy-binding <PROJECT_ID> \
--member "serviceAccount:service-<PROJECT_NUMBER>@gcp-sa-yakima.iam.gserviceaccount.com" \
--role "roles/editor" \
--project "<PROJECT_ID>"
Policy Controller でポリシーの作成
Config Controller の用意ができたので、ここからは Cloud SQL のインスタンスの location を日本リージョンのみに制限するポリシーを作成します。
まずは、ConstraintTemplate を用意します。この ConstraintTemplate は locations
というプロパティを持ち、こちらには Cloud SQL インスタンスの実行を許可する location を渡します。実際のインスタンスの location が locations
に含まれていない場合に違反とみなすように Rego を記述します。
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: cloudsqllocation
spec:
crd:
spec:
names:
kind: CloudSQLLocation
validation:
openAPIV3Schema:
type: object
properties:
locations:
description: A list of locations where Cloud SQL instances are allowed to run.
items:
type: string
type: array
targets:
- rego: |
package cloudsqllocation
allowedLocation(reviewLocation) {
locations := input.parameters.locations
satisfied := [good |
location = locations[_]
good = lower(location) == lower(reviewLocation)
]
any(satisfied)
}
violation[{"msg": msg}] {
instanceName := input.review.object.metadata.name
instanceLocation := input.review.object.spec.region
not allowedLocation(instanceLocation)
msg := sprintf("Cloud SQL instance <%v> runs in a disallowed location <%v>, allowed locations are %v", [instanceName, instanceLocation, input.parameters.locations])
}
target: admission.k8s.gatekeeper.sh
$ kubectl apply -f template.yaml
そして、日本リージョンのみを許可するように Constraint を作成します。
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: CloudSQLLocation
metadata:
name: only-japan
spec:
match:
kinds:
- apiGroups: ["sql.cnrm.cloud.google.com"]
kinds: ["SQLInstance"]
parameters:
locations: ["asia-northeast1", "asia-northeast2"]
$ kubectl apply -f constraint.yaml
Config Connector で Cloud SQL インスタンスの作成
ポリシーを作成できたので、ここからは Config Connector で Cloud SQL インスタンスを作成していきます。
config-control
ネームスペースに SQLInstance を作成すると、Config Connector が実際に Cloud SQL のインスタンスを作成してくれます。まずは試しに、us-central1
で作成してみます。
apiVersion: sql.cnrm.cloud.google.com/v1beta1
kind: SQLInstance
metadata:
name: sample-mysql
namespace: config-control
spec:
databaseVersion: MYSQL_8_0
region: us-central1
settings:
tier: db-f1-micro
$ kubectl apply -f cloudsql.yaml
Error from server (Forbidden): error when creating "cloudsql.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [only-japan] Cloud SQL instance <sample-mysql> runs in a disallowed location <us-central1>, allowed locations are ["asia-northeast1", "asia-northeast2"]
すると、us-central1
は許可されていない location だと怒られてしまったので、spec.region
を asia-northeast2
に変更します。
apiVersion: sql.cnrm.cloud.google.com/v1beta1
kind: SQLInstance
metadata:
name: sample-mysql
namespace: config-control
spec:
databaseVersion: MYSQL_8_0
region: asia-northeast2
settings:
tier: db-f1-micro
$ kubectl apply -f cloudsql.yaml
sqlinstance.sql.cnrm.cloud.google.com/sample-mysql created
今度は正常に作成できました。
しばらくすると、インスタンスが立ち上がります。
$ gcloud sql instances list
AME DATABASE_VERSION LOCATION TIER PRIMARY_ADDRESS PRIVATE_ADDRESS STATUS
mysql MYSQL_8_0 asia-northeast2-b db-f1-micro <PUBLIC_IP> - RUNNABLE
以上が Policy Controller と Config Connector の使用例となります。
Config Sync については割愛してしまったので、気になる方は公式のチュートリアルをご覧ください。
まとめ
- Config Controller は Config Connector と Config Sync と Policy Controller をまとめたサービスです
- Config Connector を使うと Kubernetes 経由で Google Cloud のリソースを管理できます
- Config Sync を使うと Git や OCI、Helm リポジトリと同期できます
- Policy Controller を使うとポリシーを設定して、ポリシーに違反するリクエストを監査したり、ブロックしたりすることができます
- Policy Controller で Config Connector のポリシーを用意しておき、Google Cloud リソースの管理を Config Connector に寄せることで、ポリシー制御を行いながら Google Cloud のリソースを管理することができます
- Google Cloud リソースのポリシーは Organization policies でも設定できるので、まずはそちらを試してみて、それだけでは要件が満たせなければ、Config Controller の使用を検討すると良いかと思います
Config Connector については、秋季インターンでインターン生がまとめてくれている記事があるので、ご興味あればぜひそちらもご覧ください。
Discussion