Skip to content

Commit

Permalink
support test server
Browse files Browse the repository at this point in the history
Signed-off-by: Ling Samuel (WSL) <lingsamuelgrace@gmail.com>
  • Loading branch information
lingsamuel committed Jul 20, 2023
1 parent 869e0e5 commit f328133
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 26 deletions.
29 changes: 17 additions & 12 deletions apisix/plugins/chaitin-waf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -333,17 +333,19 @@ end
local function check_match(conf, ctx)
local match_passed = true

for _, match in ipairs(conf.match) do
local exp, err = expr.new(match.vars)
if err then
local msg = "failed to create match expression for " .. tostring(match.vars) .. ", err: " .. tostring(err)
core.log.error(msg)
return false, msg
end
if conf.match then
for _, match in ipairs(conf.match) do
local exp, err = expr.new(match.vars)
if err then
local msg = "failed to create match expression for " .. tostring(match.vars) .. ", err: " .. tostring(err)
core.log.error(msg)
return false, msg
end

match_passed = exp:eval(ctx.var)
if match_passed then
break
match_passed = exp:eval(ctx.var)
if match_passed then
break
end
end
end

Expand Down Expand Up @@ -424,16 +426,19 @@ local function do_access(conf, ctx)
extra_headers[HEADER_CHAITIN_WAF_TIME] = ngx_now() * 1000 - start_time

local code = 200
extra_headers[HEADER_CHAITIN_WAF_STATUS] = code
if result then
code = result.status
if result.status then
code = result.status
extra_headers[HEADER_CHAITIN_WAF_STATUS] = code
extra_headers[HEADER_CHAITIN_WAF_ACTION] = "reject"

return tonumber(code), fmt(blocked_message, code, result.event_id), extra_headers
end
end
extra_headers[HEADER_CHAITIN_WAF_STATUS] = code
if not ok then
extra_headers[HEADER_CHAITIN_WAF_STATUS] = nil
end

return nil, nil, extra_headers
end
Expand Down
56 changes: 42 additions & 14 deletions docs/zh/latest/plugins/chaitin-waf.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,42 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H 'X-API-KE
}'
```

在没有配置健康检查的情况下,一部分请求会转发到不可用的 WAF 服务器上,从而导致不可用:
在没有配置健康检查的情况下,一部分请求会转发到不可用的 WAF 服务器上,从而导致不可用(该输出开启了 `add_debug_header` 选项)

```
```bash
curl -H "Host: httpbun.org" -H "waf: true" http://127.0.0.1:9080/get -i

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 427
Connection: keep-alive
X-APISIX-CHAITIN-WAF: waf-err
X-APISIX-CHAITIN-WAF-SERVER: 127.0.0.1
X-APISIX-CHAITIN-WAF-TIME: 1
X-APISIX-CHAITIN-WAF-ACTION: pass
X-APISIX-CHAITIN-WAF-ERROR: failed to connect to t1k server 127.0.0.1:1551: connection refused
Date: Wed, 19 Jul 2023 09:41:20 GMT
X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596
Server: APISIX/3.3.0

{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Host": "httpbun.org",
"User-Agent": "curl/8.1.2",
"Waf": "true",
"X-Forwarded-For": "127.0.0.1",
"X-Forwarded-Host": "httpbun.org",
"X-Forwarded-Port": "9080",
"X-Forwarded-Proto": "http",
"X-Real-Ip": "127.0.0.1"
},
"method": "GET",
"origin": "127.0.0.1, 122.231.76.178",
"url": "http://httpbun.org/get"
}
```

添加了健康检查的示例配置如下,此时健康检查将会过滤掉不可用的 WAF 服务器:
Expand Down Expand Up @@ -291,20 +322,17 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H 'X-API-KE

## 禁用插件

当您要禁用 `tencent-waf` 插件时,这很简单,您可以在插件配置中删除相应的 json 配置,无需重新启动服务,它将立即生效
需要禁用 `tencent-waf` 插件时,在插件配置中删除相应的插件配置即可

```bash
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/hello",
"plugins": {},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
"uri": "/*",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbun.org:80": 1
}
}
}'
```
60 changes: 60 additions & 0 deletions t/lib/chaitin_waf_server.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
local _M = {}

function _M.pass(sock)
sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) })
sock:send(".")
sock:send({ string.char(165), string.char(77), string.char(0), string.char(0), string.char(0) })
sock:send("{\"event_id\":\"1e902e84bf5a4ead8f7760a0fe2c7719\",\"request_hit_whitelist\":false}")
end

-- 返回值会被分为如下的形式

-- 长度为 5 bytes 的 packet
-- 紧跟一段 body

-- 其中:
-- 第一个 packet 的 第一个 byte 需要 & 0x40 == 0x40
-- 最后一个 packet 的 第一个 byte 需要 & 0x80 == 0x80

-- tag 为 packet 的第一个 byte & !0x40 & !0x80

function _M.ip(sock)
sock:send("HTTP/1.1 200\r\nserver:nginx\r\ncontent-type: application/json\r\ncontent-length: 27\r\n\r\n{\"origin\":\"122.231.76.178\"}")
end


function _M.reject(sock)
sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) })
sock:send("?")
sock:send({ string.char(2), string.char(3), string.char(0), string.char(0), string.char(0) })
sock:send("403")
sock:send({ string.char(37), string.char(77), string.char(0), string.char(0), string.char(0) })
sock:send("{\"event_id\":\"b3c6ce574dc24f09a01f634a39dca83b\",\"request_hit_whitelist\":false}")
sock:send({ string.char(35), string.char(79), string.char(0), string.char(0), string.char(0) })
sock:send("Set-Cookie:sl-session=ulgbPfMSuWRNsi/u7Aj9aA==; Domain=; Path=/; Max-Age=86400\n")
sock:send({ string.char(164), string.char(51), string.char(0), string.char(0), string.char(0) })
sock:send("<!-- event_id: b3c6ce574dc24f09a01f634a39dca83b -->")
end

function _M.go()
local action = "pass"

local timeout = ngx.var.arg_timeout
if timeout then
ngx.sleep(tonumber(timeout))
end

--ngx.log(ngx.ERR, action .. ": waf recv request body size: ", ngx.var.http_content_length)

ngx.flush(true)
local sock, err = ngx.req.socket(true)
if not sock then
core.log.error("failed to get the request socket: ", err)
return
end

_M[action](sock)
ngx.exit(200)
end

return _M
178 changes: 178 additions & 0 deletions t/plugin/chaitin-waf.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use t::APISIX 'no_plan';

repeat_each(1);
no_long_string();
no_root_location();

add_block_preprocessor(sub {
my ($block) = @_;

my $stream_default_server = <<_EOC_;
listen 8088;
listen 8089;
content_by_lua_block {
require("apisix.chaitin_waf_server").go()
}
_EOC_

$block->set_value("stream_server_config", $stream_default_server);

# setup default conf.yaml
my $extra_yaml_config = $block->extra_yaml_config // <<_EOC_;
apisix:
stream_proxy: # TCP/UDP L4 proxy
only: true # Enable L4 proxy only without L7 proxy.
tcp:
- addr: 9100 # Set the TCP proxy listening ports.
tls: true
- addr: "127.0.0.1:9101"
udp: # Set the UDP proxy listening ports.
- 9200
- "127.0.0.1:9201"
plugins:
- chaitin-waf
_EOC_

$block->set_value("extra_yaml_config", $extra_yaml_config);

if (!$block->request) {
# use /do instead of /t because stream server will inject a default /t location
$block->set_value("request", "GET /do");
}

if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
$block->set_value("no_error_log", "[error]");
}
});

run_tests;

__DATA__
=== TEST 1: wrong schema: nodes empty
--- config
location /do {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf',
ngx.HTTP_PUT,
[[{
"nodes": []
}
]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: expect array to have at least 1 items"}
=== TEST 2: wrong schema: nodes misses host
--- config
location /do {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf',
ngx.HTTP_PUT,
[[{
"nodes": [
{}
]
}
]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: failed to validate item 1: property \"host\" is required"}
=== TEST 4: sanity
--- config
location /do {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf',
ngx.HTTP_PUT,
[[{
"nodes": [
{
"host": "127.0.0.1",
"port": 8088
},
{
"host": "127.0.0.1",
"port": 8089
}
]
}]]
)
if code >= 300 then
ngx.status = code
return ngx.print(body)
end
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"chaitin-waf": {
"upstream": {
"servers": ["httpbun.org"]
}
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== test 5: pass
--- request
GET /hello
--- error_code: 200
--- response_body
hello world
--- error_log
--- response_headers
X-APISIX-CHAITIN-WAF: yes
X-APISIX-CHAITIN-WAF-STATUS: 200
X-APISIX-CHAITIN-WAF-ACTION: pass
--- response_headers_like
X-APISIX-CHAITIN-WAF-TIME:

0 comments on commit f328133

Please sign in to comment.