kubernetesnginx-ingressmod-security

SecRule REQUEST_HEADERS:Content-Type to parse XML & JSON for Ingress k8s


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.


Solution

  • 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