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