Enforce Service Mesh policies

Platform Admin Platform Admin Duration: 10 min | Persona: Platform Admin

In this section, you will enforce policies in order to make sure that your clusters, namespaces and apps are well configured to be secured by your Service Mesh.

Initialize variables:

WORK_DIR=~/
source ${WORK_DIR}acm-workshop-variables.sh
echo "export INGRESS_GATEWAY_NAMESPACE=asm-ingress" >> ${WORK_DIR}acm-workshop-variables.sh
source ${WORK_DIR}acm-workshop-variables.sh

Define “Automatic sidecar proxy injection” policies

https://cloud.google.com/service-mesh/docs/anthos-service-mesh-proxy-injection

Define the namespaces-automatic-sidecar-injection-label Constraint based on the K8sRequiredLabels ConstraintTemplate for Namespaces:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/namespaces-automatic-sidecar-injection-label.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: namespaces-automatic-sidecar-injection-label
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires Namespaces to have the "istio-injection" label in order to be included in the Service Mesh.',
        remediation: 'Any Namespaces should have the "istio-injection" label with the "enabled" value.'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - ""
      kinds:
      - Namespace
    excludedNamespaces:
    - config-management-monitoring
    - config-management-system
    - default
    - gatekeeper-system
    - istio-system
    - kube-node-lease
    - kube-public
    - kube-system
    - resource-group-system
    - poco-trial
  parameters:
    labels:
    - allowedRegex: enabled
      key: istio-injection
EOF

Define the pods-sidecar-injection-annotation Constraint based on the AsmSidecarInjection ConstraintTemplate for Pods:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/pods-sidecar-injection-annotation.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmSidecarInjection
metadata:
  name: pods-sidecar-injection-annotation
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Enforce the istio proxy sidecar always been injected to workload pods.',
        remediation: 'Any Pods shouldn't have the "sidecar.istio.io/inject" annotation set to "false".'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - ""
      kinds:
      - Pod
    excludedNamespaces:
    - kube-system # to exclude istio-cni pods
  parameters:
    strictnessLevel: High
EOF

Define “STRICT mTLS in the Mesh” policies

Define the mesh-level-strict-mtls Constraint based on the AsmPeerAuthnMeshStrictMtls ConstraintTemplate:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/mesh-level-strict-mtls.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmPeerAuthnMeshStrictMtls
metadata:
  name: mesh-level-strict-mtls
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Enforce the mesh level strict mtls PeerAuthentication.',
        remediation: 'The istio-system namespace should have a default PeerAuthentication with STRICT mTLS.'
      }"
spec:
  enforcementAction: deny
  parameters:
    rootNamespace: istio-system
    strictnessLevel: High
EOF

Define the peerauthentication-strict-mtls Constraint based on the AsmPeerAuthnStrictMtls ConstraintTemplate for PeerAuthentications:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/peerauthentication-strict-mtls.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmPeerAuthnStrictMtls
metadata:
  name: peerauthentication-strict-mtls
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Enforce all PeerAuthentications cannot overwrite strict mtls.',
        remediation: 'Any PeerAuthentications should have STRICT mTLS.'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - security.istio.io
      kinds:
      - PeerAuthentication
  parameters:
    rootNamespace: istio-system
    strictnessLevel: High
EOF

Define the destination-rule-tls-enabled Constraint based on the DestinationRuleTLSEnabled ConstraintTemplate for DestinationRules:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/destinationrule-tls-enabled.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: DestinationRuleTLSEnabled
metadata:
  name: destinationrule-tls-enabled
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Prohibits disabling TLS for all hosts and host subsets in Istio DestinationRules.',
        remediation: 'Any DestinationRules should not disable TLS.'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - networking.istio.io
      kinds:
      - DestinationRule
EOF

Define AuthorizationPolicy policies

https://istio.io/latest/docs/reference/config/security/authorization-policy/

Define the default-deny-authorization-policies Constraint based on the AsmAuthzPolicyDefaultDeny ConstraintTemplate for DestinationRules:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/default-deny-authorization-policies.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmAuthzPolicyDefaultDeny
metadata:
  name: default-deny-authorization-policies
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Enforce the mesh level default deny AuthorizationPolicy. Reference to https://istio.io/latest/docs/ops/best-practices/security/#use-default-deny-patterns.',
        remediation: 'The istio-system namespace should have a default deny-all AuthorizationPolicy for the entire mesh.'
      }"
spec:
  enforcementAction: deny
  parameters:
    rootNamespace: istio-system
    strictnessLevel: High
EOF

Define the authz-source-principals-not-all Constraint based on the AsmAuthzPolicyEnforceSourcePrincipals ConstraintTemplate for DestinationRules:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/authz-source-principals-not-all.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmAuthzPolicyEnforceSourcePrincipals
metadata:
  name: authz-source-principals-not-all
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires that Istio AuthorizationPolicy "from" field, when defined, has source principles, which must be set to something other than "*".',
        remediation: 'Any AuthorizationPolicies shouldn't define the "from" field with "*".'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - security.istio.io
      kinds:
      - AuthorizationPolicy
    excludedNamespaces:
      - ${INGRESS_GATEWAY_NAMESPACE}
EOF

Define the authz-source-principals-prefix-not-default Constraint based on the AsmAuthzPolicyDisallowedPrefix ConstraintTemplate for AuthorizationPolicies:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/authz-source-principals-prefix-not-default.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AsmAuthzPolicyDisallowedPrefix
metadata:
  name: authz-source-principals-prefix-not-default
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires that principals and namespaces in Istio AuthorizationPolicy rules not have a prefix from a specified list.',
        remediation: 'Any AuthorizationPolicies shouldn't have the principal as "default".'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - security.istio.io
      kinds:
      - AuthorizationPolicy
  parameters:
    disallowedPrincipalPrefixes:
    - default
EOF

Define K8sBlockAllIngress policy

Define the block-all-ingress Constraint based on the K8sBlockAllIngress ConstraintTemplate to only allow public ingress from the ASM Ingress Gateway:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/block-all-ingress.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockAllIngress
metadata:
  name: block-all-ingress
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Disallows the creation of Ingress objects (Ingress, Gateway, and Service types of NodePort and LoadBalancer).',
        remediation: 'Any Ingress objects (Ingress, Gateway, and Service) should go through the ASM Ingress Gateway instead.'
      }"
spec:
  enforcementAction: deny
  match:
    excludedNamespaces:
    - kube-system # default-http-backend as NodePort
    - ${INGRESS_GATEWAY_NAMESPACE} # asm-ingressgateway as LoadBalancer
EOF

Define VirtualServiceWithHost policy

Define the ConstraintTemplate:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/templates/virtualservicewithhost.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: virtualservicewithhost
  annotations:
    description: "VirtualService shouldn't define the hosts with *."
spec:
  crd:
    spec:
      names:
        kind: VirtualServiceWithHost
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |-
        package virtualservicewithhost
        # spec.hosts does not exist
        violation[{"msg": msg}] {
          is_virtualservice(input.review.kind)
          not contains_hosts(input.review.object.spec)
          msg := "hosts does not exist"
        }
        # spec.hosts does not contain '*'
        violation[{"msg": msg}] {
          is_virtualservice(input.review.kind)
          principal := input.review.object.spec.hosts[_]
          principal == "*"
          msg := "hosts[] cannot be '*'"
        }
        is_virtualservice(kind) {
          kind.kind == "VirtualService"
          kind.group == "networking.istio.io"
        }
        contains_hosts(spec) {
          spec.hosts
        }
EOF

Define the virtual-service-with-host Constraint based on the VirtualServiceWithHost ConstraintTemplate just defined:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/policies/constraints/virtual-service-with-host.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: VirtualServiceWithHost
metadata:
  name: virtual-service-with-host
  annotations:
    policycontroller.gke.io/constraintData: |
      "{
        description: 'Requires that Istio VirtualService "hosts" must be set to something other than "*".',
        remediation: 'Any VirtualService shouldn't define the "hosts" with "*".'
      }"
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups:
      - networking.istio.io
      kinds:
      - VirtualService
EOF

Update Gatekeeper config for Referrential Constraints

Update the previously defined config-referential-constraints Config:

cat <<EOF > ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/gatekeeper-system/config-referential-constraints.yaml
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: gatekeeper-system
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Namespace"
      - group: "networking.k8s.io"
        version: "v1"
        kind: "NetworkPolicy"
      - group: "security.istio.io"
        version: "v1beta1"
        kind: "PeerAuthentication"
      - group: "security.istio.io"
        version: "v1beta1"
        kind: "AuthorizationPolicy"
EOF

Deploy Kubernetes manifests

cd ${WORK_DIR}$GKE_CONFIGS_DIR_NAME/
git add . && git commit -m "Policies for ASM/Istio" && git push origin main

Check deployments

List the Kubernetes resources managed by Config Sync in GKE cluster for the GKE cluster configs repository:

Run this command and click on this link:

echo -e "https://console.cloud.google.com/kubernetes/config_management/packages?project=${TENANT_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 $TENANT_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".

See the Policy Controller Constraints without any violations in the GKE cluster, by running this command and click on this link:

echo -e "https://console.cloud.google.com/kubernetes/policy_controller/dashboard?project=${TENANT_PROJECT_ID}"

List the GitHub runs for the GKE cluster configs repository:

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