
OpenResty 스택 기반의 고성능 WAF
lua-resty-waf - OpenResty 스택 기반의 고성능 WAF
참고: lua-resty-waf는 본질적으로 중단된 프로젝트입니다. 이 프로젝트는 ModSecurity for Nginx가 실행 가능한 선택지가 아니던 시절에 유용했지만, 이제는 더 이상 그렇지 않습니다. 2020년에 프로젝트를 되살리려는 시도가 있었지만, 이를 완료할 자원이 없었습니다. 해당 작업은 redux 브랜치에 부분적으로 완료되어 있습니다.
lua-resty-waf는 OpenResty 스택을 사용하여 구축된 리버스 프록시 WAF입니다. Nginx Lua API를 사용하여 HTTP 요청 정보를 분석하고 유연한 규칙 구조에 따라 처리합니다. lua-resty-waf는 ModSecurity CRS를 모방한 규칙 세트, 초기 개발 및 테스트 중에 만들어진 몇 가지 사용자 정의 규칙, 그리고 새로운 위협에 대한 소규모 가상 패치셋과 함께 배포됩니다. 또한 lua-resty-waf는 기존 ModSecurity 규칙을 자동으로 변환하는 도구와 함께 배포되므로, 사용자가 새로운 규칙 구문을 배울 필요 없이 lua-resty-waf 구현을 확장할 수 있습니다.
lua-resty-waf는 Robert Paprocki가 Western Governor's University에서 석사 학위 논문을 위해 처음 개발했습니다.
lua-resty-waf는 여러 타사 resty lua 모듈을 필요로 하지만, 이러한 모듈은 모두 lua-resty-waf에 패키징되어 있어 별도로 설치할 필요가 없습니다. OpenResty 소프트웨어 번들이 실행되는 시스템에 lua-resty-waf를 설치하는 것이 좋습니다. lua-resty-waf는 별도의 Nginx 소스와 Nginx Lua 모듈 패키지를 사용하여 구축된 플랫폼에서는 테스트되지 않았습니다.
최적의 정규식 컴파일 성능을 위해 JIT 컴파일을 지원하는 PCRE 버전으로 Nginx/OpenResty를 빌드하는 것이 좋습니다. OS에서 이를 제공하지 않는 경우, JIT 지원 PCRE를 Nginx/OpenResty 빌드에 직접 포함할 수 있습니다. 이렇게 하려면 --with-pcre 구성 플래그에서 PCRE 소스 경로를 참조하십시오. 예를 들어:```sh
PCRE 소스는 [PCRE 웹사이트](http://www.pcre.org/)에서 다운로드할 수 있습니다. JIT가 활성화된 PCRE 라이브러리로 OpenResty를 빌드하는 단계별 안내는 이 [블로그 게시물](https://www.cryptobells.com/building-openresty-with-pcre-jit/)도 참조하세요.
## Performance
lua-resty-waf는 효율성과 확장성을 염두에 두고 설계되었습니다. Nginx의 비동기 처리 모델과 효율적인 설계를 활용하여 각 트랜잭션을 가능한 한 빠르게 처리합니다. 부하 테스트 결과, ModSecurity CRS의 논리를 모방하도록 설계된 모든 제공 규칙 세트를 구현한 배포 환경은 요청당 약 300~500마이크로초 만에 트랜잭션을 처리하는 것으로 나타났습니다. 이는 [Cloudflare의 WAF](https://www.cloudflare.com/waf)가 광고하는 성능과 동일합니다. 테스트는 합리적인 하드웨어 스택(E3-1230 CPU, 32GB RAM, RAID 0 구성의 840 EVO 2개)에서 실행되었으며, 초당 약 15,000개의 요청을 처리했습니다. 자세한 내용은 [이 블로그 게시물](http://www.cryptobells.com/freewaf-a-high-performance-scalable-open-web-firewall)을 참조하세요.
lua-resty-waf의 작업 부하는 거의 전적으로 CPU 바운드입니다. Lua VM의 메모리 사용량(`lua-shared-dict`이 지원하는 영구 저장소 제외)은 약 2MB입니다.
## 설치
간단한 Makefile이 제공됩니다:```
# make && sudo make install
또는 Luarocks를 통해 설치하십시오:```
lua-resty-waf는 최신 OpenResty 배포판에서 사용할 수 있는 [OPM](https://github.com/openresty/opm) 패키지 관리자를 사용합니다. OPM 클라이언트 도구를 사용하려면 시스템의 `PATH` 환경 변수에 `resty` 명령줄 도구가 있어야 합니다.
기본적으로 lua-resty-waf는 애플리케이션에 즉시 영향을 주지 않도록 SIMULATE 모드로 실행됩니다. 규칙 동작을 활성화하려면 운영 모드를 명시적으로 ACTIVE로 설정해야 합니다.
## Synopsis```lua
http {
init_by_lua_block {
-- use resty.core for performance improvement, see the status note above
require "resty.core"
-- require the base module
local lua_resty_waf = require "resty.waf"
-- perform some preloading and optimization
lua_resty_waf.init()
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- define options that will be inherited across all scopes
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
-- this may be desirable for low-traffic or testing sites
-- by default, event logs are not written until the buffer is full
-- for testing, flush the log buffer every 5 seconds
--
-- this is only necessary when configuring a remote TCP/UDP
-- socket server for event logs. otherwise, this is ignored
waf:set_option("event_log_periodic_flush", 5)
-- run the firewall
waf:exec()
}
header_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
-- note that options set in previous handlers (in the same scope)
-- do not need to be set again
local waf = lua_resty_waf:new()
waf:exec()
}
body_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
log_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
}
}
}
디스크에서 ModSecurity SecRules 파일을 변환하고 초기화합니다. 이 경우에도 규칙 세트는 add_ruleset을 통해 추가되어야 합니다 (파일의 basename이 키로 제공되어야 합니다).
예시:```lua http { init_by_lua_block { local lua_resty_waf = require "resty.waf"
-- this translates and calculates a ruleset called 'ruleset_name'
local ok, errs = pcall(function()
lua_resty_waf.load_secrules("/path/to/secrules/ruleset_name")
end)
-- errs is an array-like table
if errs then
for i = 1, #errs do
ngx.log(ngx.ERR, errs[i])
end
end
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- in order to use the loaded ruleset, it must be added via
-- the 'add_ruleset' option
waf:set_option("add_ruleset", "ruleset_name")
}
}
}
}
추가로, `load_secrules`는 다양한 변환 함수에 전달할 옵션 테이블을 선택적 두 번째 인수로 받을 수 있습니다. 다음 옵션이 인식됩니다:
* *path*: @pmFromFile과 같은 연산자를 위한 데이터 파일을 검색할 파일시스템 경로를 정의합니다. 해당 키가 정의되지 않으면 현재 작업 디렉터리(`.`)가 사용됩니다
* *force*: 규칙 변수 변환에 실패해도 오류를 내거나 중단하지 않습니다
* *loose*: 규칙 액션 변환에 실패해도 오류를 내거나 중단하지 않습니다
* *quiet*: 규칙 액션 변환에 실패해도 오류나 경고를 표시하지 않습니다
이 함수는 나중에 처리하기 위해 변환 오류를 수집하는 테이블을 세 번째 옵션으로 받을 수도 있습니다. 이 옵션이 없거나 테이블이 아닌 경우, 변환 오류는 대신 오류 로그에 기록됩니다.
### lua-resty-waf.init()
기본 배포 규칙셋을 통해 제공된 내용을 기반으로 규칙과 규칙셋에 대한 사전 계산을 수행합니다. 이 함수를 호출하는 것이 권장되지만 필수는 아닙니다(호출하지 않으면 약간의 성능 저하가 발생합니다). 이 함수는 이 범위 밖에서 호출해서는 안 됩니다.
*예시*:```lua
http {
init_by_lua_block {
local lua_resty_waf = require "resty.waf"
lua_resty_waf.init()
}
}
lua-resty-waf의 새 인스턴스를 생성합니다. lua-resty-waf를 실행하려는 모든 요청 핸들러 단계에서 이 메서드를 호출해야 하며, 반환된 결과를 사용해 추가 객체 메서드를 호출해야 합니다.
예제:```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
}
}
### lua-resty-waf:set_option()
옵션을 범위별로 구성합니다.
*예제*:```lua
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- enable debug logging only for this scope
waf:set_option("debug", true)
}
}
WAF를 실행하기 전에 트랜잭션 변수(TX 변수 컬렉션에 저장됨)를 정의합니다. 이는 OWASP CRS와 같은 복잡한 규칙셋에서 사용되는 변수를 정의하는 데 사용할 수 있습니다.
예시:```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_var("FOO", "bar")
}
}
다른 ModSecurity 규칙과 마찬가지로 변수의 존재는 WAF 처리에 기능적 변화를 가져오지 않는다는 점에 유의하세요. `TX` 변수를 이해하고 사용하는 것은 규칙 작성자의 책임입니다.
### lua-resty-waf:sieve_rule()
특정 규칙에 대한 컬렉션 제외를 정의합니다.
*예*:```lua
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
local sieves = {
{
type = "ARGS",
elts = "foo",
action = "ignore",
}
}
waf:sieve_rule("12345", sieves)
}
}
See the rule sieves wiki page for details and advanced usage examples.
Rules engine을 실행합니다. 기본적으로 엔진은 현재 실행 중인 단계(phase)에 따라 실행됩니다. 선택적 테이블을 전달하여 사용자가 다른 단계의 실행을 "모의(mock)"할 수 있습니다.
예시:```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- execute according to access phase collections and rules
waf:exec()
}
content_by_lua_block {
local lua_resty_waf = require "waf"
local waf = lua_resty_waf:new()
-- execute header_filter rules, passing in a table of additional collections
-- this assumes the 'request_headers' and 'status' Lua variables were
-- declared and initialized elsewhere
local opts = {
phase = 'header_filter',
collections = {
REQUEST_HEADERS = request_headers,
STATUS = status,
}
}
waf:exec(opts)
}
}
### lua-resty-waf:write_log_events()
트랜잭션에서 생성된 모든 감사 로그 항목을 작성합니다. 이 호출은 `exec`가 `log_by_lua` 핸들러에서 호출될 때에만 선택사항입니다.
*예시*:```lua
location / {
log_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- write out any event log entries to the
-- configured target, if applicable
waf:write_log_events()
}
}
기본값: 없음
처리 중에 사용할 추가 규칙 집합을 추가합니다. 이를 통해 사용자는 포함된 rules 디렉터리를 덮어쓰지 않고도 사용자 지정 규칙 집합을 구현할 수 있습니다. 추가 규칙 집합은 lua_package_path 내에 있는 "rules"라는 폴더 안에 있어야 합니다.
예시:```lua http { -- the rule file 50000.json must live at -- /path/to/extra/rulesets/rules/50000.json lua_package_path '/path/to/extra/rulesets/?.lua;;';
server {
location / {
access_by_lua_block {
waf:set_option("add_ruleset", "50000_extra_rules")
}
}
}
}
### add_ruleset_string
*기본값*: 없음
`set_option`에 값 테이블을 전달하여 여러 규칙 집합을 추가할 수 있습니다. 참고로 규칙 집합 이름은 처리 전에 정렬됩니다. 규칙 집합은 낮은 순서에서 높은 순서로 정렬된 순서대로 처리됩니다.
처리 중에 사용할 추가 규칙 집합을 추가합니다. 이를 통해 사용자는 포함된 rules 디렉터리를 덮어쓰지 않고 사용자 지정 규칙 집합을 구현할 수 있습니다. 규칙 집합은 변환된 규칙 집합 JSON 구조 형태의 Lua 문자열로 인라인 정의됩니다.
*예제*:```lua
location / {
access_by_lua_block {
waf:set_option("add_ruleset_string", "70000_extra_rules", [=[{"access":[{"action":"DENY","id":73,"operator":"REGEX","opts":{},"pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
}
}
Note that ruleset names are sorted before processing, and must be given as strings. Rulesets are processed in a low-to-high sorted order.
기본값: false
lua-resty-waf가 allowed_content_types 테이블에 없는 Content-Type 헤더가 전송된 요청을 계속 처리하도록 지시합니다. 이러한 요청은 요청 본문이 lua-resty-waf에 의해 처리되지 않습니다 (REQUEST_BODY 컬렉션은 nil이 됩니다). 이 방식으로 사용자는 발생할 수 있는 모든 Content-Type 헤더를 명시적으로 허용 목록에 추가할 필요가 없습니다.
예제:```lua location / { access_by_lua_block { waf:set_option("allow_unknown_content_types", true) } }
### allowed_content_types
*기본값*: 없음
기본 Content-Type인 `application/x-www-form-urlencoded` 및 `multipart/form-data` 외에 허용할 하나 이상의 Content-Type 헤더를 정의합니다. 콘텐츠 유형이 `allowed_content_types` 중 하나와 일치하는 요청은 `REQUEST_BODY` 컬렉션을 (테이블이 아닌) 단일 문자열로 설정합니다. 콘텐츠 유형이 이러한 값 중 하나와 일치하지 않거나 `application/x-www-form-urlencoded` 또는 `multipart/form-data`와 일치하지 않는 요청은 거부됩니다.
*예제*:```lua
location / {
access_by_lua_block {
-- define a single allowed Content-Type value
waf:set_option("allowed_content_types", "text/xml")
-- defines multiple allowed Content-Type values
waf:set_option("allowed_content_types", { "text/html", "text/json", "application/json" })
}
}
set_option 호출을 allowed_content_types 매개변수와 함께 여러 번 사용하면 기존 옵션 테이블을 그냥 덮어쓰게 된다는 점에 유의하세요. 따라서 여러 허용 콘텐츠 유형을 정의하려면 위와 같이 Lua 테이블로 정의해야 합니다.
기본값: false
디버그 로깅을 비활성화/활성화합니다. 디버그 로그 문은 error_log에 출력됩니다. 디버그 로깅은 비용이 매우 많이 들므로 프로덕션 환경에서 사용해서는 안 됩니다.
예제:```lua location / { access_by_lua_block { waf:set_option("debug", true) } }
### debug_log_level
*기본값*: ngx.INFO
디버그 로깅에 사용되는 nginx 로그 레벨 상수를 설정합니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("debug_log_level", ngx.DEBUG)
}
}
기본값: ngx.HTTP_FORBIDDEN
요청을 거부할 때 사용할 상태 코드를 설정합니다.
예시:```lua location / { access_by_lua_block { waf:set_option("deny_status", ngx.HTTP_NOT_FOUND) } }
### disable_pcre_optimization
*기본값*: false
모든 `ngx.re.match`, `ngx.re.find`, `ngx.re.sub` 호출에서 `oj` 플래그를 제거합니다. 이는 이전 PCRE 라이브러리를 사용하는 일부 경우에 유용할 수 있지만, 심각한 성능 저하를 초래하므로 사용을 권장하지 않습니다. 대신 사용자는 최신 JIT 지원 PCRE 라이브러리로 OpenResty를 빌드하는 것이 좋습니다.
*예제*:```lua
location / {
access_by_lua_block {
waf:set_option("disable_pcre_optimization", true)
}
}
참고: 이 동작은 더 이상 사용되지 않으며 향후 버전에서 제거될 예정입니다.
기본값: true
lua-resty-waf에 의해 변경되지 않은 트랜잭션에서 규칙 일치에 대한 로그 항목을 작성할지 여부를 결정합니다. "변경됨(Altered)"은 lua-resty-waf가 ACCEPT 또는 DENY 액션을 가진 규칙에 대해 작동하는 것으로 정의됩니다. 이 옵션이 설정되지 않으면 lua-resty-waf는 트랜잭션이 변경되지 않았더라도 규칙 일치를 기록합니다. 기본적으로 lua-resty-waf는 트랜잭션이 변경된 경우에만 일치 항목에 대한 로그 항목을 작성합니다.
예시:```lua location / { access_by_lua_block { waf:set_option("event_log_altered_only", false) } }
`mode`는 트랜잭션이 변경된 것으로 간주되는지 여부를 결정하는 데 영향을 미치지 않습니다. 즉, `DENY` 동작이 있는 규칙이 일치하더라도 lua-resty-waf가 `SIMULATE` 모드에서 실행 중이면 트랜잭션은 여전히 변경된 것으로 간주되며 규칙 일치가 기록됩니다.
### event_log_buffer_size
*기본값*: 4096
이벤트 로그를 보관하는 데 사용되는 버퍼의 임계값 크기(바이트)를 정의합니다. 이 임계값에 도달하면 버퍼가 비워집니다.
*예제*:```lua
location / {
access_by_lua_block {
-- 8 KB event log message buffer
waf:set_option("event_log_buffer_size", 8192)
}
}
기본값: ngx.INFO
이벤트 로깅에 사용되는 nginx 로그 레벨 상수를 설정합니다.
예시:```lua location / { access_by_lua_block { waf:set_option("event_log_level", ngx.WARN) } }
### event_log_ngx_vars
*기본값*: 비어 있음
`ngx.var`에서 어떤 추가 변수가 로그 이벤트에 포함되는지 정의합니다. 이는 알림에 추가 컨텍스트를 확장하는 일반적인 방법입니다. 변수 이름은 로그 항목에서 `ngx` 키 아래 항목의 키가 됩니다. 해당 변수가 nginx 변수로 존재하지 않으면 이벤트에 항목이 추가되지 않습니다.
*예제*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_ngx_vars", "host")
waf:set_option("event_log_ngx_vars", "request_id")
}
}
결과 이벤트에는 다음과 같은 추가 항목이 있습니다:```json { "ngx": { "host": "example.com", "request_id": "373bcce584e3c18a" } }
### event_log_periodic_flush
*기본값*: none
이벤트 로그 버퍼가 주기적으로 플러시되는 간격(초)을 정의합니다. 값이 구성되지 않으면 버퍼는 주기적으로 플러시되지 않으며 `event_log_buffer_size` 임계값에 도달할 때만 플러시됩니다. 오랜 시간 동안 이벤트 로그 데이터를 수신하지 않을 수 있는 매우 낮은 트래픽 사이트의 경우 버퍼에 오래된 데이터가 남아 있지 않도록 이 옵션을 구성하세요.
*예시*:```lua
location / {
access_by_lua_block {
-- flush the event log buffer every 30 seconds
waf:set_option("event_log_periodic_flush", 30)
}
}
기본값: false
true로 설정하면 로그 항목에 uri_args 키 아래에 요청 인자가 포함됩니다.
예시:```lua location / { access_by_lua_block { waf:set_option("event_log_request_arguments", true) } }
### event_log_request_body
*기본값*: false
true로 설정하면 로그 항목에 `request_body` 키 아래에 요청 본문이 포함됩니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_request_body", true)
}
}
기본값: false
HTTP 요청의 헤더는 request_headers 키 아래의 로그 이벤트에 복사됩니다.
예제:```lua location / { access_by_lua_block { waf:set_option("event_log_request_headers", true) } }
결과 이벤트에는 다음과 같은 추가 항목이 있습니다:```json
{
"request_headers": {
"accept": "*/*",
"user-agent": "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3"
}
}
기본값: false
TCP/UDP로 로깅할 때 SSL 연결을 활성화합니다.
예제:```lua location / { access_by_lua_block { waf:set_option("event_log_ssl", true) } }
### event_log_ssl_sni_host
*기본값*: none
`lua-resty-logger-socket` 연결에 대한 SNI 호스트를 설정합니다.
*예제*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_ssl_sni_host", "loghost.example.com")
}
}
기본값: false
TCP/UDP로 로깅할 때 SSL 연결에 대한 인증서 검증을 활성화합니다.
예제:```lua location / { access_by_lua_block { waf:set_option("event_log_ssl_verify", true) } }
### event_log_socket_proto
*기본값*: udp
원격 소켓을 통해 이벤트 로그를 전송할 때 사용할 IP 프로토콜(TCP 또는 UDP)을 정의합니다. 프로토콜과 관계없이 동일한 버퍼링 및 주기적 플러시 로직이 사용됩니다.
*예시*:```lua
location / {
access_by_lua_block {
-- send logs via TCP
waf:set_option("event_log_socket_proto", "tcp")
}
}
기본값: error
이벤트 로그의 대상을 정의합니다. lua-resty-waf는 현재 오류 로그, 로컬 파일 시스템의 별도 파일, 또는 원격 TCP/UDP 서버로의 로깅을 지원합니다. 후자의 두 경우, 이벤트 로그는 정의된 임계값에 도달하면 버퍼링되고 플러시됩니다 (이벤트 로깅 옵션에 대한 추가 옵션은 아래 참조).
예시:```lua location / { access_by_lua_block { -- send event logs to the server's error_log location (default) waf:set_option("event_log_target", "error")
-- send event logs to a local file on disk
waf:set_option("event_log_target", "file")
-- send event logs to a remote server
waf:set_option("event_log_target", "socket")
}
}
사용되는 로깅 라이브러리의 제한으로 인해 단일 대상 소켓만 정의할 수 있습니다. 즉, 특정 호스트/포트 조합으로 `socket` 대상을 하나만 구성할 수 있습니다. 두 번째 호스트/포트 조합을 구성하면 데이터가 제대로 기록되지 않습니다.
### event_log_target_host
*Default*: none
원격 서버를 대상으로 하는 이벤트 로그의 대상 서버를 정의합니다.
*Example*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_target_host", "10.10.10.10")
}
}
기본값: none
로컬 파일 시스템 위치를 대상으로 하는 이벤트 로그의 대상 경로를 정의합니다.
예:```lua location / { access_by_lua_block { waf:set_option("event_log_target_path", "/var/log/lua-resty-waf/event.log") } }
이 경로는 nginx 사용자가 쓸 수 있는 위치에 있어야 합니다. 참고로, 디스크 기반 로깅은 본질적으로 높은 동시성 환경에서 심각한 성능 저하를 유발할 수 있습니다.
### event_log_target_port
*기본값*: 없음
원격 서버를 대상으로 하는 이벤트 로그의 대상 포트를 정의합니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_target_port", 9001)
}
}
Default: none
규칙이 일치할 때 수행되는 작업의 기능을 재정의합니다. 자세한 내용은 예시를 참조하세요.
Example:```lua
location / {
access_by_lua_block {
local deny_override = function(waf, ctx)
ngx.log(ngx.INFO, "Overriding DENY action")
ngx.status = 404
end
-- override the DENY action with the function defined above
waf:set_option("hook_action", "DENY", deny_override)
}
}
### ignore_rule
*기본값*: none
모듈이 지정된 규칙 ID를 무시하도록 지시합니다. 체인 내의 규칙을 무시하면 전체 체인이 무시되며, 처리는 체인 다음에 오는 다음 규칙으로 계속됩니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("ignore_rule", 40294)
waf:set_option("ignore_rule", {40002, 41036})
}
}
set_option에 규칙 ID 테이블을 전달하여 여러 규칙을 무시할 수 있습니다.
기본값: 없음
모듈이 전체 규칙 세트를 무시하도록 지시합니다. 일부 규칙 세트(예: SQLi 또는 XSS CRS 규칙 세트)가 오탐(false positive)이 너무 발생하기 쉽거나 애플리케이션에 적용할 수 없는 경우 유용할 수 있습니다.
예제:```lua location / { access_by_lua_block { waf:set_option("ignore_ruleset", "41000_sqli") } }
### mode
*기본값*: SIMULATE
모듈의 작동 모드를 설정합니다. 옵션은 ACTIVE, INACTIVE, SIMULATE입니다. ACTIVE 모드에서는 규칙 일치가 기록되고 작업이 실행됩니다. SIMULATE 모드에서는 lua-resty-waf가 활성화된 각 규칙을 순회하며 규칙 일치를 기록하지만, 주어진 실행에서 지정된 작업을 완료하지는 않습니다. INACTIVE 모드는 모듈이 실행되지 않도록 방지합니다.
기본적으로 모드가 명시적으로 설정되지 않은 경우 SIMULATE가 선택됩니다. 따라서 새 사용자는 모드를 ACTIVE로 설정하여 차단을 적극적으로 구현해야 합니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("mode", "ACTIVE")
}
}
기본값: 없음
RBL 조회에 사용할 DNS 확인자(들)을 설정합니다. 현재 UDP/53 트래픽만 지원됩니다. 이 옵션은 호스트 이름이 아닌 숫자 주소로 정의해야 합니다. 이 옵션이 정의되지 않으면 모든 RBL 조회 규칙은 false를 반환합니다.
예시:```lua location / { access_by_lua_block { waf:set_option("nameservers", "10.10.10.10") } }
### process_multipart_body
*기본값* true
`lua-resty-upload` 모듈을 사용하여 multipart/form-data 요청 본문(있는 경우) 처리를 활성화합니다. 향후 lua-resty-waf는 이 처리를 사용하여 업로드 본문에 대해 더 엄격한 검사를 수행할 수 있습니다. 현재 이 모듈은 요청 본문에 대해 최소한의 기본 검사만 수행하며, 요청 본문이 유효하지 않더라도 이벤트를 기록하지 않습니다. 이 검사가 필요하지 않거나 업스트림 모듈의 버그가 HTTP 업로드에 문제를 일으키는 경우 이 옵션을 비활성화하십시오.
*예시*:```lua
location / {
access_by_lua_block {
-- disable processing of multipart/form-data requests
-- note that the request body will still be sent to the upstream
waf:set_option("process_multipart_body", false)
}
}
기본값: false
업스트림 요청에 값이 트랜잭션 ID인 HTTP 헤더 X-Lua-Resty-WAF-ID를 설정합니다. 이 ID는 디버그 로그(설정된 경우)에 있는 트랜잭션 ID와 연관됩니다. 이는 요청 추적 또는 디버그 목적으로 유용할 수 있습니다.
예시:```lua location / { access_by_lua_block { waf:set_option("req_tid_header", true) } }
### res_body_max_size
*기본값*: 1048576 (1 MB)
응답 본문이 처리되지 않을 콘텐츠 길이 임계값을 정의합니다. 응답 본문의 크기는 Content-Length 응답 헤더에 의해 결정됩니다. 이 헤더가 응답에 존재하지 않으면 응답 본문은 절대 처리되지 않습니다.
*예시*:```lua
location / {
access_by_lua_block {
-- increase the max response size to 2 MB
waf:set_option("res_body_max_size", 1024 * 1024 * 2)
}
}
본질적으로 응답을 컬렉션으로 제대로 사용하려면 전체 응답 본문을 버퍼링해야 하므로, 정당한 이유(그리고 충분한 서버 리소스) 없이 이 숫자를 크게 늘리는 것은 권장되지 않습니다.
기본값: "text/plain", "text/html"
lua-resty-waf가 응답 본문을 처리할 MIME 유형을 정의합니다. 이 값은 Content-Type 헤더에 의해 결정됩니다. 이 헤더가 없거나 응답 유형이 이 목록에 없으면 응답 본문은 처리되지 않습니다. 이 옵션을 설정하면 주어진 MIME 유형이 기존 기본값인 text/plain 및 text/html에 추가됩니다.
예시:```lua location / { access_by_lua_block { -- mime types that will be processed are now text/plain, text/html, and text/json waf:set_option("res_body_mime_types", "text/json") } }
`set_option`에 유형 테이블을 전달하여 여러 MIME 유형을 추가할 수 있습니다.
### res_tid_header
*기본값*: false
다운스트림 응답에 HTTP 헤더 `X-Lua-Resty-WAF-ID`를 설정하며, 값은 트랜잭션 ID입니다. 이 ID는 (설정된 경우) 디버그 로그에 있는 트랜잭션 ID와 연관됩니다. 이는 요청 추적 또는 디버그 목적에 유용할 수 있습니다.
*예시*:```lua
location / {
access_by_lua_block {
waf:set_option("res_tid_header", true)
}
}
기본값: 5
이상 징후 점수(anomaly scoring)의 임계값을 설정합니다. 임계값에 도달하면 lua-resty-waf가 요청을 거부합니다.
예시:```lua location / { access_by_lua_block { waf:set_option("score_threshold", 10) } }
### storage_backend
*기본값*: dict
영구 변수 저장에 사용할 엔진을 정의합니다. 현재 사용 가능한 옵션은 *dict* (ngx_lua 공유 메모리 영역), *memcached* 및 *redis*입니다.
*예시*:```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_backend", "memcached")
}
}
기본값: true
원격 영구 저장소 호스트에 대한 연결에 TCP keepalive를 활성화하거나 비활성화합니다.
예제:```lua location / { acccess_by_lua_block { waf:set_option("storage_keepalive", false) } }
### storage_keepalive_timeout
*기본값*: 10000
원격 영구 저장소 호스트를 위한 cosocket keepalive 풀의 타임아웃을 (밀리초 단위로) 구성합니다.
*예시*:```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_timeout", 30000)
}
}
기본값: 100
원격 영구 저장소 호스트를 위한 cosocket keepalive 풀의 풀 크기를 구성합니다.
예제:```lua location / { acccess_by_lua_block { waf:set_option("storage_keepalive_pool_size", 50) } }
### storage_memcached_host
*기본값*: 127.0.0.1
memcached를 영구 변수 저장 엔진으로 사용할 때 사용할 호스트를 정의합니다.
*예시*:```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_host", "10.10.10.10")
}
}
기본값: 11211
memcached를 영구 변수 저장 엔진으로 사용할 때 사용할 포트를 정의합니다.
예제:```lua location / { acccess_by_lua_block { waf:set_option("storage_memcached_port", 11221) } }
### storage_redis_host
*Default*: 127.0.0.1
redis를 영구 변수 저장 엔진으로 사용할 때 사용할 호스트를 정의합니다.
*예제*:```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_host", "10.10.10.10")
}
}
기본값: 6379
redis를 영구 변수 저장 엔진으로 사용할 때 사용할 포트를 정의합니다.
예시:```lua location / { acccess_by_lua_block { waf:set_option("storage_redis_port", 6397) } }
### storage_zone
*기본값*: 없음
영구 스토리지 데이터를 보관하는 데 사용할 `lua_shared_dict`를 정의합니다. 이 영역은 구성의 `http{}` 블록에서 정의해야 합니다.
*예*:_```lua
http {
-- define a 64M shared memory zone to hold persistent storage data
lua_shared_dict persistent_storage 64m;
}
location / {
access_by_lua_block {
waf:set_option("storage_zone", "persistent_storage")
}
}
여러 공유 영역(shared zone)을 정의하고 사용할 수 있지만, 구성 위치(configuration location)마다 하나의 영역만 정의할 수 있습니다. 영역이 가득 차서 공유 사전 인터페이스(shared dictionary interface)가 추가 키를 추가할 수 없게 되면 다음 메시지가 오류 로그에 기록됩니다:
Error adding key to persistent storage, increase the size of the lua_shared_dict
lua-resty-waf는 요청 수명주기의 여러 단계에서 실행되도록 설계되었습니다. 다음 단계에서 규칙을 처리할 수 있습니다:
이 단계들은 각각 해당하는 Nginx lua 핸들러(access_by_lua, header_filter_by_lua, body_filter_by_lua, log_by_lua)에 대응됩니다. 이 목록에 없는 lua 단계 핸들러에서 lua-resty-waf를 실행하면 동작이 깨질 수 있습니다. 이전 단계에서 사용할 수 있는 모든 데이터는 이후 단계에서도 사용할 수 있습니다. 즉, access 단계에서 사용할 수 있는 데이터는 header_filter 및 body_filter 단계에서도 사용할 수 있지만, 그 반대는 성립하지 않습니다.
lua-resty-waf는 ModSecurity CRS의 기능을 모방하도록 설계된 여러 룰셋과 함께 배포됩니다. 참고로 이러한 룰셋은 다음과 같습니다:
lua-resty-waf는 디스크에 저장된 JSON 블롭(blob)에서 규칙 정의를 파싱합니다. 규칙은 목적과 심각도에 따라 그룹화되며 룰셋으로 정의됩니다. 포함된 룰셋은 ModSecurity CRS의 일부 기능, 특히 base_rules 정의를 모방하기 위해 만들어졌습니다. 또한 포함된 modsec2lua-resty-waf.pl 스크립트를 사용하여 추가 또는 사용자 정의 룰셋을 lua-resty-waf와 호환되는 JSON 블롭으로 변환할 수 있습니다.
변환 스크립트에는 지원되지 않는 액션, 컬렉션, 연산자와 관련된 몇 가지 제한 사항이 있습니다. 알려진 비호환성의 최신 목록은 이 위키 페이지를 참조하십시오.
Freenode IRC 채널 #lua-resty-waf가 있습니다. Travis CI가 이곳으로 알림을 보냅니다. 이 채널에서 질문을 하거나 의견을 남겨도 좋습니다.
또한 CodeWake에서 Q/A를 이용할 수 있습니다:
모든 풀 리퀘스트는 개발(development) 브랜치를 대상으로 해 주세요. PR이 중요한 변경 사항인 경우 기능(feature) 브랜치를 대상으로 하세요. master로의 커밋은 문서 업데이트 또는 모듈 자체에 영향을 주지 않는 변경(개발 브랜치로 깔끔하게 병합될 수 있는 변경)에만 한정해야 합니다.
lua-resty-waf는 지속적인 개발과 개선이 진행 중이므로 기능과 성능에 제한이 있을 수 있습니다. 현재 알려진 제한 사항은 이 저장소의 GitHub 이슈 트래커에서 확인할 수 있습니다.
이 프로그램은 자유 소프트웨어입니다: 자유 소프트웨어 재단(Free Software Foundation)이 발표한 GNU 일반 공중 사용 허가서(GNU General Public License)의 제3판 또는 (선택에 따라) 이후 버전의 조건에 따라 이 프로그램을 재배포하거나 수정할 수 있습니다.
이 프로그램은 유용하게 쓰이리라는 희망으로 배포되지만, 어떠한 보증도 제공하지 않습니다. 상품성(MERCHANTABILITY) 또는 특정 목적 적합성(FITNESS FOR A PARTICULAR PURPOSE)에 대한 묵시적 보증조차 없습니다. 자세한 내용은 GNU 일반 공중 사용 허가서를 참조하십시오.
이 프로그램과 함께 GNU 일반 공중 사용 허가서 사본을 받았을 것입니다. 받지 못했다면 http://www.gnu.org/licenses/를 참조하십시오.
버그는 GitHub 이슈 트래커에 티켓을 생성하여 보고해 주세요.