If you are using Anthos Service Mesh to deliver your public applications from a GFE HTTPS LB, I would strongly suggest enabling Cloud Armor which is a WAF (web application firewall) that can mitigate and defend against a variety of attacks such as cross-site scripting and denial of service.
As a summary overview, the first step is to create a Cloud Armor security policy, then create a BackendConfig that is referenced from an annotation on the service you want to protect.
Here is how you would create a simple policy that senses cross-site scripting and denial of service.
gcloud compute security-policies create my-security-policy --description "my XSS and DDOS policy" gcloud compute security-policies update my-security-policy --log-level=VERBOSE gcloud compute security-policies rules create 1000 --security-policy my-security-policy --expression "evaluatePreconfiguredExpr('xss-stable')" --action deny-403 --description "XSS attack filtering" gcloud beta compute security-policies update my-security-policy --enable-layer7-ddos-defense
With this security policy created, the next step is to add an annotation to your service that references a BackendConfig. In turn, the BackendConfig provides a health check and security policy reference.
Enabling security policy on ASM IngressGateway service
If you are following the pattern from Google’s documentation on “Exposing service mesh applications through GKE Ingress“, then your GCP HTTPS LB is pointing at the ASM service entrypoint. All services you project unto the VirtualService/Gateway have an IngressGateway selected. Therefore you can set the security policy on the IngressGateway, and all services projected unto it will benefit.
In this scenario, your ingressgateway service should have an “cloud.google.com/backend-config” annotation pointing to the BackendConfig like below.
kind: Service metadata: name: istio-ingressgateway labels: app: istio-ingressgateway istio: ingressgateway annotations: cloud.google.com/backend-config: '{"default": "ingress-backendconfig"}' ... spec: ports: - name: status-port port: 15021 protocol: TCP targetPort: 15021 ...
The BackendConfig is then defined as below, using port 15021 as the health check port, and referring to “my-security-policy” as created by gcloud earlier.
apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: ingress-backendconfig #namespace: default spec: healthCheck: requestPath: /healthz/ready port: 15021 type: HTTP securityPolicy: name: my-security-policy
Enabling security policy directly on individual service
If instead of pointing at the ASM entry point you are using something like Container native load-balancing where the GCP HTTPS LB uses NEG (Network Endpoint Groups) to point directly at pods, then you need to set the BackendConfig explicitly for that individual service.
Below is a simple hello world service, with the added “cloud.google.com/backend-config” annotation that points to a BackendConfig named “golang-hello-world-web-service-backendconfig”.
apiVersion: v1 kind: Service metadata: name: golang-hello-world-web-service #namespace: default labels: app: golang-hello-world-web annotations: cloud.google.com/backend-config: '{"default": "golang-hello-world-web-service-backendconfig"}' spec: ports: - port: 8080 name: http targetPort: 8080 protocol: TCP selector: app: golang-hello-world-web
The matching BackendConfig defines the health check at port 8080, as well as applying the “my-security-policy” created by gcloud earlier.
apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: golang-hello-world-web-service-backendconfig spec: healthCheck: checkIntervalSec: 15 port: 8080 type: HTTP requestPath: /healthz securityPolicy: name: my-security-policy
REFERENCES
google, configure security policies, examples
google, WAF rules language reference
google, load balancer logging and monitoring
google, exposing service mesh apps through gke ingress
alwaysupon.com, GKE, ASM, and Cloud Armor
tudip.com, GCP HTTP LB with Cloud Armor
itnext.io, GCP Ingress for Anthos multi-cluster ingress and LB and WAF
NOTES
Add rule to block certain regions
gcloud compute security-policies list my_security_policy=my-policy rule_num=1000 gcloud compute security-policies rules create $rule_num \ --security-policy $my_security_policy \ --expression "origin.region_code == 'RU' || origin.region_code == 'IR'" \ --action "deny-403" \ --description "Block embargo countries"
Add list of preconfigured WAF rules
rules="xss-stable cve-canary sqli-v33-stable lfi-v33-stable rfi-v33-stable rce-v33-stable methodenforcement-v33-stable scannerdetection-v33-stable protocolattack-v33-stable sessionfixation-v33-stable java-v33-stable nodejs-v33-stable" COUNTER=1000 for rule in $rules; do gcloud compute security-policies rules create $COUNTER --security-policy $my_security_policy --expression "evaluatePreconfiguredExpr('$rule')" --action deny-403 --description "$rule" ((COUNTER+=2)) done
block a single external IP address
theIP=a.b.c.d my_security_policy=my-policy rule_num=1000 gcloud compute security-policies describe $my_security_policy # takes ~60 seconds to take affect gcloud compute security-policies rules create $rule_num \ --security-policy $my_security_policy \ --description "block traffic from $theIP/32" \ --src-ip-ranges "$theIP/32" \ --action "deny-403" # to delete rule and re-allow IP address gcloud compute security-policies rules delete $rule_num \ --security-policy $my_security_policy --quiet
throttles traffic by domain, path, and method
ACTION=create SECURITY_POLICY=my-security-policy PRIORITY=100 RATE_LIMIT_THRESHOLD_COUNT=3 RATE_LIMIT_THRESHOLD_INTERVAL_SEC=30 THE_HOST=my.domain.com THE_PATH=/test THE_METHOD=GET gcloud compute security-policies rules $ACTION $PRIORITY \ --security-policy=$SECURITY_POLICY \ --expression="request.method=='$THE_METHOD' && request.path.startsWith('$THE_PATH') && request.headers['host'].lower().contains('$THE_HOST')" \ --action "throttle" \ --rate-limit-threshold-count=$RATE_LIMIT_THRESHOLD_COUNT \ --rate-limit-threshold-interval-sec=$RATE_LIMIT_THRESHOLD_INTERVAL_SEC \ --conform-action=allow \ --exceed-action=deny-502 \ --enforce-on-key=sni