Used this article to set up ModSecurity. But getting an error from nginx-controller when using XML & JSON SecRules.
When setting both enable-owasp-modsecurity-crs: true
and enable-modsecurity: true
, the former overrides the recommended set of ModSecurity settings. This can be surprising as it means you won’t have request body inspection enabled nor parsing of XML/JSON requests, which is why I explicitly added those two SecRules. When adding these two lines to my ingress,
# Enable XML and JSON parsing
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\\+|/)|text/)xml" "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
The nginx-controller pods getting an error:
one or more objects failed to apply, reason:
admission webhook "validate.nginx.ingress.kubernetes.io" denied the request:
-------------------------------------------------------------------------------
Error: exit status 1 2024/03/04 17:45:48 [emerg] 3741
#3741: unexpected "2" in /tmp/nginx/nginx-cfg4276377893:
132 nginx: [emerg] unexpected "2" in /tmp/nginx/nginx-cfg4276377893:
132 nginx: configuration file /tmp/nginx/nginx-cfg4276377893 test failed
-------------------------------------------------------------------------------
I0304 17:43:00.098748 7 controller.go:190] "Configuration changes detected, backend reload required"
E0304 17:43:00.165306 7 controller.go:205] Unexpected failure reloading the backend:
-------------------------------------------------------------------------------
Error: exit status 1
2024/03/04 17:43:00 [emerg] 3651#3651: unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: [emerg] unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: configuration file /tmp/nginx/nginx-cfg2331967383 test failed
-------------------------------------------------------------------------------
E0304 17:43:00.165351 7 queue.go:131] "requeuing" err=<
-------------------------------------------------------------------------------
Error: exit status 1
2024/03/04 17:43:00 [emerg] 3651#3651: unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: [emerg] unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: configuration file /tmp/nginx/nginx-cfg2331967383 test failed
-------------------------------------------------------------------------------
> key="prod-witc-infra/oauth2-proxy-native-redis-replicas-zdl5l"
I0304 17:43:00.165387 7 event.go:298] Event(v1.ObjectReference{Kind:"Pod", Namespace:"kube-system", Name:"rke2-ingress-nginx-controller-qjmwr", UID:"758bc746-af41-4099-9248-d5bdb627d623", APIVersion:"v1", ResourceVersion:"203713628", FieldPath:""}): type: 'Warning' reason: 'RELOAD' Error reloading NGINX:
-------------------------------------------------------------------------------
Error: exit status 1
2024/03/04 17:43:00 [emerg] 3651#3651: unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: [emerg] unexpected "2" in /tmp/nginx/nginx-cfg2331967383:132
nginx: configuration file /tmp/nginx/nginx-cfg2331967383 test failed
-------------------------------------------------------------------------------
Full ingress manifest:
apiVersion: v1
data:
#https://systemweakness.com/nginx-ingress-waf-with-modsecurity-from-zero-to-hero-fa284cb6f54a
allow-snippet-annotations: "false"
# Enables ModSecurity functionality
enable-modsecurity: "true"
# Enables loading the core rule set (optional, can be enabled on specific ingresses only instead)
enable-owasp-modsecurity-crs: "true"
# Update ModSecurity config and rules
modsecurity-snippet: |
# Enable prevention mode. Can be any of: DetectionOnly,On,Off
# (default is DetectionOnly)
SecRuleEngine On
# Enable scanning of the request body
SecRequestBodyAccess On
# Enable XML and JSON parsing
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\\+|/)|text/)xml" "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
# Max request sizes in bytes (with/without files)
# Note NGINX Ingress has its own annotations, keep in sync!
SecRequestBodyLimit 20971520 # 20Mb (default is 12.5Mb)
SecRequestBodyNoFilesLimit 262144 # 250Kb (default is 128Kb)
SecRequestBodyLimitAction Reject # Reject if larger (we could also let it pass with ProcessPartial)
# Update config to include PUT/PATCH/DELETE in the allowed HTTP methods (instead of fully disabling 911100)
SecAction "id:900200,phase:1,nolog,pass,t:none,setvar:tx.allowed_methods=GET HEAD POST OPTIONS PUT PATCH DELETE"
# Send ModSecurity audit logs to the stdout (only for rejected requests)
SecAuditLog /dev/stdout
SecAuditLogFormat JSON
SecAuditEngine RelevantOnly # could be On/Off/RelevantOnly
kind: ConfigMap
metadata:
annotations:
meta.helm.sh/release-name: rke2-ingress-nginx
meta.helm.sh/release-namespace: kube-system
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: rke2-ingress-nginx
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: rke2-ingress-nginx
app.kubernetes.io/part-of: rke2-ingress-nginx
name: rke2-ingress-nginx-controller
namespace: kube-system
Upon investigating the nginx config files:
www-data@rke2-ingress-nginx-controller-qjmwr:/etc/nginx> nginx -c /tmp/nginx/nginx-cfg4276377893
2024/03/04 19:05:48 [emerg] 4242#4242: unexpected "2" in /tmp/nginx/nginx-cfg4276377893:132
nginx: [emerg] unexpected "2" in /tmp/nginx/nginx-cfg4276377893:132
kubectl cp kube-system/rke2-ingress-nginx-controller-qjmwr:/tmp/nginx/nginx-cfg4276377893 nginx-cfg4276377893
The 132 & 133 lines of the nginx config are:
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\\+|/)|text/)xml" "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
When I remove those two lines, it's all good. What seems to be a problem with XML and JSON parsing?
Current workaround: Remove the XML and JSON parsing.
Fixed versions of XML & JSON SecRules formating:
# Enable XML and JSON parsing
SecRule REQUEST_HEADERS:Content-Type "(?:text|application(?:/soap\+|/)|application/xml)/" \
"id:200000,phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"id:200001,phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
Compleate fixed & working ingress manifest:
apiVersion: v1
data:
#https://systemweakness.com/nginx-ingress-waf-with-modsecurity-from-zero-to-hero-fa284cb6f54a
allow-snippet-annotations: "false"
# Enables ModSecurity functionality
enable-modsecurity: "true"
# Enables loading the core rule set (optional, can be enabled on specific ingresses only instead)
enable-owasp-modsecurity-crs: "true"
# Update ModSecurity config and rules
modsecurity-snippet: |
# Enable prevention mode. Can be any of: DetectionOnly,On,Off
# (default is DetectionOnly)
SecRuleEngine On
# Enable scanning of the request body
SecRequestBodyAccess On
#Usefull for testing modsecurity is functioning: block any request containing the word attack in the URI or in the querystring, for example in:
#https://secrule.sample.com/api?task=attack
SecRule REQUEST_URI|ARGS|QUERY_STRING \"@contains attack\" \"id:100001,phase:1,t:lowercase,deny,status:403,msg:\'Attack Detected\'\"
# Enable XML and JSON parsing
SecRule REQUEST_HEADERS:Content-Type "(?:text|application(?:/soap\+|/)|application/xml)/" \
"id:200000,phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"id:200001,phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
# Max request sizes in bytes (with/without files)
# Note NGINX Ingress has its own annotations, keep in sync!
SecRequestBodyLimit 20971520 # 20Mb (default is 12.5Mb)
SecRequestBodyNoFilesLimit 262144 # 250Kb (default is 128Kb)
SecRequestBodyLimitAction Reject # Reject if larger (we could also let it pass with ProcessPartial)
# Update config to include PUT/PATCH/DELETE in the allowed HTTP methods (instead of fully disabling 911100)
SecAction "id:900200,phase:1,nolog,pass,t:none,setvar:tx.allowed_methods=GET HEAD POST OPTIONS PUT PATCH DELETE"
# Send ModSecurity audit logs to the stdout (only for rejected requests)
#SecAuditLog /dev/stdout
SecAuditLogFormat JSON
# could be On/Off/RelevantOnly
SecAuditEngine RelevantOnly
kind: ConfigMap
metadata:
annotations:
meta.helm.sh/release-name: rke2-ingress-nginx
meta.helm.sh/release-namespace: kube-system
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: rke2-ingress-nginx
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: rke2-ingress-nginx
app.kubernetes.io/part-of: rke2-ingress-nginx
name: rke2-ingress-nginx-controller
namespace: kube-system