Make the OWASP WAF rules actually evaluate (preview mode first) - #153
Merged
Merged
Conversation
The three preconfigured WAF rules in `hangar-armor` have never evaluated a request. They sit at priority 2000-2002, below the rate-limit rules, and Cloud Armor enforces the first matching rule and stops -- the throttle rules between them match every request. Confirmed in the load-balancer logs while fixing #152: every request reports `enforcedSecurityPolicy priority 1000`, including a scanner walking /cgi-bin, /docSQL and ~60 other paths, which collects a 429 from the rate limiter rather than a 403 from the RFI rule. They were dead config, not defence. This moves them to 700-702, above the throttles, which is what makes them run. They land `preview = true`. A preview rule logs its match and evaluation continues to the next rule, so the 429 ceilings still apply and nothing new is denied -- this commit is behaviour-neutral by construction. That is deliberate: preconfigured CRS rules are known to false-positive on ordinary traffic, and because these have never been in this dashboard's request path, their real hit rate here is unknown rather than demonstrably zero. Enforcing three untested deny rules on an origin Mozilla staff use daily, in the same change that first makes them reachable, would be the wrong order of operations. The rule bodies are unchanged -- same three `evaluatePreconfiguredExpr` calls -- so the only variables are order and preview. Graduation path, measurement query and the reason to drop sqli last are in a comment at the rules. Relevant now rather than later: #152 raised the ceilings on the only Cloud Armor rule that actually executes, 100/min to 1200 for /api and 3000 for the app shell. That was the right call for the operator-facing 429, but it does mean less incidental friction for the scanners already probing this origin, and the WAF is the control that is supposed to cover that -- so it should at least be measured. Verified: terraform fmt, validate clean. Rule order is now 700/701/702 WAF (preview) -> 900 API throttle -> 1000 shell throttle -> default allow. The separate hangar-runner-armor policy is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The three preconfigured WAF rules in
hangar-armorhave never evaluated a single request.They sit at priority
2000–2002, below the rate-limit rules. Cloud Armor enforces the first matching rule and stops, and the throttle rules between them match every request. Confirmed in the load-balancer logs while fixing #152 — every request reportsenforcedSecurityPolicy priority 1000:That last line is the tell. A scanner walking
/cgi-bin,/docSQLand ~60 other paths collects a 429 from the rate limiter, never a 403 from the RFI rule. The rules were dead config, not defence.The change
Move them to
700–702, above the throttles. That is the entire mechanism — Cloud Armor evaluates ascending, so they have to be below the rate-limit rules numerically to run before them.Resulting order:
xss-v33-stable)sqli-v33-stable)rfi-v33-stable)/api/*— 1200/60sThe separate
hangar-runner-armorpolicy is untouched.Why preview, and why that makes this safe to merge
This commit is behaviour-neutral by construction. A preview rule logs its match and evaluation continues to the next rule, so the 429 ceilings still apply and nothing new is denied. (The proof that evaluation continues is that LB log entries carry
enforcedSecurityPolicyandpreviewSecurityPolicyas separate fields on the same request.)That is deliberate rather than timid. Preconfigured CRS rules are well known to false-positive on ordinary traffic, and because these have never been in this dashboard's request path, their real hit rate here is unknown rather than demonstrably zero. Enforcing three untested deny rules on an origin Mozilla staff use daily, in the same change that first makes them reachable, is the wrong order of operations — a false positive would surface as a 403 on a dashboard page with no obvious cause.
The rule bodies are unchanged — same three
evaluatePreconfiguredExprcalls — so the only variables here are order and preview.Measuring, then graduating
previewSecurityPolicyis only populated by preview rules, so this isolates exactly what they would have blocked:Run for at least a week of normal use. If every
DENYis a scanner and none is an operator, droppreview = trueone rule at a time — sqli last, since it is the most false-positive-prone. If a rule matches legitimate traffic, tune sensitivity or add a rule exclusion rather than deleting it.Why now
#152 raised the ceilings on the only Cloud Armor rule that actually executes — 100/min → 1200 for
/apiand 3000 for the app shell. That was the right call for the operator-facing 429, but it does mean less incidental friction for the scanners already probing this origin, and the WAF is the control that is supposed to cover that. It should at least be measured.Deploy note
Terraform-only; nothing deploys on merge. Needs a manual
terraform apply, and CI cannot validate this — the terraform job runsfmtandvalidate, neverplan. Expect0 to add, 1 to change, 0 to destroytouching onlygoogle_compute_security_policy.hangar.Post-apply, confirm the order and that the three WAF rules report
preview: Truewhile the throttles reportFalse:gcloud compute security-policies describe hangar-armor --project=relops-dashboard \ --format="table(rules[].priority,rules[].action,rules[].preview)"Verified:
terraform fmt,terraform validateclean.🤖 Generated with Claude Code