AWS WAF is a web application firewall that lets you monitor the HTTP and HTTPS requests that are forwarded to your protected web application resources.
You can use an Amazon CloudFront to add a Web Application Firewall (WAF) to your {product-title} (ROSA) workloads. Using an external solution protects ROSA resources from experiencing denial of service due to handling the WAF.
-
You have access to the OpenShift CLI (
oc
). -
You have access to the AWS CLI (
aws
).
-
Prepare the environment variables:
$ export AWS_PAGER="" $ export CLUSTER_NAME=$(oc get infrastructure cluster -o=jsonpath="{.status.infrastructureName}" | sed 's/-[a-z0-9]\{5\}$//') $ export REGION=$(oc get infrastructure cluster -o=jsonpath="{.status.platformStatus.aws.region}") $ export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) $ export SCRATCH="/tmp/${CLUSTER_NAME}/cloudfront-waf" $ mkdir -p ${SCRATCH} $ echo "Cluster: ${CLUSTER_NAME}, Region: ${REGION}, AWS Account ID: ${AWS_ACCOUNT_ID}"
It is necessary to configure a secondary ingress controller to segment your external WAF-protected traffic from your standard (and default) cluster ingress controller. In ROSA, we do this using the Custom Domain Operator.
-
A unique domain, such as
*.apps.<company_name>.io
-
A custom SAN or wildcard certificate, such as
CN=*.apps.<company_name>.io
-
Create a new project
$ oc new-project waf-demo
-
Create a new TLS secret from a private key and a public certificate, where
fullchain.pem
is your full wildcard certificate chain (including any intermediaries) andprivkey.pem
is your wildcard certificate’s private key.Example$ oc -n waf-demo create secret tls waf-tls --cert=fullchain.pem --key=privkey.pem
-
Create a new
CustomDomain
custom resource (CR):Examplewaf-custom-domain.yaml
apiVersion: managed.openshift.io/v1alpha1 kind: CustomDomain metadata: name: cloudfront-waf spec: domain: apps.<company_name>.io (1) scope: External loadBalancerType: NLB certificate: name: waf-tls namespace: waf-demo routeSelector: (2) matchLabels: route: waf
-
The custom domain.
-
Filters the set of routes serviced by the CustomDomain ingress. In this tutorial, we will use the
waf
route selector, but if no value was to be provided, no filtering would occur.
-
-
Apply the CR:
Example$ oc apply -f waf-custom-domain.yaml
-
Verify that your custom domain ingress controller has been deployed and is
Ready
:$ oc get customdomains
Example outputNAME ENDPOINT DOMAIN STATUS cloudfront-waf xxrywp.<company_name>.cluster-01.opln.s1.openshiftapps.com *.apps.<company_name>.io Ready
The AWS WAF service is a web application firewall that lets you monitor, protect, and control the HTTP and HTTPS requests that are forwarded to your protected web application resources, like ROSA.
-
Create a AWS WAF rules file to apply to our web ACL:
$ cat << EOF > ${SCRATCH}/waf-rules.json [ { "Name": "AWS-AWSManagedRulesCommonRuleSet", "Priority": 0, "Statement": { "ManagedRuleGroupStatement": { "VendorName": "AWS", "Name": "AWSManagedRulesCommonRuleSet" } }, "OverrideAction": { "None": {} }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "AWS-AWSManagedRulesCommonRuleSet" } }, { "Name": "AWS-AWSManagedRulesSQLiRuleSet", "Priority": 1, "Statement": { "ManagedRuleGroupStatement": { "VendorName": "AWS", "Name": "AWSManagedRulesSQLiRuleSet" } }, "OverrideAction": { "None": {} }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "AWS-AWSManagedRulesSQLiRuleSet" } } ] EOF
This will enable the Core (Common) and SQL AWS Managed Rule Sets.
-
Create an AWS WAF Web ACL using the rules we specified above:
$ WAF_WACL=$(aws wafv2 create-web-acl \ --name cloudfront-waf \ --region ${REGION} \ --default-action Allow={} \ --scope CLOUDFRONT \ --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=${CLUSTER_NAME}-waf-metrics \ --rules file://${SCRATCH}/waf-rules.json \ --query 'Summary.Name' \ --output text)
-
Retrieve the newly created custom domain ingress controller’s NLB hostname:
$ NLB=$(oc -n openshift-ingress get service router-cloudfront-waf \ -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') $ echo "Origin domain: ${NLB}"
-
Import your certificate into Amazon Certificate Manager, where
cert.pem
is your wildcard certificate,fullchain.pem
is your wildcard certificate’s chain andprivkey.pem
is your wildcard certificate’s private key.NoteRegardless of what region your cluster is deployed, you must import this certificate to
us-east-1
as Amazon CloudFront is a global AWS service.Example$ aws acm import-certificate --certificate file://cert.pem \ --certificate-chain file://fullchain.pem \ --private-key file://privkey.pem \ --region us-east-1
-
Log into the AWS console to create a CloudFront distribution.
-
Configure the CloudFront distribution by using the following information:
NoteIf an option is not specified in the table below, leave them the default (which may be blank).
Option Value Origin domain
Output from the command above [1]
Name
rosa-waf-ingress [2]
Viewer protocol policy
Redirect HTTP to HTTPS
Allowed HTTP methods
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cache policy
CachingDisabled
Origin request policy
AllViewer
Web Application Firewall (WAF)
Enable security protections
Use existing WAF configuration
true
Choose a web ACL
cloudfront-waf
Alternate domain name (CNAME)
*.apps.<company_name>.io [3]
Custom SSL certificate
Select the certificate you imported from the step above [4]
-
Run
echo ${NLB}
to get the origin domain. -
If you have multiple clusters, ensure the origin name is unique.
-
This should match the wildcard domain you used to create the custom domain ingress controller.
-
This should match the alternate domain name entered above.
-
-
Retrieve the Amazon CloudFront Distribution endpoint:
$ aws cloudfront list-distributions --query "DistributionList.Items[?Origins.Items[?DomainName=='${NLB}']].DomainName" --output text
-
Update the DNS of your custom wildcard domain with a CNAME to the Amazon CloudFront Distribution endpoint from the step above.
Example*.apps.<company_name>.io CNAME d1b2c3d4e5f6g7.cloudfront.net
-
Deploy a hello world application:
$ oc -n waf-demo new-app --image=docker.io/openshift/hello-openshift
-
Create a route for the application specifying your custom domain name:
Example$ oc -n waf-demo create route edge --service=hello-openshift hello-openshift-tls \ --hostname hello-openshift.apps.<company_name>.io
-
Label the route to admit it to your custom domain ingress controller:
$ oc -n waf-demo label route.route.openshift.io/hello-openshift-tls route=waf
-
Test that the app is accessible behind Amazon CloudFront:
Example$ curl "https://hello-openshift.apps.<company_name>.io"
Example outputHello OpenShift!
-
Test that the WAF denies a bad request:
Example$ curl -X POST "https://hello-openshift.apps.<company_name>.io" \ -F "user='<script><alert>Hello></alert></script>'"
Example output<html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html
The expected result is a
403 Forbidden
error, which means the AWS WAF is protecting your application.
-
Custom domains for applications in the Red Hat documentation
-
Adding Extra Security with AWS WAF, CloudFront and ROSA | Amazon Web Services on YouTube