Hey all, was wondering if there's built-in functionality within os-OPNWAF to perform request header inspection.
I have an on-prem hypervisor and use CloudFront as a cache-less reverse proxy on a flat rate plan as such:
1.) Client queries public DNS A/AAAA for host.domain.tld
2.) Route 53 responds with an Alias A/AAAA to d123example.cloudfront.net
3.) Client connects to CloudFront, initial HTTPS cert/host/WAF check
4.) Because CloudFront is cache-less, if the WAF inspection passes, begins an origin request to DNS A for onprem.domain.tld which is updated over time with os-ddclient
5.) CloudFront connects to my edge OPNsense (hardware Deciso 697 appliance) via PubIp:443 per the onprem.domain.tld record's IPv4 value
6.) Edge OPNsense uses its Destination NAT scoping CloudFront IPs as the allowed source (CLOUDFRONT_ORIGIN_FACING from AWS' ip_ranges.json) towards This Firewall:443 with a corresponding firewall rule on the WAN interface
7.) os-OPNWAF receives the request and forwards onto the corresponding vhost/location pairs based on Host header
This is working completely fine right now from a usability perspective, but in addition to source IP restrictions I'd also like to perform requester header inspection and drop if not matching. In CloudFront I use a X-Originated-From-CloudFront custom origin request header for all my workflows, for cloud/hybrid/on-prem destinations alike, because ideally you can then inspect the value origin-side and drop if not matching. This is outlined by AWS as a design pattern here:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/restrict-access-to-load-balancer.html#restrict-alb-add-custom-header
However, when reviewing os-OPNWAF's Web GUI settings, I only see options to Add/Merge/Unset/etc. request headers, but no way to inspect them. The main reason I bought an official appliance was for OPNsense Business licensing (plus the niceties of the hardware, this unit is wonderful) which includes os-OPNWAF and was hoping to replace my on-prem NGINX -> Anubis -> Origin flow with it where NGINX was performing request header inspection.
Before I get to modifying the underlying Apache config of os-OPNWAF I was wondering if there's a known implementation pattern of doing:
Check Request Header
If Request Header Value != Desired Value
Drop Request
I'd prefer to not add any other plugins to my OPNsense if possible, i.e. throwing up the NGINX-based plugin and whatnot seems heavy for the firewall, if not currently possible I can accept os-OPNWAF's current features and keep my NGINX for header inspection, but it would be nice if request header inspection was added as a feature.
Thanks in advance for any insights!
Right now I don't know of a way how this would be possible in the current GUI.
Though in the latest OPNWAF version we added new modsecurity and apache import folders in which you can drop custom configs.
You can probably create a custom modsecurity rule that can drop all traffic that does not match a certain header and add that to the modsecurity import folder.
/usr/local/etc/apache24/modsecurity-user/
Thanks a ton! This is working as expected, outlining steps for others on the internet:
1.) SSH into the OPNsense
2.) Gather the upcoming $OriginHeaderValue value with the header secret from CloudFront -> Distribution -> Origins -> OPNsense Origin -> Edit -> "Add custom header"
3.) vi /usr/local/etc/apache24/modsecurity-user/001-x-originated-from-cloudfront.conf
SecRule REQUEST_HEADERS:X-Originated-From-CloudFront "!@streq $OriginHeaderValue" "id:100001,phase:1,deny,status:403,log,msg:'Denied - Request Bypassed the CDN'"
4.) service apache24 reload
Works for me right away, a lack of this header or an invalid value thereof 403 as desired. Note that my rule as-written applies for ALL Virtual Hosts in os-OPNWAF, you may need to refactor if you split some public stuff across CloudFront-sourced and direct-to-OPNsense concurrently.
Type opnsense-business
Version 26.4_6
os-OPNWAF (installed) 2.2_1 4.75MiB 1 OPNsense Web Application Firewall using the well-known Apache webserver
Cool thank for sharing :)
Just keep in mind that your rule ID might overlap with IDs that the templating engine generates for crs-exclusions.conf. (If you exclude rules in the GUI it generates custom modsecurity rules as well)
It also starts at 100001.
Good catch, I'll sanity check what already exists (figured this was a typical override with incrementing 0->9 priority separate from baseline) but will review and spruce this up. Thanks a ton again, things are spinning beautifully here!