ModSecurity Web Application Firewall in kubernetes Nginx Ingress controller
ModSecurity Web Application Firewall in Kubernetes Nginx Ingress controller
The Nginx Ingress controller does support the WAF using the ModSecurity by default. We just have to enable it.
I faced some issues with Nginx 0.26.1 so better to use latest 0.27.1 or latest. (I have tested the setup using 0.32.0)
Deploy the nginx ingress
1
helm3 install ingress virtual-helm-chart/nginx-ingress -f nginx.values -n datar-james-reanplatform
below is the values file I have used
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
COMPUTED VALUES:
controller:
addHeaders: {}
admissionWebhooks:
enabled: false
failurePolicy: Fail
patch:
enabled: true
image:
pullPolicy: IfNotPresent
repository: jettech/kube-webhook-certgen
tag: v1.0.0
nodeSelector: {}
podAnnotations: {}
priorityClassName: ""
resources: {}
port: 8443
service:
annotations: {}
clusterIP: ""
externalIPs: []
loadBalancerIP: ""
loadBalancerSourceRanges: []
omitClusterIP: false
servicePort: 443
type: ClusterIP
affinity: {}
autoscaling:
enabled: false
maxReplicas: 11
minReplicas: 1
targetCPUUtilizationPercentage: 50
targetMemoryUtilizationPercentage: 50
config:
enable-modsecurity: "true"
enable-owasp-modsecurity-crs: "true"
modsecurity-snippet: |
SecRuleEngine On
SecRequestBodyAccess On
SecAuditEngine RelevantOnly
SecAuditLogParts ABIJDEFHZ
SecAuditLog /var/log/modsec_audit.log
enable-vts-status: "true"
hsts-include-subdomains: "false"
server-name-hash-bucket-size: "256"
server-tokens: "false"
ssl-ciphers: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4
ssl-protocols: TLSv1.1 TLSv1.2
use-http2: "true"
configMapNamespace: ""
containerPort:
http: 80
https: 443
customTemplate:
configMapKey: ""
configMapName: ""
daemonset:
hostPorts:
http: 80
https: 443
useHostPort: false
defaultBackendService: ""
deploymentAnnotations: {}
deploymentLabels: {}
dnsConfig: {}
dnsPolicy: ClusterFirst
electionID: ingress-controller-leader
extraContainers: []
extraEnvs: []
extraInitContainers: []
extraVolumeMounts: []
extraVolumes: []
force-namespace-isolation: ""
hostNetwork: false
image:
allowPrivilegeEscalation: true
pullPolicy: IfNotPresent
repository: virtual-docker-reanplatform.artifactory.reancloud.com/kubernetes-ingress-controller/nginx-ingress-controller
runAsUser: 101
tag : 0.32.0
# tag: 0.27.1
ingressClass: nginx
kind: Deployment
lifecycle: {}
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
port: 10254
successThreshold: 1
timeoutSeconds: 1
maxmindLicenseKey: ""
metrics:
enabled: false
port: 10254
prometheusRule:
additionalLabels: {}
enabled: false
namespace: ""
rules: []
service:
annotations: {}
clusterIP: ""
externalIPs: []
loadBalancerIP: ""
loadBalancerSourceRanges: []
omitClusterIP: false
servicePort: 9913
type: ClusterIP
serviceMonitor:
additionalLabels: {}
enabled: false
namespace: ""
namespaceSelector: {}
scrapeInterval: 30s
minAvailable: 1
minReadySeconds: 0
name: controller
nodeSelector:
node-role.kubernetes.io/master: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
priorityClassName: ""
proxySetHeaders: {}
publishService:
enabled: false
pathOverride: ""
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
port: 10254
successThreshold: 1
timeoutSeconds: 1
replicaCount: 1
reportNodeInternalIp: false
resources:
requests:
cpu: 100m
memory: 100Mi
scope:
enabled: false
namespace: datar-james-reanplatform
service:
annotations: {}
clusterIP: ""
enableHttp: true
enableHttps: true
enabled: true
externalIPs: []
externalTrafficPolicy: Local
healthCheckNodePort: 0
internal:
annotations: {}
enabled: false
labels: {}
loadBalancerIP: ""
loadBalancerSourceRanges: []
nodePorts:
http: 30080
https: 30443
# tcp:
# "22": 30022
udp: {}
omitClusterIP: false
ports:
http: 80
https: 443
sessionAffinity: ""
targetPorts:
http: http
https: https
type: NodePort
tcp:
configMapNamespace: ""
terminationGracePeriodSeconds: 60
tolerations: []
udp:
configMapNamespace: ""
updateStrategy: {}
useComponentLabel: false
defaultBackend:
affinity: {}
apiVersion: apps/v1
deploymentLabels: {}
enabled: true
extraArgs: {}
extraEnvs: []
image:
pullPolicy: IfNotPresent
repository: virtual-docker-reanplatform.artifactory.reancloud.com/defaultbackend-amd64
runAsUser: 65534
tag: 1.5
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
minAvailable: 1
name: default-backend
nodeSelector:
node-role.kubernetes.io/master: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
port: 8080
priorityClassName: ""
readinessProbe:
failureThreshold: 6
initialDelaySeconds: 0
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
replicaCount: 1
resources: {}
service:
annotations: {}
clusterIP: ""
externalIPs: []
loadBalancerIP: ""
loadBalancerSourceRanges: []
omitClusterIP: false
servicePort: 80
type: ClusterIP
serviceAccount:
create: true
tolerations: []
useComponentLabel: false
imagePullSecrets:
- name: platform-docker-secret
podSecurityPolicy:
enabled: true
rbac:
create: true
scope: false
releaseLabelOverride: ""
revisionHistoryLimit: 10
serviceAccount:
annotations: {}
create: true
# tcp:
# "22": datar-james-reanplatform/gitlab-gitlab-shell:22
udp: {}
Deploy the test service with ingress
Below is the sample file I used to deploy the application having ingress.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: http-svc
spec:
replicas: 1
selector:
matchLabels:
app: http-svc
template:
metadata:
labels:
app: http-svc
spec:
containers:
- name: http-svc
image: gcr.io/kubernetes-e2e-test-images/echoserver:2.1
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
name: http-svc
labels:
app: http-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: http-svc
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: http-svc
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/modsecurity-snippet: |
SecRuleEngine On
spec:
rules:
- host: james.k8s.ijuned.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 8080
path: /
Test using the curl
Positive i.e. working curl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
curl "https://james.k8s.ijuned.com/"
Hostname: http-svc-f4d976d76-c5jzt
Pod Information:
node name: ip-10-64-12-208.ec2.internal
pod name: http-svc-f4d976d76-c5jzt
pod namespace: datar-james-reanplatform
pod IP: 10.244.0.166
Server values:
server_version=nginx: 1.12.2 - lua: 10010
Request Information:
client_address=10.244.0.1
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://james.k8s.ijuned.com:8080/
Request Headers:
accept=*/*
host=james.k8s.ijuned.com
user-agent=curl/7.64.1
x-forwarded-for=10.244.0.1
x-forwarded-host=james.k8s.ijuned.com
x-forwarded-port=443
x-forwarded-proto=https
x-original-forwarded-for=106.193.84.45
x-real-ip=10.244.0.1
x-request-id=f058122d88b859e82f7458783d226852
x-scheme=https
Request Body:
-no body in request-
Blocking i.e. non working curl
1
2
3
4
5
6
7
8
$ curl "https://james.k8s.ijuned.com/?test=%271%20OR%201=1&x=%3Cscript%3Ealert%28%27hello%27%29;%3C/script%3E"
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
Logs from the ingress-controller pod
1
2
2020/07/10 09:29:13 [error] 3849#3849: *47585 [client 10.244.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `15' ) [file "/etc/nginx/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "79"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 15)"] [data ""] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "10.244.0.163"] [uri "/"] [unique_id "159437335364.564617"] [ref ""], client: 10.244.0.1, server: james.k8s.ijuned.com, request: "GET /?test=%271%20OR%201=1&x=%3Cscript%3Ealert%28%27hello%27%29;%3C/script%3E HTTP/1.1", host: "james.k8s.ijuned.com"
10.244.0.1 - - [10/Jul/2020:09:29:13 +0000] "GET /?test=%271%20OR%201=1&x=%3Cscript%3Ealert%28%27hello%27%29;%3C/script%3E HTTP/1.1" 403 146 "-" "curl/7.64.1" 261 0.000 [datar-james-reanplatform-http-svc-8080] [] - - - - 1b21c1f39f81c608a4b74bf587042001
References