How to add new parameter and generate parser for new code
vzhirnov opened this issue · 4 comments
Hello, I have encountered issues in understanding how the parser generator works in the project.
Task:
- I need to add a new parameter called domain_id.
- My custom parameter domain_id is used in the ngx_modsecurity_module module and determines the value of the domain_id directive in the server context.
Example usage:
server {
server_name vts;
domain_id 1150; # <<<<<<=======
listen 80;
access_log off;
location /status { vhost_traffic_status_display; vhost_traffic_status_display_format html; }
location /stub-status {
stub_status on;
}
}
- I would like my code owasp-modsecurity/ModSecurity to be able to receive the domain_id parameter from ngx_modsecurity_module and use it in rules, as well as see the domain_id value in modsec json logs.
- I added code (briefly, just to show what exactly was done and at what stage I got stuck):
- m_variableDomainId(t, "DOMAIN_ID"),
- int msc_add_domain_id(Transaction *transaction, int domain_id);
- int Transaction::addDomainId(int domain_id) {
this->m_domainId = domain_id;
m_variableDomainId.set(std::to_string(this->m_domainId), m_variableOffset);
return true;
}
- domain_id.h file
- int msc_add_domain_id(Transaction *transaction, int domain_id);
- Call of the msc_add_domain_id function in the ngx_modsecurity_module module.
Problem:
I don’t understand how to generate the code in such a way that the appropriate code appears in the necessary files. It seems that bison, lex, and yacc are needed for this.
Searching for a solution:
I searched for similar PRs, and this one has something similar to what I need:
owasp-modsecurity/ModSecurity@fa6e418
But I don't fully understand:
- What code needs to be written before running the parser generator.
- At what stage to run the parser (which code should be written before running the parser).
- How exactly to run the parser correctly.
Could you please help me understand what the entire process of adding a new parameter looks like?
For example, my domain_id.
If needed, and if it is relevant, I will later write a part of the documentation that describes the process of adding a new parameter, based on our dialogue, and make a PR.
I figured it out. Sorry for bothering you.
no worries, feel free to ask here, we are happy to help. And sorry for the late reply.
Just to clarify: do you want to use this modification only for yourself, or you want to add it into repository?
An other remark: why do you want to change/modify the library's parser? It seems you want to add a specific directive to the Nginx, not to the library.
And finally, may be that's not a solution what you are looking for, but you should take a look at SecWebAppId. That works out of box.
hi @airween , thank you so much for your quick answer!
do you want to use this modification only for yourself , or you want to add it into repository?
I'd like to use this modification only by myself (to be clear, for our project). As far as I understand, there is no need to add DOMAIN_ID to github modsec project because the domain_id is the directive we only use in our nginx, with several modules, and ngx_http_modsecurity_module module as example. The main puprose is to identify vhost, and see it in modsec logs. Also it would be great to work with DOMAIN_ID as a variable (say, IF domain_id eq some_number THAN some_action).
So we need ngx_http_modsecurity_module to have the opportunity to work with domain_id although this directive is the ngx_http_core directive. We get it from http core module, and pass to msc_add_domain_id(Transaction *transaction, int domain_id) within ngx_http_modsecurity_module, after that we can see the corresponding domain_id in modsec logs, and can work with DOMAIN_ID as the variable.
why do you want to change/modify the library's parser
yes, we want to:
- use domain_id value in modsec logs
- use DOMAIN_ID as a variable for modsec rules.
may be that's not a solution what you are looking for, but you should take a look at SecWebAppId.
thanks, I didn't know about that. I will ask about our devops guys if we can use it (and also I have a hunch that we could use the setvar for our purposes.)
You can try the following three solutions.
I can retrieve it successfully in my local tests.
方案 1:使用 SecWebAppId 设置应用 ID
适用场景
- 适用于使用
SecWebAppId作为应用标识。 - 通过
WEBAPPID变量存储DOMAIN_ID,并在 ModSecurity 规则中记录。
配置步骤
1️⃣ Nginx 配置
server {
listen 80;
server_name app1.example.com;
modsecurity on;
modsecurity_rules 'SecWebAppId "1150"';
modsecurity_rules_file /usr/local/openresty/nginx/conf/modsecurity/modsecurity.conf;
location /app1 {
return 200;
}
}2️⃣ ModSecurity 规则配置 (test.conf)
SecRule WEBAPPID "!@streq ''" "id:10011,phase:1,log,msg:'domain_id: %{WEBAPPID}'"结果
WEBAPPID变量会存储1150,并写入 ModSecurity 日志。- 但
WEBAPPID变量不能直接在tx.变量空间中使用,适用场景受限。
ModSecurity: Warning. Matched "Operator `StrEq' with parameter `''' against variable `' (Value: `1150"' ) [file "/usr/local/openresty/nginx/conf/modsecurity/rules/test.conf"] [line "2"] [id "10011"] [rev ""] [msg "domain_id: 1150""] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "192.100.2.110"] [uri "/app1"] [unique_id "174103244874.402481"] [ref ""]
方案 2:使用 SecAction 设置 tx.domain_id
适用场景
- 适用于在 ModSecurity 规则中直接指定
DOMAIN_ID。 - 适用于 Nginx 不能传递
DOMAIN_ID,但希望在 ModSecurity 内部赋值。
配置步骤
1️⃣ Nginx 配置
server {
listen 80;
server_name app2.example.com;
modsecurity on;
modsecurity_rules_file /usr/local/openresty/nginx/conf/modsecurity/modsecurity.conf;
location /app2 {
return 200;
}
}2️⃣ ModSecurity 规则配置 (test.conf)
SecAction "id:10012,phase:1,nolog,pass,setvar:tx.domain_id=1150"
SecRule REQUEST_URI "@rx .*" "id:10012,phase:1,log,deny,msg:'Access to all requests',logdata:'domain_id: %{tx.domain_id}'"结果
tx.domain_id变量被强制设定为1150,并在规则匹配时记录。- 适用于静态
domain_id值,但 不适用于动态设置。
ModSecurity: Access denied with code 403 (phase 1). Matched "Operator `Rx' with parameter `.*' against variable `REQUEST_URI' (Value: `/healthz' ) [file "/usr/local/openresty/nginx/conf/modsecurity/rules/test.conf"] [line "2"] [id "10012"] [rev ""] [msg "Access to all requests"] [data "domain_id: 1150"] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "192.100.2.110"] [uri "/healthz"] [unique_id "174103304564.933566"] [ref "o0,8v4,8"]
方案 3:使用 OpenResty 环境变量 + Nginx 传递请求头
适用场景
- 适用于动态
DOMAIN_ID,从 OpenResty 读取环境变量并传递到 ModSecurity。 - 适用于 Kubernetes / Docker 等环境,
DOMAIN_ID可能是 环境变量。
配置步骤
1️⃣ Nginx 配置
env DOMAIN_ID;
server {
listen 80;
server_name app3.example.com;
set_by_lua $domain_id 'return os.getenv("DOMAIN_ID")';
modsecurity on;
modsecurity_rules_file /usr/local/openresty/nginx/conf/modsecurity/modsecurity.conf;
location /app3 {
proxy_set_header X-Domain-ID $domain_id;
return 200;
}
}2️⃣ ModSecurity 规则配置 (test.conf)
方法 1:使用 SecAction 读取 X-Domain-ID
SecAction "id:10020,phase:1,nolog,pass,setvar:tx.domain_id=%{REQUEST_HEADERS:X-Domain-ID}"
SecRule TX:domain_id "!@streq ''" "id:10021,phase:1,log,msg:'domain_id: %{TX.domain_id}'"方法 2:直接在 SecRule 中获取 X-Domain-ID
SecRule REQUEST_HEADERS:X-Domain-ID "@rx .*" "id:10022,phase:1,log,pass,setvar:tx.domain_id=%{MATCHED_VAR},msg:'Captured domain_id: %{TX.domain_id}'"结果
DOMAIN_ID从环境变量获取,并传递给 Nginx 变量$domain_id。- Nginx 通过
proxy_set_header将X-Domain-ID传给 ModSecurity。 - ModSecurity 通过
SecAction或SecRule读取X-Domain-ID并存入tx.domain_id。 - 适用于 多租户环境,可动态修改
DOMAIN_ID,适用性最强。
对比总结
| 方案 | 适用场景 | 变量来源 | 适配性 | 适用环境 |
|---|---|---|---|---|
| 方案 1 | SecWebAppId 方式 |
固定 WEBAPPID |
受限 | 单应用 |
| 方案 2 | SecAction 直接赋值 |
静态 domain_id |
中等 | 静态规则 |
| 方案 3 | OpenResty 变量传递 | 环境变量 DOMAIN_ID |
最佳 | 动态环境 (K8s, Docker) |
推荐
✅ 如果 DOMAIN_ID 固定,使用 SecAction 方案(方案 2)。
✅ 如果 DOMAIN_ID 需要动态修改,使用 OpenResty 环境变量方案(方案 3)。
✅ 如果已经使用 SecWebAppId,可以尝试方案 1,但不适用于动态场景。