Config Controller で Google Cloud のガードレールを構築する

Kazuki Uchima
google-cloud-jp
Published in
29 min readDec 2, 2021

この記事は Google Cloud Japan Advent Calendar 2021 の 2 日目の記事です。Config Controller という新しいサービスを使ってガードレールを構築する話をします。

tl;dr

  • Config Controller という Google Cloud リソースの管理・プロビジョニングを行ってくれるサービスが登場しました。
  • Config Controller を活用することにより、柔軟なポリシー制御(ガードレール)と Reconciliation(自己修復)を組み合わせ Google Cloud 環境をより高度に管理することができます。

Config Controller とは

Config Controller は Google Cloud リソースの管理・プロビジョニングをしてくれるサービスです。

Config Controller は Terraform や Deployment Manager 等のツール/サービスと同じようにクラウドリソースのプロビジョニングを行う役割を持っていますが、いわゆる Infrastructure as Code (IaC) ではなく、Configuration as Data (CaD) というアプローチをとっているのが特徴です。

Configuration as Data (CaD) は、リソースをプロビジョニングするための操作や手順を定義するのではなく、リソースの理想の状態データとして定義する宣言型アプローチです。(CaD の詳細については以下の公式 Blog をご覧ください)

CaD は Kubernetes でも導入されている概念で、Kubernetes では Reconciliation Loop という仕組みにより理想の状態(定義した内容)と実際の状態を比較し、状態に差分がある場合は理想の状態となるよう継続的に修正することで実現しています。

Config Controller は Kubernetes Resource Model (KRM) を利用して、Google Cloud リソースを管理します(たとえば GCS や GKE など Google Cloud リソースを Kubernetes リソースとして管理することができます)。

KRM を利用することによって、クラウドリソースを管理する場合でも上記 Kubernetes の Reconciliation Loop や各種 Kubernetes エコシステムの恩恵を受けることができます。

Config Controller の構成要素

Config Controller は以下 3 つのコンポーネントから構成されています。

  • Config Connector … KRM を利用し Google Cloud リソースの管理をするコンポーネントです。Reconciliation Loop を使って宣言的にリソース管理ができるため、予め定義した理想状態実際の環境間での差分(ドリフト)を防ぐことができます。Config Connector を利用すると、もし誤って実環境の設定を手動で変更してしまっても自動的に理想状態と合致するように実環境側を継続的に修正してくれます。(本記事後半で実際の挙動も確認します)
  • Policy ControllerOPA Gatekeeper をベースとしているプロダクトで、事前に定義された制約(ポリシー)に基づき Kubernetes クラスタ上のリソースの制御・監査をすることができます。
  • Config Sync … GitOps の形式でクラスタ構成を同期/管理するコンポーネントです。構成ファイルが配置されたGit リポジトリとクラスタ間の連携をしてくれます。

実はこれまでも Config Connector をユーザー管理の GKE クラスタ上に導入することで、CaD なアプローチを用いた Google Cloud リソースの管理が可能でした。ただ、Config Connector は ”自前で GKE クラスタを用意する” 必要がありました。

クラウドリソースを管理するためにわざわざ GKE クラスタを構築/運用するのはちょっと… という方もいらっしゃったのではないでしょうか。この問題を解消してくれるのが、Config Controller です。

Config Controller は、Config Connector を含んだ上記 3 コンポーネントがプリインストールされた GKE クラスタを 1 コマンドでプロビジョニングしてくれます。この GKE クラスタは Google 管理となりますので、利用者側でアップグレードやスケールなど運用面を気にする必要はありません。

また、Policy Controller を利用して Google Cloud リソースに関する制約を設定できるようになるのもポイントです。先述の通り、Config Controller は Google Cloud リソースを Kubernetes のリソースとして定義しますので、Policy Controller を使った Kubernetes の世界での制約を Google Cloud リソースに対しても適用することができます。

Config Controller によるガードレールの構築

では、ここから本題に入っていきます。Google Cloud リソースに対するガードレールを構築する場合、Google Cloud ネイティブな機能だと以下のような方法が考えられます。

  1. 組織ポリシーを利用
  2. Config Controller (Policy Controller) を利用

まず最初に思いつくのが「1. 組織ポリシー」を活用する方法だと思います。組織ポリシーを設定することにより、Google Cloud リソースに関する各種制限を簡単にかけることができます。

例として、以下のような制限をかけることができます:

  • リソース ロケーションの制限
  • 許可される Google Cloud API とサービスを制限する
  • VM インスタンス用に許可される外部 IP を定義する (Compute Engine)
  • 公開アクセスの防止を適用する (Cloud Storage)、等

※その他、組織ポリシーの詳細については Seiji さんの記事「Google Cloud のイケてる(ような気がする)サービス・機能のご紹介」をご覧ください

ただ、組織ポリシーは事前に定義されたポリシーから選択する方式であるため、利用者側で独自にポリシーを作成したり自由にカスタマイズすることができません。既存の組織ポリシーが絶妙に要件と合っておらず苦労された方もいらっしゃったのではないでしょうか。

そこで選択肢として出てくるのが、「2. Config Controller 」です。Google Cloud のリソース管理に Config Controller を活用していただくと、Policy Controller による制約を設定することができますので、組織ポリシーには存在しないような自由度の高い制約を作成することが可能になります。また、組織ポリシーと Config Controller を併用することももちろんできますし、Config Controller で組織ポリシー自体を管理することもできます。

Policy Controller の制約の仕組み

Policy Controller のベースとなっている OPA Gatekeeper は、OPA (Open Policy Agent) というポリシーエンジンを Kuberentes の Admission Webhook 使って実装しています。ポリシー(ロジック)は Rego という言語で記述されます。

https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes/

Policy Controller の制約は以下 2 種類のリソースから構成されています。

制約テンプレート (Constraint Template)

制約のスキーマやロジックを定義するカスタムリソースです。実際に設定する制約のテンプレートとなる定義をこのリソースで記述します。ロジックは Rego で書かれます。

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: <制約テンプレート名>
spec:
crd:
spec:
names:
kind: <制約の kind>
validation:
openAPIV3Schema:
<スキーマの定義>
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
<ロジックの記述>

制約 (Constraint)

実際に設定する制約を定義するカスタムリソースです。制約で利用する各種パラメータや制約の適用対象となるリソースを指定します。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: <制約テンプレート内で定義した kind>
metadata:
name: <制約名>
spec:
enforcementAction: deny
match:
<制約の対象となるリソースの定義>
parameters:
<パラメータの定義>

上記のような制約+制約テンプレートをクラスタ内で事前に定義しておくことで、制約に違反するようなリソースの作成/更新等を防ぐことができます。

ちなみに制約を設定する前から存在している既存リソースが制約に違反している場合、 Violation として情報を記録します。 gatekeeper-securitycenter という OSS を利用することで、Poicy Controller で検知した Violation を Security Command Center に連携させることもできます。

実際に試してみる

ではここから実際に Config Controller でガードレールを構築してみます。以下の流れで進めていきます。

  1. Config Controller クラスタの作成
  2. Config Controller によるリソースの作成
  3. Policy Controller を使った制約の作成
  4. Config Controller による組織ポリシーの設定

1. Config Controller クラスタの作成

まず Config Controller クラスタを作成していきます。

事前準備として、Cloud Shell 上で以下コマンドを実行し、本手順で利用する API (Config Controller, GKE, Resource Manager) を有効化します。

$ gcloud services enable krmapihosting.googleapis.com \
container.googleapis.com \
cloudresourcemanager.googleapis.com

Config Controller クラスタを作成します。
CONFIG_CONTROLLER_NAME に任意の名前を設定し以下作成コマンドを実行します (完成まで 十数分程度時間がかかります)。

$ export CONFIG_CONTROLLER_NAME=<Config Controller クラスタ名>$ gcloud anthos config controller create ${CONFIG_CONTROLLER_NAME} \
--location=us-central1

処理が正常に完了し、クラスタが作成されていることを確認します。

$ gcloud anthos config controller list
NAME: projects/<project id>/locations/us-central1/krmApiHosts/<cluster name>
LOCATION: us-central1

Config Controller で利用する Service Account に対して必要な権限を付与します。Config Controller で管理するリソースが特定プロジェクト配下のみであれば、Service Account に対して対象プロジェクトのオーナー権限等を付与します(組織に対する操作が必要な場合は、組織レベルでの権限を付与します)。

$ export PROJECT_ID=$(gcloud config get-value project)
$ export SA_EMAIL="$(kubectl get ConfigConnectorContext -n config-control \
-o jsonpath='{.items[0].spec.googleServiceAccount}' 2> /dev/null)"
$ gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member "serviceAccount:${SA_EMAIL}" \
--role "roles/owner" \
--condition=None \
--project "${PROJECT_ID}"

2. Config Controller によるリソースの作成

ではここから実際に Config Controller 経由で Google Cloud リソースを作成していきます。東京リージョン (asia-northeast1) に Cloud Storage のバケットを作成するために以下のマニフェストを用意します。

apiVersion や kind で作成対象となる Google Cloud リソースを指定します。今回は GCS バケットを作成したいので、apiVersion に storage.cnrm.cloud.google.com/v1beta1 に kind に StorageBucket を指定します。バケットのロケーションとして東京 (asia-northeast1) を指定します。

リソースを作成するためにどういうマニフェストを書けば良いかは Config Connector の公式ドキュメントに情報がまとまっていますのでご参照ください。以下 Cloud Storage バケットの例です:

7 行目の namespace では config-control を指定しています。config-control は Config Controller クラスタ内にデフォルトで作成される Namespace です。config-control 上にオブジェクトを作成すると、Config Controller クラスタと同じプロジェクトに Google Cloud リソースが作成されます。(今回の例でいうと ${PROJECT_ID} で指定されているプロジェクト上にリソースが作成されます)

これは Config Controller (Config Connector) では、リソース作成のスコープを annotation で管理しており、config-control namespace には Config Controller クラスタがデプロイされてるプロジェクトが紐づけられているからです。(別プロジェクト上に作成したい場合は、別 Namespace を作成し対象プロジェクトを紐づけるか、直接リソースに annotation を付与することで実現可能です

apiVersion: v1
kind: Namespace
metadata:
annotations:
cnrm.cloud.google.com/project-id: ${PROJECT_ID}

マニフェストが作成できたら Config Controller クラスタに Apply してみます。

# バケットの作成
$ kubectl apply -f tokyo-bucket.yaml
storagebucket.storage.cnrm.cloud.google.com/gcs-tokyo-adventcal2021-kuchima created

ちゃんと作成ができたようです。念の為 Cloud Console からも確認をしてみます。

問題なさそうです。これで、Config Controller を使って asia-northeast1 上にバケットを作成することができました。

3. Policy Controller を使った制約の作成

では続いて Policy Controller による制約を作成していきたいと思います。まずは制約の基となるテンプレートを用意します。

以下が制約テンプレートの作成例です。許可されているロケーション上でのみリソースを作成可能にするという内容です(但し特定のキーワードを含むリソースは例外として指定されたロケーション以外でも作成可能)。

9 行目で今回作成する制約の kind (GCPStorageLocationConstraintV1) を定義しています。

11 ~ 20 行目部分が制約テンプレートのスキーマを定義している部分です。インプット パラメータとして、許可されるロケーションのリスト (locations) や制約の除外対象となるリソース名 (exemptions) を定義しています。

23 行目以降に実際の制約ロジックが記述されています。 allowedLocation(reviewLocation) で作成しようとしてるリソースのロケーションが許可されているかを確認し、また exempt(reviewName) では作成しようとしてるリソースが除外対象であるかを確認しています。

violation[{“msg”: msg}] に実際に防ぎたい内容を記述します。

37 行目では allowedLocation(bucketLocation) もしくは exempt(bucketName) どちらにも合致しないリソースの作成を防ぐように記述されています。また、45 行目ではインプット パラメータに値が入力されていない場合の処理が定義されています。

制約の作成

テンプレートが作成できたので、実際の制約を定義していきます。先ほど制約テンプレートで定義した kind: GCPStorageLocationConstraintV1 を指定し制約を作成します。

7 ~ 12 行目で制約をかける対象となる Google Cloud リソースを指定します。今回は GCS バケットの作成をコントロールしたいので、apiGroups に storage.cnrm.cloud.google.com 、kind に StorageBucket を指定します。この辺りの情報は先述した Config Connector の公式ドキュメントCRD を見ることでも確認できます。

15 行目以降で制約のパラメータを設定します。この制約では東京 (asia-northeast1) と大阪 (asia-northeast2) 上にのみバケット作成を許可しています。

制約テンプレートと制約マニフェストが作成できたら Config Controller クラスタに Apply します。

# 制約テンプレートの作成
$ kubectl apply -f gcpstoragelocationconstraintv1.yaml
constrainttemplate.templates.gatekeeper.sh/gcpstoragelocationconstraintv1 configured
# 制約の作成
$ kubectl apply -f gcs-tokyo-osaka-only.yaml
gcpstoragelocationconstraintv1.constraints.gatekeeper.sh/tokyo-and-osaka-only created

動作確認のため、大阪 (asia-northeast2) とシンガポール (asia-southeast1) 用のマニフェストを用意し、それぞれのロケーションにバケットを作成してみたいと思います。

# 大阪にバケットを作成(成功)
$ kubectl apply -f osaka-bucket.yaml
storagebucket.storage.cnrm.cloud.google.com/gcs-osaka-adventcal2021-kuchima created
# シンガポールにバケットを作成(失敗)
$ kubectl apply -f singapore-bucket.yaml
Error from server ([tokyo-and-osaka-only] Cloud Storage bucket <gcs-singapore-adventcal2021-kuchima> uses a disallowed location <asia-southeast1>, allowed locations are ["asia-northeast1", "asia-northeast2"]): error when creating "singapore-bucket.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [tokyo-and-osaka-only] Cloud Storage bucket <gcs-singapore-adventcal2021-kuchima> uses a disallowed location <asia-southeast1>, allowed locations are ["asia-northeast1", "asia-northeast2"]

想定通り大阪にバケットを作成できましたが、シンガポールには作成できませんでした。シンガポールのバケット作成時には asia-southeast1 が許可されたロケーションではないということで、制約テンプレートのロジック内で定義したエラーメッセージが出力されています。

次に、一度制約を削除し制約に違反したリソースが存在する状況を作り出します。その状況下で改めてロケーション制約を付与した際にどういう挙動になるかを確認してみます。

$ kubectl delete -f gcs-tokyo-osaka-only.yaml
gcpstoragelocationconstraintv1.constraints.gatekeeper.sh "tokyo-and-osaka-only" deleted
$ kubectl apply -f singapore-bucket.yaml
storagebucket.storage.cnrm.cloud.google.com/gcs-singapore-adventcal2021-kuchima created

シンガポールのバケットが作成できました。では、改めて tokyo-osaka-only の制約を設定してみます。

$ kubectl apply -f gcs-tokyo-osaka-only.yaml
gcpstoragelocationconstraintv1.constraints.gatekeeper.sh/tokyo-and-osaka-only created

既存のシンガポールバケットは変わらず存在していますので、制約に違反しているリソースを強制削除するような動きではないことが分かります。

制約リソースを Describe してみると、Violation としてシンガポールバケットが制約に違反していると記録されていることが分かりました。既存リソースの制約違反に関してはこのようにして監査が可能です(この Audit 情報は Cloud Logging でも確認できますし、Security Command Center への連携することも可能です)。

$ kubectl describe -f gcs-tokyo-osaka-only.yaml
Name: tokyo-and-osaka-only
Namespace:
Labels: <none>
Annotations: <none>
API Version: constraints.gatekeeper.sh/v1beta1
Kind: GCPStorageLocationConstraintV1
<中略>
Violations:
Enforcement Action: deny
Kind: StorageBucket
Message: Cloud Storage bucket <gcs-singapore-adventcal2021-kuchima> uses a disallowed location <asia-southeast1>, allowed locations are ["asia-northeast1", "asia-northeast2"]
Name: gcs-singapore-adventcal2021-kuchima
Namespace: config-control

4. Config Controller による組織ポリシーの設定

最後に、Config Controller で組織ポリシーを管理してみます。Config Controller で管理することにより、大事な組織ポリシーを間違って手動で無効化してしまった際も自動的に理想状態に修正されるか試してみます。

組織ポリシーには “constraints/storage.publicAccessPrevention” という公開アクセスを防ぐための便利なポリシーが存在しますので、これを設定していきます。

組織ポリシーは以下のように ResourceManagerPolicy という kind を使い、設定するポリシーを spec.constraint に指定します。

組織ポリシーを設定するために、Config Controller サービスアカウントに対して orgpolicy.policyAdmin のロールを付与します。

$ export SA_EMAIL="$(kubectl get ConfigConnectorContext -n config-control \
-o jsonpath='{.items[0].spec.googleServiceAccount}' 2> /dev/null)"
$ export ORG_ID=$(gcloud organizations list --format 'value(ID)')# Service Account に対する Policy Admin 権限の付与
$ gcloud organizations add-iam-policy-binding ${ORG_ID} \
--role=roles/orgpolicy.policyAdmin \
--condition=None \
--member="serviceAccount:${SA_EMAIL}"

マニフェストを Apply し、組織ポリシーを設定します。

# 組織ポリシーの設定
$ kubectl apply -f gcs-prevent-publicaccess.yaml

Cloud Console 上から確認してみると、公開アクセス防止の組織ポリシーが適用されていることが分かります。

それではこの大事な組織ポリシーを間違えて無効にしてしまった際の挙動をみてみましょう。手動で無効化してみます。

未適用の状態になりました。Config Controller (Config Connector) では 10 分間隔でリソースを調整するので 10 分ほど待ちます。

〜〜〜〜〜〜 10 分後〜〜〜〜〜〜

適用済みの状態に戻りました。Config Controller が予め定義された理想状態実際の環境間での差分に気づいてくれ、自動的に修正をしてくれました。大事な設定をうっかり手でいじってしまっても自動で直してくれるのは有難いですね。

Config Controller 利用時の注意点

ここまでで Config Controller の特徴やメリットなどについて説明をしてきましたが、利用する際の注意点もいくつかあるのでご紹介します。

  • Config Controller (正確には Config Connector) ではまだサポートされていないリソースも存在します。ご利用の際は以下ドキュメントを参照いただき、対象リソースがサポートされているかご確認ください。
  • Config Controller を利用すると、Anthos Config Management (ACM) の利用料金 ($0.10/時間) と Config Controller がインストールされた GKE クラスタの料金 (GKE クラスタ管理費 $0.10/時間 + Worker Node 費用)が発生します。

※ Anthos ライセンスを既に購入されている場合は、ACM 利用料 ($0.10/時間) は課金されません。

Config Controller ユーザー事例

最後に、Google Cloud Next ’21 で Goldman Sachs 社における Config Controller の事例が発表されていましたので、参考までにリンクを貼っておきます。気になった方はぜひご覧ください。

まとめ

Config Controller を活用することにより、ポリシー制御(ガードレール)と Reconciliation(自己修復)を組み合わせ Google Cloud 環境をより高度に管理することができるようになりました。

また本記事では紹介しきれませんでしたが、Config Controller では GitOps による運用をクラウドリソースの管理にも適用することができたり、Landing Zone という Google Cloud のベスト プラクティスに基づいたブループリントを活用し、環境を迅速にセットアップすることもできます。

この記事を読んで Config Controller が気になった方がいらっしゃいましたら、ぜひ試してみてください!

明日 3 日目の記事は「Google Cloud Japan に入社してみた」です。お楽しみに!

参考資料

--

--

Kazuki Uchima
google-cloud-jp

Application Modernization Specialist at Google Cloud. All views and opinions are my own.