将NGINX Ingress Controller转化为WAF解决方案
这篇文章是NTTコムウェア Advent Calendar 2022第19天的文章。
https://qiita.com/advent-calendar/2022/nttcomware
你好。我是NTT Comware的東。
大家都在使用Kubernetes(k8s)吗?
在Kubernetes中,NGINX Ingress Controller被认为是Ingress控制器的事实标准。实际上,NGINX Ingress Controller内置了一个名为ModSecurity的开源Web应用防火墙(WAF)实现。默认情况下,ModSecurity处于非运行状态,但只需进行相关设置就可以启用它。因此,您可以免费将Ingress转化为WAF。这是一个不能错过的功能。
那么,在本文中,我将介绍如何在NGINX Ingress Controller中启用ModSecurity的WAF功能。
顺便提一下,本文针对已经掌握Kubernetes基本概念和操作技巧的用户。
请留意以下注意事项。
-
- 本内容については、不正アクセスを助長するものではございません。
-
- 本ページの内容については、悪用を禁止致します。
あくまで自身のサイトのテスト目的での利用に限ります。
悪用した場合、不正アクセス禁止法等で訴えられる可能性がございます。
关于OWASP ModSecurity核心规则集(CRS)。
虽然我想立即介绍操作步骤,但在此之前,让我们简单介绍一下“OWASP”和“CRS”。若你对这些名字有所了解,可以跳过本章节。
ModSecurity根据定义了许多应视为攻击的访问模式的规则集来判断访问是否为攻击。当前的ModSecurity仅是一个基于规则集进行判断的模块,规则集与ModSecurity是分开开发和维护的。
在这个规则集中,最强大的是由OWASP社区维护的名为”核心规则集”的开源软件。它有时被称为”OWASP ModSecurity CRS”或简称为”CRS”。
OWASP(Open Web Application Security Project) 是一个开源社区,致力于在软件和Web应用安全领域进行研究和指导性准则的制定,开发漏洞诊断工具,举办活动等多方面的活动。
刚刚我提到「NGINX Ingress Controller中已经集成了ModSecurity」,但实际上更准确的说法是它实际上集成了ModSecurity和CRS。
因此,从现在开始,我将展示在NGINX Ingress Controller中引入和启用ModSecurity + CRS的步骤。
环境条件
筆者所用的測試環境如下所示。無論如何,只要有運行NGINX Ingress Controller的k8s環境,不限於EKS,都可以。
Kubernetes v1.24.7
Amazon EKSを用い構築
NGINX Ingress Controller v1.5.1 (この後の手順で導入)
コンテナイメージ:registry.k8s.io/ingress-nginx/controller:v1.5.1
同梱モジュール:
ModSecurity v3.0.8
OWASP ModSecurity CRS v3.3.4
创建ModSecurity推荐配置文件
作为准备工作,我们将创建一个配置文件,其中包含了要构建的ModSecurity的各种推荐参数。
原因是,NGINX Ingress Controller的容器镜像已经安装了ModSecurity和CRS,并且配置文件已经被部署,但是请求正文的检测是无效的,有点不太好。
此外,NGINX Ingress Controller还可以使用modsecurity-snippet添加任意的ModSecurity配置,但是该方法有一个限制,即不能超过4096个字符,因此无法添加太多的设置和规则。
因此,在这里,我们将根据ModSecurity社区的建议创建配置文件,并在ConfigMap中附加。
首先,使用以下命令下载ModSecurity社区的推荐内容。
“v3/master”部分是ModSecurity GitHub的当前默认分支(2022/12)。请注意,此后可能会发生更改,请确保查看GitHub首页。
$ curl -L -O https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended
我把下载的ModSecurity社区建议稍作修改,用于NGINX Ingress Controller。修改后的文件另存为custom-modsecurity.conf。
$ cat modsecurity.conf-recommended | sed \
-e "s/^SecRuleEngine DetectionOnly/SecRuleEngine On/" \
-e "s/^SecAuditLog \/var\/log\/modsec_audit.log/SecAuditLog \/dev\/stdout/" \
-e "s/^SecUnicodeMapFile unicode.mapping 20127/SecUnicodeMapFile \/etc\/nginx\/modsecurity\/unicode.mapping 20127/" \
-e "s/^SecStatusEngine On/SecStatusEngine Off/" \
-e '$aSecAuditLogFormat JSON' \
-e '$aSecRuleRemoveById 920350' > custom-modsecurity.conf
以下是书写修改的要点。
-
- SecRuleEngine
デフォルトのDetectionOnlyでは検出しログ出力するのみでアクセスは通すため、”On”とし検知した攻撃アクセスを拒否させる。
SecAuditLog
ModSecurityのログをコンテナの標準出力に出力する。
SecUnicodeMapFile
unicode.mappingのパスをNGINX Ingress Controllerイメージに合わせ書き換える。
SecStatusEngine
NGINX起動時にステータスレポートをModSecurityプロジェクトチームに送信しない(Off)。
SecAuditLogFormat
ログ形式をJSONに指定。(追記)
SecRuleRemoveById
CRSの id:920350 というルールは、localhostからのアクセスを拒否するルールだが、k8sの場合、内部コンポーネントからのアクセスがlocalhostからあるためこのルールを無効化する行を追加。
使用修改后的文件(custom-modsecurity.conf)作为基础创建ConfigMap。然后将此ConfigMap附加为后续NGINX Ingress Controller(Pod)的配置文件。
请根据部署NGINX Ingress Controller的目标进行适当更改,将命名空间名”ingress-nginx”替换为合适的值。
$ kubectl create namespace ingress-nginx
$ kubectl -n ingress-nginx create configmap modsecurity-config \
--from-file=custom-modsecurity.conf=custom-modsecurity.conf
部署NGINX Ingress Controller
鉴于笔者使用的是AWS(EKS)环境,因此将按照此环境直接指定清单的方式进行部署。
首先,使用以下命令下载NGINX Ingress Controller的清单文件。
$ curl -L -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/aws/deploy.yaml
在deploy.yaml文件中,添加启用ModSecurity的配置和附加之前创建的ConfigMap“modsecurity-config”的配置。
$ vi deploy.yaml
→以下diffの内容を追記
以下是追加内容前后的变动。
--- deploy.yaml.org 2022-12-08 10:03:37.083369804 +0900
+++ deploy.yaml 2022-12-08 10:07:20.720523517 +0900
@@ -336,6 +336,10 @@
apiVersion: v1
data:
allow-snippet-annotations: "true"
+ enable-modsecurity: "true"
+ enable-owasp-modsecurity-crs: "true"
+ modsecurity-snippet: |
+ Include /etc/nginx/owasp-modsecurity-crs/custom/custom-modsecurity.conf
kind: ConfigMap
metadata:
labels:
@@ -509,6 +513,8 @@
- mountPath: /usr/local/certificates/
name: webhook-cert
readOnly: true
+ - mountPath: /etc/nginx/owasp-modsecurity-crs/custom/
+ name: modsecurity-config
dnsPolicy: ClusterFirst
nodeSelector:
kubernetes.io/os: linux
@@ -518,6 +524,9 @@
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
+ - name: modsecurity-config
+ configMap:
+ name: modsecurity-config
---
apiVersion: batch/v1
kind: Job
使用更改后的deploy.yaml文件进行NGINX Ingress Controller的部署。
$ kubectl apply -f deploy.yaml
确认行动
在已启动的 NGINX Ingress Controller 的 Pod 内部执行 “nginx -T” 命令,以确认 NGINX 是否已加载 ModSecurity 模块。
以下的代码将在PODNAME环境变量中记录NGINX Ingress Controller的Pod名称,并通过kubectl exec执行命令。可以确认存在几行包含”ModSecurity”这个词的行。
$ PODNAME=$( kubectl -n ingress-nginx get pod -l app.kubernetes.io/component=controller -o=jsonpath='{.items[0].metadata.name}' )
$ kubectl -n ingress-nginx exec -it $PODNAME -- nginx -T | grep -i modsecurity
2022/12/08 02:46:15 [notice] 579#579: ModSecurity-nginx v1.0.2 (rules loaded inline/local/remote: 7/782/0)
load_module /etc/nginx/modules/ngx_http_modsecurity_module.so;
modsecurity on;
modsecurity_rules '
Include /etc/nginx/owasp-modsecurity-crs/custom/custom-modsecurity.conf
modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf;
下一步,我会实际进行一些攻击模拟,并确保它们能够被有效地防御。我将根据这里提供的指南,引入一款作为Ingress后端的示范应用。
$ kubectl create deployment demo --image=httpd --port=80
$ kubectl expose deployment demo
$ kubectl create ingress demo-localhost --class=nginx \
--rule="demo.localdev.me/*=demo:80"
$ kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80
使用curl等工具,确认能够访问。
$ curl http://demo.localdev.me:8080/
<html><body><h1>It works!</h1></body></html>
我会用一种攻击的方式尝试访问这个示例应用程序。
$ curl -X POST -d "cmd=<script>" http://demo.localdev.me:8080/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
返回了“403 Forbidden”。
查看NGINX Ingress Controller的日志,可以看到ModSecurity的反应,似乎成功地阻止了访问。我们可以确认ModSecurity正常运行。
$ kubectl -n ingress-nginx logs $PODNAME | grep ModSecurity:
・・・
2022/12/08 03:01:34 [error] 844#844: *47316 [client 127.0.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 "81"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 15)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.4"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/"] [unique_id "167046849467.029474"] [ref ""], client: 127.0.0.1, server: demo.localdev.me, request: "POST / HTTP/1.1", host: "demo.localdev.me:8080"
此外,NGINX Ingress Controller的日志以JSON格式更详细地输出了ModSecurity的检测结果。
以下是通过使用grep和jq命令对以”^{“transaction”: “开头的行进行格式化的结果(由于长度较长,已折叠)。您可以获取到访问来源、响应内容以及被检测的规则和原因等详细信息。
$ kubectl -n ingress-nginx logs $PODNAME | grep ‘^{“transaction”:’ | jq
{
“transaction”: {
“client_ip”: “127.0.0.1”,
“time_stamp”: “2022年12月8日星期四 03:01:34”,
“server_id”: “d0fb58befb07ce0cec85f68d9197f05ab5150f14”,
“client_port”: 34376,
“host_ip”: “127.0.0.1”,
“host_port”: 80,
“unique_id”: “167046849467.029474”,
“request”: {
“method”: “POST”,
“http_version”: 1.1,
“uri”: “/”,
“headers”: {
“Host”: “demo.localdev.me:8080”,
“User-Agent”: “curl/7.61.1”,
“Accept”: “*/*”,
“Content-Length”: “12”,
“Content-Type”: “application/x-www-form-urlencoded”
}
},
“response”: {
“body”: “\r\n\r\n\r\n
403 Forbidden
\r\n\r\n”,
“http_code”: 403,
“headers”: {
“Server”: “”,
“Date”: “2022年12月8日星期四 03:01:34 GMT”,
“Content-Length”: “146”,
“Content-Type”: “text/html”,
“Connection”: “keep-alive”
}
},
“producer”: {
“modsecurity”: “ModSecurity v3.0.8 (Linux)”,
“connector”: “ModSecurity-nginx v1.0.2”,
“secrules_engine”: “Enabled”,
“components”: [
“OWASP_CRS/3.3.4\””
]
},
“messages”: [
{
“message”: “检测到XSS攻击(libinjection)”,
“details”: {
“match”: “检测到使用libinjection的XSS攻击。”,
“reference”: “v152,8t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls”,
“ruleId”: “941100”,
“file”: “/etc/nginx/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf”,
“lineNumber”: “38”,
“data”: “匹配到的数据: 在ARGS:cmd内发现XSS数据: