Enforce GCP resources policies

Org Admin Org Admin Duration: 5 min | Persona: Org Admin

In this section, you will set up policies in order to enforce governance against the Kubernetes manifests defining your Google Cloud services. As an example, you will limit the locations and the kind available for the Google Cloud services.

Initialize variables:

WORK_DIR=~/
source ${WORK_DIR}acm-workshop-variables.sh
echo "export GKE_LOCATION=northamerica-northeast1" >> ${WORK_DIR}acm-workshop-variables.sh
source ${WORK_DIR}acm-workshop-variables.sh
Info

We are defining the GKE_LOCATION in northamerica-northeast1 this will be used later for the location of the VPC, GKE, Artifact Registry, etc. in the Tenant project. We are using this region because that’s the greenest Google Cloud region (Low CO2) in the regions supported by GKE Confidential Nodes used in this workshop.

Define “Allowed KCC resources” policies

Define the ConstraintTemplate resource:

cat <<EOF > ${WORK_DIR}$HOST_PROJECT_DIR_NAME/policies/templates/allowedkccresources.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: allowedkccresources
  annotations:
    description: "Requirements for any KCC resources."
spec:
  crd:
    spec:
      names:
        kind: AllowedKccResources
      validation:
        legacySchema: false
        openAPIV3Schema:
          properties:
            allowedKinds:
              items:
                type: string
              type: array
          type: object
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |-
        package allowedkccresources
        violation[{"msg": msg}] {
          _matches_group(input.review.kind.group)
          objectKind := input.review.kind.kind
          not _matches_kind(input.parameters.allowedKinds, objectKind)
          msg := sprintf("KCC resource of kind: %v is not allowed", [objectKind])
        }
        _matches_group(group) {
          endswith(group, ".cnrm.cloud.google.com")
          not group == "core.cnrm.cloud.google.com"
        }
        _matches_kind(allowedKinds, objectKind) {
          allowedKinds[_] = objectKind
        }
EOF

Define the Constraint resource:

cat <<EOF > ${WORK_DIR}$HOST_PROJECT_DIR_NAME/policies/constraints/allowed-kcc-resources.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AllowedKccResources
metadata:
  name: allowedkccresources
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires Config Connector resources kind to be in the specified list.',
        remediation: 'Any Config Connector resources kind should be in the specified list, they are the only Config Connector resources kind allowed.'
      }"
spec:
  enforcementAction: deny
  parameters:
    allowedKinds:
    - ArtifactRegistryRepository
    - ComputeAddress
    - ComputeNetwork
    - ComputeRouter
    - ComputeRouterNAT
    - ComputeSecurityPolicy
    - ComputeSSLPolicy
    - ComputeSubnetwork
    - ContainerCluster
    - ContainerNodePool
    - GKEHubFeature
    - GKEHubFeatureMembership
    - GKEHubMembership
    - IAMPartialPolicy
    - IAMPolicyMember
    - IAMServiceAccount
    - MonitoringAlertPolicy
    - MonitoringNotificationChannel
    - MonitoringUptimeCheckConfig
    - Project
    - RedisInstance
    - Service
    - SpannerDatabase
    - SpannerInstance
EOF

Define “Allowed GCP locations” policies

Define the ConstraintTemplate resource:

cat <<EOF > ${WORK_DIR}$HOST_PROJECT_DIR_NAME/policies/templates/limitlocations.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: limitlocations
  annotations:
    description: "Allowed GCP locations."
spec:
  crd:
    spec:
      names:
        kind: LimitLocations
      validation:
        openAPIV3Schema:
          type: object
          properties:
            locations:
              description: List of allowed GCP locations
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |-
        package limitlocations
        violation[{"msg":msg}] {
          contains(input.review.object.apiVersion, "cnrm.cloud.google.com")
          not allowedLocation(input.review.object.spec.location)
          msg := sprintf("%s %s uses a disallowed location: %s, authorized locations are: %s", [input.review.object.kind, input.review.object.metadata.name, input.review.object.spec.location, input.parameters.locations])
        }
        violation[{"msg":msg}] {
          contains(input.review.object.apiVersion, "cnrm.cloud.google.com")
          not allowedLocation(input.review.object.spec.region)
          msg := sprintf("%s %s uses a disallowed location: %s, authorized locations are: %s", [input.review.object.kind, input.review.object.metadata.name, input.review.object.spec.region, input.parameters.locations])
        }
        allowedLocation(reviewLocation) {
          locations := input.parameters.locations
          satisfied := [good |
            location = locations[_]
            good = lower(location) == lower(reviewLocation)
          ]
          any(satisfied)
        }
EOF

Define the Constraint resource:

cat <<EOF > ${WORK_DIR}$HOST_PROJECT_DIR_NAME/policies/constraints/allowed-locations.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: LimitLocations
metadata:
  name: allowed-locations
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires Config Connector resources location to be in the specified list.',
        remediation: 'Any Config Connector resources location should be in the specified list, they are the only Config Connector resources location allowed.'
      }"
spec:
  enforcementAction: deny
  parameters:
    locations:
      - "northamerica-northeast1"
      - "global"
EOF

Deploy Kubernetes manifests

cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME/
git add . && git commit -m "Enforce policies for GCP resources" && git push origin main

Check deployments

List the Kubernetes resources managed by Config Sync in Config Controller for the Host project configs repository:

Run this command and click on this link:

echo -e "https://console.cloud.google.com/kubernetes/config_management/packages?project=${HOST_PROJECT_ID}"

Wait until you see the Sync status column as Synced and the Reconcile status column as Current.

Run this command:

gcloud alpha anthos config sync repo describe \
    --project $HOST_PROJECT_ID \
    --managed-resources all \
    --sync-name root-sync \
    --sync-namespace config-management-system

Wait and re-run this command above until you see "status": "SYNCED". All the managed_resources listed should have STATUS: Current too.

List the GitHub runs for the Host project configs repository:

cd ${WORK_DIR}$HOST_PROJECT_DIR_NAME && gh run list