- Remove telemetry: disabled all phone-home (installation notify, false positives, behavior tracking, upgrade tips) - Version branding: removed 'ce-' prefix, product name changed to '长亭雷池 WAF' - Detection engine enhancement: strict preset enables all 17 modules with aggressive configs - Multi-user RBAC: admin/operator/viewer roles with password + TOTP 2FA - Rate limiting: nginx limit_req per website (RPS, burst, action) - JS Challenge: browser fingerprint verification page with cookie-based bypass - Security fixes: removed InsecureSkipVerify, hardcoded credentials, NO_AUTH backdoor
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# ingress-nginx-safeline
|
||||
|
||||
[Ingress-nginx](https://kubernetes.github.io/ingress-nginx/) plugin for Chaitin SafeLine Web Application Firewall (WAF). This plugin is used to protect your API from malicious requests. It can be used to block requests that contain malicious content in the request body, query parameters, headers, or URI.
|
||||
|
||||
## Safeline Prepare
|
||||
The detection engine of the SafeLine provides services by default via Unix socket. We need to modify it to use TCP, so it can be called by the t1k plugin.
|
||||
|
||||
1.Navigate to the configuration directory of the SafeLine detection engine:
|
||||
```shell
|
||||
cd /data/safeline/resources/detector/
|
||||
```
|
||||
2.Open the `detector.yml` file in a text editor. Modify the bind configuration from Unix socket to TCP by adding the following settings:
|
||||
```yaml
|
||||
bind_addr: 0.0.0.0
|
||||
listen_port: 8000
|
||||
```
|
||||
These configuration values will override the default settings in the container, making the SafeLine engine listen on port 8000.
|
||||
|
||||
3.Next, map the container’s port 8000 to the host machine. First, navigate to the SafeLine installation directory:
|
||||
```shell
|
||||
cd /data/safeline
|
||||
```
|
||||
|
||||
4.Open the compose.yaml file in a text editor and add the ports field to the detector container to expose port 8000:
|
||||
```yaml
|
||||
...
|
||||
detect:
|
||||
ports:
|
||||
- 8000:8000
|
||||
...
|
||||
```
|
||||
|
||||
5.Save the changes and restart SafeLine with the following commands:
|
||||
```shell
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
This will apply the changes and activate the new configuration.
|
||||
|
||||
## Plugin Usage
|
||||
|
||||
### Step 1: Install the plugin
|
||||
|
||||
way 1: Build your own ingress-nginx/controller image with the plugin installed.
|
||||
|
||||
```dockerfile
|
||||
FROM registry.k8s.io/ingress-nginx/controller:v1.10.1
|
||||
|
||||
USER root
|
||||
|
||||
RUN apk add --no-cache make gcc unzip wget
|
||||
|
||||
# install luaroncks
|
||||
RUN wget https://luarocks.org/releases/luarocks-3.11.0.tar.gz && \
|
||||
tar zxpf luarocks-3.11.0.tar.gz && \
|
||||
cd luarocks-3.11.0 && \
|
||||
./configure && \
|
||||
make && \
|
||||
make install && \
|
||||
cd .. && \
|
||||
rm -rf luarocks-3.11.0 luarocks-3.11.0.tar.gz
|
||||
|
||||
RUN luarocks install ingress-nginx-safeline && \
|
||||
ln -s /usr/local/share/lua/5.1/safeline /etc/nginx/lua/plugins/safeline
|
||||
|
||||
USER www-data
|
||||
```
|
||||
|
||||
way 2: Use the chaitin ingress-nginx-controller image.
|
||||
|
||||
replace image ingress-nginx-controller with `docker.io/chaitin/ingress-nginx-controller:v1.10.1` in your deployment.
|
||||
|
||||
### Step 2: Configure the plugin
|
||||
|
||||
use a ConfigMap to configure the plugin
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: safeline
|
||||
namespace: default
|
||||
data:
|
||||
host: "YOUR_DETECTOR_HOST"
|
||||
port: "YOUR_DETECTOR_PORT"
|
||||
```
|
||||
|
||||
### Step 3: Configure the ingress-controller
|
||||
|
||||
inject env `SAFELINE_HOST` and `SAFELINE_PORT` to the ingress-controller deployment
|
||||
|
||||
```yaml
|
||||
...
|
||||
env:
|
||||
- name: SAFELINE_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: host
|
||||
- name: SAFELINE_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: port
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
### Step 3: Enable the plugin
|
||||
|
||||
enable safeline plugin in configmap
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
data:
|
||||
allow-snippet-annotations: "false"
|
||||
plugins: "safeline"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ingress-nginx-controller
|
||||
namespace: default
|
||||
```
|
||||
|
||||
### Step 4: Set externalTrafficPolicy to Local
|
||||
by default, the ingress-nginx-controller service is of type LoadBalancer, which means the source IP of the request will be the IP of the LoadBalancer. If you want to get the real source IP, you can set the externalTrafficPolicy to Local.
|
||||
|
||||
### Step 5: Test the plugin
|
||||
|
||||
use a simple http sql injection test
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost/ -d "select * from users where id=1 or 1=1"
|
||||
```
|
||||
|
||||
you should get a 403 response.
|
||||
```bash
|
||||
{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "b53eb5b95796475699c52a019abb8e6a"}
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
package = "ingress-nginx-safeline"
|
||||
version = "1.0.2-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/ingress-nginx-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Ingress-Nginx plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/xbingW/ingress-nginx-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["safeline.main"] = "lib/safeline/main.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package = "ingress-nginx-safeline"
|
||||
version = "1.0.3-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/ingress-nginx-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Ingress-Nginx plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/xbingW/ingress-nginx-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["safeline.main"] = "lib/safeline/main.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package = "ingress-nginx-safeline"
|
||||
version = "1.0.4-1"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/ingress-nginx-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Ingress-Nginx plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/ingress-nginx-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["safeline.main"] = "lib/safeline/main.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
local t1k = require "resty.t1k"
|
||||
local t1k_constants = require "resty.t1k.constants"
|
||||
|
||||
local ngx = ngx
|
||||
local fmt = string.format
|
||||
|
||||
local blocked_message = [[{"code": %s, "success":false, ]] ..
|
||||
[["message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]]
|
||||
|
||||
local _M = {}
|
||||
|
||||
local mode = os.getenv("SAFELINE_MODE")
|
||||
local host = os.getenv("SAFELINE_HOST")
|
||||
local port = os.getenv("SAFELINE_PORT")
|
||||
local connect_timeout = os.getenv("SAFELINE_CONNECT_TIMEOUT")
|
||||
local send_timeout = os.getenv("SAFELINE_SEND_TIMEOUT")
|
||||
local read_timeout = os.getenv("SAFELINE_READ_TIMEOUT")
|
||||
local req_body_size = os.getenv("SAFELINE_REQ_BODY_SIZE")
|
||||
local keepalive_size = os.getenv("SAFELINE_KEEPALIVE_SIZE")
|
||||
local keepalive_timeout = os.getenv("SAFELINE_KEEPALIVE_TIMEOUT")
|
||||
local remote_addr = os.getenv("SAFELINE_REMOTE_ADDR")
|
||||
|
||||
local function get_conf()
|
||||
local t = {
|
||||
mode = mode or "block",
|
||||
host = host,
|
||||
port = port,
|
||||
connect_timeout = connect_timeout or 1000,
|
||||
send_timeout = send_timeout or 1000,
|
||||
read_timeout = read_timeout or 1000,
|
||||
req_body_size = req_body_size or 1024,
|
||||
keepalive_size = keepalive_size or 256,
|
||||
keepalive_timeout = keepalive_timeout or 60000,
|
||||
remote_addr = remote_addr
|
||||
}
|
||||
return t
|
||||
end
|
||||
|
||||
function _M.rewrite()
|
||||
local t = get_conf()
|
||||
if not t.host then
|
||||
ngx.log(ngx.ERR, "safeline host is required")
|
||||
return
|
||||
end
|
||||
local ok, err, result = t1k.do_access(t, false)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to detector req: ", err)
|
||||
return
|
||||
end
|
||||
if result then
|
||||
if result.action == t1k_constants.ACTION_BLOCKED then
|
||||
local msg = fmt(blocked_message, result.status, result.event_id)
|
||||
ngx.log(ngx.ERR, "blocked by safeline waf: ",msg)
|
||||
ngx.status = tonumber(result.status,10)
|
||||
ngx.say(msg)
|
||||
return ngx.exit(ngx.HTTP_OK)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,69 @@
|
||||
# Kong Safeline Plugin
|
||||
Kong plugin for Chaitin SafeLine Web Application Firewall (WAF). This plugin is used to protect your API from malicious requests. It can be used to block requests that contain malicious content in the request body, query parameters, headers, or URI.
|
||||
|
||||
## Safeline Prepare
|
||||
The detection engine of the SafeLine provides services by default via Unix socket. We need to modify it to use TCP, so it can be called by the t1k plugin.
|
||||
|
||||
1.Navigate to the configuration directory of the SafeLine detection engine:
|
||||
```shell
|
||||
cd /data/safeline/resources/detector/
|
||||
```
|
||||
2.Open the `detector.yml` file in a text editor. Modify the bind configuration from Unix socket to TCP by adding the following settings:
|
||||
```yaml
|
||||
bind_addr: 0.0.0.0
|
||||
listen_port: 8000
|
||||
```
|
||||
These configuration values will override the default settings in the container, making the SafeLine engine listen on port 8000.
|
||||
|
||||
3.Next, map the container’s port 8000 to the host machine. First, navigate to the SafeLine installation directory:
|
||||
```shell
|
||||
cd /data/safeline
|
||||
```
|
||||
|
||||
4.Open the compose.yaml file in a text editor and add the ports field to the detector container to expose port 8000:
|
||||
```yaml
|
||||
...
|
||||
detect:
|
||||
ports:
|
||||
- 8000:8000
|
||||
...
|
||||
```
|
||||
|
||||
5.Save the changes and restart SafeLine with the following commands:
|
||||
```shell
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
This will apply the changes and activate the new configuration.
|
||||
|
||||
## Plugin Installation
|
||||
To install the plugin, run the following command in your Kong server:
|
||||
|
||||
```shell
|
||||
$ luarocks install kong-safeline
|
||||
```
|
||||
|
||||
## Plugin Configuration
|
||||
You can add the plugin to your API by making the following request:
|
||||
|
||||
```shell
|
||||
# if your detector is running on tcp port
|
||||
$ curl -X POST http://kong:8001/services/{name}/plugins \
|
||||
--data "name=safeline" \
|
||||
--data "config.host=your_detector_host" \
|
||||
--data "config.port=your_detector_port"
|
||||
# if your detector is running on unix socket
|
||||
$ curl -X POST http://kong:8001/services/{name}/plugins \
|
||||
--data "name=safeline" \
|
||||
--data "config.host=unix:/path/to/your/unix/socket"
|
||||
```
|
||||
|
||||
## Test
|
||||
You can test the plugin by sending a request to your API with malicious content. If the request is blocked, you will receive a `403 Forbidden` response.
|
||||
|
||||
```shell
|
||||
$ curl -X POST http://kong:8000?1=1%20and%202=2
|
||||
|
||||
# you will receive a 403 Forbidden response
|
||||
{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "8b41a021ea9541c89bb88f3773b4da24"}
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.0-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/kong-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/xbingW/kong-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.1-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/kong-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/xbingW/kong-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.2-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/kong-safeline.git"
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/xbingW/kong-safeline",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.3-1"
|
||||
source = {
|
||||
url = "file://kong-safeline-1.0.3.tar.gz",
|
||||
}
|
||||
build = {
|
||||
type = "script",
|
||||
rockspec = {
|
||||
build = {
|
||||
"git clone https://github.com/chaitin/SafeLine.git",
|
||||
"cp -r sdk/kong .",
|
||||
"rm -rf SafeLine"
|
||||
}
|
||||
}
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/SafeLine",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.4-1"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/SafeLine.git",
|
||||
}
|
||||
build = {
|
||||
type = "script",
|
||||
rockspec = {
|
||||
build = {
|
||||
"git clone https://github.com/chaitin/SafeLine.git",
|
||||
"cp -r sdk/kong .",
|
||||
"rm -rf SafeLine"
|
||||
}
|
||||
}
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/SafeLine",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.5-1"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/SafeLine.git",
|
||||
}
|
||||
build = {
|
||||
type = "script",
|
||||
rockspec = {
|
||||
build = {
|
||||
"git clone https://github.com/chaitin/SafeLine.git",
|
||||
"cp -r sdk/kong .",
|
||||
"rm -rf SafeLine"
|
||||
}
|
||||
}
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/SafeLine",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.6-1"
|
||||
source = {
|
||||
url = "git://github.com/xbingW/kong-safeline.git",
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/SafeLine",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package = "kong-safeline"
|
||||
version = "1.0.7-1"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/kong-safeline.git",
|
||||
}
|
||||
description = {
|
||||
summary = "Kong plugin for Chaitin SafeLine Web Application Firewall",
|
||||
homepage = "https://github.com/chaitin/SafeLine",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xiaobing Wang <xiaobing.wang@chaitin.com>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua-resty-t1k >= 1.1.5"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["kong.plugins.safeline.handler"] = "kong/plugins/safeline/handler.lua",
|
||||
["kong.plugins.safeline.schema"] = "kong/plugins/safeline/schema.lua"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
local t1k = require "resty.t1k"
|
||||
local t1k_constants = require "resty.t1k.constants"
|
||||
|
||||
local fmt = string.format
|
||||
|
||||
local SafelineHandler = {
|
||||
VERSION = "0.0.1",
|
||||
PRIORITY = 1000
|
||||
}
|
||||
|
||||
local blocked_message = [[{"code": %s, "success":false, ]] ..
|
||||
[["message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]]
|
||||
|
||||
local function get_conf(conf)
|
||||
local t = {
|
||||
mode = conf.mode,
|
||||
host = conf.host,
|
||||
port = conf.port,
|
||||
connect_timeout = conf.connect_timeout,
|
||||
send_timeout = conf.send_timeout,
|
||||
read_timeout = conf.read_timeout,
|
||||
req_body_size = conf.req_body_size,
|
||||
keepalive_size = conf.keepalive_size,
|
||||
keepalive_timeout = conf.keepalive_timeout,
|
||||
remote_addr = conf.remote_addr
|
||||
}
|
||||
return t
|
||||
end
|
||||
|
||||
function SafelineHandler:access(conf)
|
||||
-- your custom code here
|
||||
local t = get_conf(conf)
|
||||
local ok, err, result = t1k.do_access(t, false)
|
||||
if not ok then
|
||||
kong.log.err("failed to detector req: ", err)
|
||||
end
|
||||
if result and result.status then
|
||||
if result.action == t1k_constants.ACTION_BLOCKED then
|
||||
local msg = fmt(blocked_message, result.status, result.event_id)
|
||||
kong.log.debug("blocked by safeline: ",msg)
|
||||
return kong.response.exit(tonumber(result.status), msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return SafelineHandler
|
||||
@@ -0,0 +1,73 @@
|
||||
local typedefs = require "kong.db.schema.typedefs"
|
||||
|
||||
return {
|
||||
name = "kong-safeline",
|
||||
fields = {{
|
||||
consumer = typedefs.no_consumer
|
||||
}, {
|
||||
protocols = typedefs.protocols_http
|
||||
}, {
|
||||
config = {
|
||||
type = "record",
|
||||
fields = {{
|
||||
host = {
|
||||
type = "string",
|
||||
required = true
|
||||
}
|
||||
}, {
|
||||
port = {
|
||||
type = "number",
|
||||
required = false
|
||||
}
|
||||
}, {
|
||||
mode = {
|
||||
type = "string",
|
||||
required = false,
|
||||
default = "block",
|
||||
one_of = {"monitor", "block", "off"}
|
||||
}
|
||||
}, {
|
||||
connect_timeout = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
send_timeout = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
read_timeout = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
req_body_size = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
keepalive_size = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
keepalive_timeout = {
|
||||
type = "number",
|
||||
required = false,
|
||||
default = 1000
|
||||
}
|
||||
}, {
|
||||
remote_addr = {
|
||||
type = "string",
|
||||
required = false
|
||||
}
|
||||
}}
|
||||
}
|
||||
}}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Lua
|
||||
uses: leafo/gh-actions-lua@v10
|
||||
|
||||
- name: Install Luarocks
|
||||
uses: leafo/gh-actions-luarocks@v4
|
||||
|
||||
- name: Extract release tag
|
||||
id: release_tag
|
||||
run: |
|
||||
# Extract the tag name from the ref
|
||||
tag="${GITHUB_REF#refs/tags/}"
|
||||
version_without_v="${tag#v}"
|
||||
echo "version=${tag}" >> $GITHUB_ENV
|
||||
echo "version_without_v=${version_without_v}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.version }}
|
||||
release_name: ${{ env.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload to luarocks
|
||||
env:
|
||||
LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }}
|
||||
run: |
|
||||
luarocks install dkjson
|
||||
luarocks upload rockspec/lua-resty-t1k-${{ env.version_without_v }}-0.rockspec --api-key=${{ secrets.LUAROCKS_API_KEY }}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
name: Test
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
luacheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: leafo/gh-actions-lua@v10
|
||||
with:
|
||||
luaVersion: "luajit-openresty"
|
||||
- uses: leafo/gh-actions-luarocks@v4
|
||||
- run: luarocks install luacheck
|
||||
- run: luacheck lib
|
||||
|
||||
run_tests:
|
||||
strategy:
|
||||
matrix:
|
||||
openresty_version:
|
||||
- 1.17.8.2
|
||||
- 1.19.9.1
|
||||
- 1.21.4.3
|
||||
- 1.25.3.1
|
||||
|
||||
services:
|
||||
detector:
|
||||
image: chaitin/safeline-detector:t1k-ci-1.6.0
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: openresty/openresty:${{ matrix.openresty_version }}-alpine-fat
|
||||
# --init runs tinit as PID 1 and prevents the 'WARNING: killing the child process' spam from the test suite
|
||||
options: --init
|
||||
|
||||
steps:
|
||||
- name: Install deps
|
||||
run: |
|
||||
apk add --no-cache bash bind-tools curl git git-lfs libarchive-tools perl perl-dev wget
|
||||
ln -s /usr/bin/bsdtar /usr/bin/tar
|
||||
|
||||
- name: Install CPAN
|
||||
run: curl -s -L http://xrl.us/cpanm > /bin/cpanm && chmod +x /bin/cpanm
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cpan
|
||||
~/.cache
|
||||
key: ${{ runner.os }}-${{ matrix.openresty_version }}-cache
|
||||
|
||||
- name: Install Test::Nginx
|
||||
run: cpanm -q -n Test::Nginx
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
curl -fs -X POST -H "Content-Type: application/octet-stream" --data-binary "@ci/bytecode" "http://detector:8001/update/policy"
|
||||
env DETECTOR_IP=$(dig detector +short) prove -r t/
|
||||
@@ -0,0 +1,25 @@
|
||||
# IntelliJ project files
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# Test::Nginx files
|
||||
t/servroot
|
||||
|
||||
# luarocks build files
|
||||
*.src.rock
|
||||
@@ -0,0 +1,6 @@
|
||||
std = "ngx_lua"
|
||||
redefined = false
|
||||
max_line_length = 130
|
||||
max_code_line_length = 130
|
||||
max_string_line_length = 130
|
||||
max_comment_line_length = 130
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Beijing Chaitin Technology Co., Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,76 @@
|
||||
# lua-resty-t1k
|
||||
|
||||
[](https://luarocks.org/modules/blaisewang/lua-resty-t1k)
|
||||
[](https://github.com/chaitin/lua-resty-t1k/releases)
|
||||
[](https://github.com/chaitin/lua-resty-t1k/blob/main/LICENSE)
|
||||
|
||||
## Name
|
||||
|
||||
Lua implementation of the T1K protocol for [Chaitin/SafeLine](https://github.com/chaitin/safeline) Web Application Firewall.
|
||||
|
||||
## Status
|
||||
|
||||
Production ready.
|
||||
|
||||
[](https://github.com/chaitin/lua-resty-t1k/actions)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
luarocks install lua-resty-t1k
|
||||
```
|
||||
|
||||
If you are in Mainland China
|
||||
|
||||
```bash
|
||||
luarocks install lua-resty-t1k --server https://luarocks.cn
|
||||
```
|
||||
|
||||
## Synopsis
|
||||
|
||||
```lua
|
||||
location / {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local t = {
|
||||
mode = "block", -- block or monitor or off, default off
|
||||
host = "unix:/workdir/snserver.sock", -- required, SafeLine WAF detection service host, unix domain socket, IP, or domain is supported, string
|
||||
port = 8000, -- required when the host is an IP or domain, SafeLine WAF detection service port, integer
|
||||
connect_timeout = 1000, -- connect timeout, in milliseconds, integer, default 1s (1000ms)
|
||||
send_timeout = 1000, -- send timeout, in milliseconds, integer, default 1s (1000ms)
|
||||
read_timeout = 1000, -- read timeout, in milliseconds, integer, default 1s (1000ms)
|
||||
req_body_size = 1024, -- request body size, in KB, integer, default 1MB (1024KB)
|
||||
keepalive_size = 256, -- maximum concurrent idle connections to the SafeLine WAF detection service, integer, default 256
|
||||
keepalive_timeout = 60000, -- idle connection timeout, in milliseconds, integer, default 60s (60000ms)
|
||||
remote_addr = "http_x_forwarded_for: 1", -- remote address from ngx.var.VARIABLE, string, default from ngx.var.remote_addr
|
||||
}
|
||||
|
||||
local ok, err, _ = t1k.do_access(t, true)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
}
|
||||
|
||||
header_filter_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
t1k.do_header_filter()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Lua Resty T1K vs. C T1K
|
||||
|
||||
[C T1K](https://t1k.chaitin.com/), as part of SafeLine's enterprise edition, is a deployment mode crafted in C language for enhanced performance.
|
||||
It is compatible with all versions of Nginx and does not require deployment via OpenResty (lua_nginx_module).
|
||||
|
||||
| | Lua Resty T1K | C T1K |
|
||||
|-----------------------|---------------|-------|
|
||||
| Request Detection | ✅ | ✅ |
|
||||
| Response Detection | ❌ | ✅ |
|
||||
| Health Checks* | ❌ | ✅ |
|
||||
| Cookie Protection | ❌ | ✅ |
|
||||
| Bot Protection | ❌ | ✅ |
|
||||
| Proxy-side Statistics | ❌ | ✅ |
|
||||
|
||||
* APISIX implements health check functionality for the `chaitin-waf` plugin. For more information, please see the [chaitin-waf documentation](https://apisix.apache.org/docs/apisix/next/plugins/chaitin-waf/).
|
||||
@@ -0,0 +1 @@
|
||||
/bytecode
|
||||
@@ -0,0 +1,6 @@
|
||||
# Usage:
|
||||
# docker build -t chaitin/safeline-detector:t1k-ci-1.6.0 .
|
||||
# docker push chaitin/safeline-detector:t1k-ci-1.6.0
|
||||
FROM chaitin/safeline-detector:1.6.0
|
||||
|
||||
RUN sed -i "s/^# bind_addr/bind_addr/; s/^# listen_port/listen_port/; s/^bind_addr: unix/# bind_addr: unix/;" /detector/snserver.yml
|
||||
Binary file not shown.
@@ -0,0 +1,97 @@
|
||||
local consts = require "resty.t1k.constants"
|
||||
local filter = require "resty.t1k.filter"
|
||||
local handler = require "resty.t1k.handler"
|
||||
local log = require "resty.t1k.log"
|
||||
local request = require "resty.t1k.request"
|
||||
local utils = require "resty.t1k.utils"
|
||||
|
||||
local lower = string.lower
|
||||
|
||||
local ngx = ngx
|
||||
local nlog = ngx.log
|
||||
|
||||
local log_fmt = log.fmt
|
||||
local debug_fmt = log.debug_fmt
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local DEFAULT_T1K_CONNECT_TIMEOUT = 1000 -- 1s
|
||||
local DEFAULT_T1K_SEND_TIMEOUT = 1000 -- 1s
|
||||
local DEFAULT_T1K_READ_TIMEOUT = 1000 -- 1s
|
||||
local DEFAULT_T1K_REQ_BODY_SIZE = 1024 -- 1024 KB
|
||||
local DEFAULT_T1K_KEEPALIVE_SIZE = 256
|
||||
local DEFAULT_T1K_KEEPALIVE_TIMEOUT = 60 * 1000 -- 60s
|
||||
|
||||
function _M.do_access(t, handle)
|
||||
local ok, err, result
|
||||
local opts = {}
|
||||
t = t or {}
|
||||
|
||||
if not t.mode then
|
||||
return true, nil, nil
|
||||
end
|
||||
|
||||
opts.mode = lower(t.mode)
|
||||
if opts.mode == consts.MODE_OFF then
|
||||
nlog(debug_fmt("t1k is not enabled"))
|
||||
return true, nil, nil
|
||||
end
|
||||
|
||||
if opts.mode ~= consts.MODE_OFF and opts.mode ~= consts.MODE_BLOCK and opts.mode ~= consts.MODE_MONITOR then
|
||||
err = log_fmt("invalid t1k mode: %s", t.mode)
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
if not t.host then
|
||||
err = log_fmt("invalid t1k host: %s", t.host)
|
||||
return nil, err, nil
|
||||
end
|
||||
opts.host = t.host
|
||||
|
||||
if utils.starts_with(opts.host, consts.UNIX_SOCK_PREFIX) then
|
||||
opts.uds = true
|
||||
else
|
||||
if not tonumber(t.port) then
|
||||
err = log_fmt("invalid t1k port: %s", t.port)
|
||||
return nil, err, nil
|
||||
end
|
||||
opts.port = tonumber(t.port)
|
||||
end
|
||||
|
||||
opts.connect_timeout = t.connect_timeout or DEFAULT_T1K_CONNECT_TIMEOUT
|
||||
opts.send_timeout = t.send_timeout or DEFAULT_T1K_SEND_TIMEOUT
|
||||
opts.read_timeout = t.read_timeout or DEFAULT_T1K_READ_TIMEOUT
|
||||
opts.req_body_size = t.req_body_size or DEFAULT_T1K_REQ_BODY_SIZE
|
||||
opts.keepalive_size = t.keepalive_size or DEFAULT_T1K_KEEPALIVE_SIZE
|
||||
opts.keepalive_timeout = t.keepalive_timeout or DEFAULT_T1K_KEEPALIVE_TIMEOUT
|
||||
|
||||
if t.remote_addr then
|
||||
local var, idx = utils.to_var_idx(t.remote_addr)
|
||||
opts.remote_addr_var = var
|
||||
opts.remote_addr_idx = idx
|
||||
end
|
||||
|
||||
ok, err, result = request.do_request(opts)
|
||||
if not ok then
|
||||
return ok, err, result
|
||||
end
|
||||
|
||||
if handle and opts.mode == consts.MODE_BLOCK then
|
||||
ok, err = _M.do_handle(result)
|
||||
end
|
||||
|
||||
return ok, err, result
|
||||
end
|
||||
|
||||
function _M.do_handle(t)
|
||||
local ok, err = handler.handle(t)
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function _M.do_header_filter()
|
||||
filter.do_header_filter()
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,24 @@
|
||||
local _M = {
|
||||
_VERSION = '1.0.0',
|
||||
}
|
||||
|
||||
function _M:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function _M:add(v)
|
||||
self[#self + 1] = v
|
||||
end
|
||||
|
||||
function _M:len()
|
||||
local len = 0
|
||||
for _, v in ipairs(self) do
|
||||
len = len + #v
|
||||
end
|
||||
return len
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,30 @@
|
||||
local t = {}
|
||||
|
||||
t.ACTION_PASSED = "."
|
||||
t.ACTION_BLOCKED = "?"
|
||||
|
||||
t.MODE_OFF = "off"
|
||||
t.MODE_BLOCK = "block"
|
||||
t.MODE_MONITOR = "monitor"
|
||||
|
||||
t.T1K_HEADER_SIZE = 5
|
||||
|
||||
t.TAG_HEAD = 0x01
|
||||
t.TAG_BODY = 0x02
|
||||
t.TAG_EXTRA = 0x03
|
||||
t.TAG_VERSION = 0x20
|
||||
t.TAG_EXTRA_HEADER = 0x23
|
||||
t.TAG_EXTRA_BODY = 0x24
|
||||
|
||||
t.MASK_FIRST = 0x40
|
||||
t.MASK_LAST = 0x80
|
||||
|
||||
t.NGX_HTTP_HEADER_PREFIX = "http_"
|
||||
|
||||
t.BLOCK_CONTENT_TYPE = "application/json"
|
||||
t.BLOCK_CONTENT_FORMAT = [[
|
||||
{"code": %s, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]]
|
||||
|
||||
t.UNIX_SOCK_PREFIX = "unix:"
|
||||
|
||||
return t
|
||||
@@ -0,0 +1,34 @@
|
||||
local buffer = require "resty.t1k.buffer"
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local buffer_size = 2 ^ 13
|
||||
|
||||
function _M.read(p, size)
|
||||
size = (not size or size < 0) and 0 or size
|
||||
|
||||
local f, err = io.open(p, "rb")
|
||||
if not f or err then
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
local left = size
|
||||
local buf = buffer:new()
|
||||
|
||||
while left ~= 0 do
|
||||
local block_size = math.min(left, buffer_size)
|
||||
local block = f:read(block_size)
|
||||
if not block then
|
||||
break
|
||||
end
|
||||
buf:add(block)
|
||||
left = math.max(left - block_size, 0)
|
||||
end
|
||||
f:close()
|
||||
|
||||
return true, nil, buf
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,50 @@
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local find = string.find
|
||||
local sub = string.sub
|
||||
|
||||
local ngx = ngx
|
||||
|
||||
local function parse_extra_header(extra_header)
|
||||
local t = {}
|
||||
local idx = 1
|
||||
|
||||
while (idx <= #extra_header) do
|
||||
local key, val
|
||||
local _, to = find(extra_header, ":", idx)
|
||||
if to == 0 then
|
||||
break
|
||||
else
|
||||
key = sub(extra_header, idx, to - 1)
|
||||
end
|
||||
|
||||
idx = to + 1
|
||||
_, to = find(extra_header, "\n", idx)
|
||||
if to == 0 then
|
||||
break
|
||||
else
|
||||
val = sub(extra_header, idx, to - 1)
|
||||
end
|
||||
|
||||
t[key] = val
|
||||
idx = to + 1
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function _M.do_header_filter()
|
||||
local extra_header = ngx.ctx.t1k_extra_header
|
||||
if extra_header ~= nil then
|
||||
local header_table = parse_extra_header(extra_header)
|
||||
for k, v in pairs(header_table) do
|
||||
if k ~= nil and v ~= nil then
|
||||
ngx.header[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,36 @@
|
||||
local consts = require "resty.t1k.constants"
|
||||
local log = require "resty.t1k.log"
|
||||
|
||||
local fmt = string.format
|
||||
|
||||
local ngx = ngx
|
||||
|
||||
local log_fmt = log.fmt
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
function _M.handle(t)
|
||||
local t_type = type(t)
|
||||
if t_type ~= "table" then
|
||||
local err = log_fmt("invalid result type: %s", t_type)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local action = t["action"]
|
||||
if action == consts.ACTION_PASSED then
|
||||
return true, nil
|
||||
elseif action == consts.ACTION_BLOCKED then
|
||||
ngx.status = t["status"] or ngx.HTTP_FORBIDDEN
|
||||
ngx.header.content_type = consts.BLOCK_CONTENT_TYPE
|
||||
ngx.say(fmt(consts.BLOCK_CONTENT_FORMAT, ngx.status, t["event_id"]))
|
||||
|
||||
return ngx.exit(ngx.status)
|
||||
else
|
||||
local err = log_fmt("unknown action from t1k server: %s", action)
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,27 @@
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local fmt = string.format
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local WARN = ngx.WARN
|
||||
local DEBUG = ngx.DEBUG
|
||||
|
||||
function _M.fmt(formatstring, ...)
|
||||
return fmt("lua-resty-t1k: " .. formatstring, ...)
|
||||
end
|
||||
|
||||
function _M.err_fmt(formatstring, ...)
|
||||
return ERR, _M.fmt(formatstring, ...)
|
||||
end
|
||||
|
||||
function _M.warn_fmt(formatstring, ...)
|
||||
return WARN, _M.fmt(formatstring, ...)
|
||||
end
|
||||
|
||||
function _M.debug_fmt(formatstring, ...)
|
||||
return DEBUG, _M.fmt(formatstring, ...)
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,365 @@
|
||||
local bit = require "bit"
|
||||
|
||||
local buffer = require "resty.t1k.buffer"
|
||||
local consts = require "resty.t1k.constants"
|
||||
local file = require "resty.t1k.file"
|
||||
local log = require "resty.t1k.log"
|
||||
local utils = require "resty.t1k.utils"
|
||||
local uuid = require "resty.t1k.uuid"
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0',
|
||||
}
|
||||
|
||||
local bor = bit.bor
|
||||
local byte = string.byte
|
||||
local char = string.char
|
||||
local fmt = string.format
|
||||
local sub = string.sub
|
||||
local concat = table.concat
|
||||
|
||||
local ngx = ngx
|
||||
local nlog = ngx.log
|
||||
local ngx_req = ngx.req
|
||||
local ngx_socket = ngx.socket
|
||||
local ngx_var = ngx.var
|
||||
|
||||
local warn_fmt = log.warn_fmt
|
||||
local debug_fmt = log.debug_fmt
|
||||
|
||||
local KEY_EXTRA_UUID = "UUID"
|
||||
local KEY_EXTRA_LOCAL_ADDR = "LocalAddr"
|
||||
local KEY_EXTRA_LOCAL_PORT = "LocalPort"
|
||||
local KEY_EXTRA_REMOTE_ADDR = "RemoteAddr"
|
||||
local KEY_EXTRA_REMOTE_PORT = "RemotePort"
|
||||
local KEY_EXTRA_SCHEME = "Scheme"
|
||||
local KEY_EXTRA_SERVER_NAME = "ServerName"
|
||||
local KEY_EXTRA_PROXY_NAME = "ProxyName"
|
||||
local KEY_EXTRA_REQ_BEGIN_TIME = "ReqBeginTime"
|
||||
local KEY_EXTRA_HAS_RSP_IF_OK = "HasRspIfOK"
|
||||
local KEY_EXTRA_HAS_RSP_IF_BLOCK = "HasRspIfBlock"
|
||||
|
||||
local TAG_HEAD_WITH_MASK_FIRST = bor(consts.TAG_HEAD, consts.MASK_FIRST)
|
||||
local TAG_EXTRA_WITH_MASK_LAST = bor(consts.TAG_EXTRA, consts.MASK_LAST)
|
||||
|
||||
local T1K_PROTO = "Proto:2\n"
|
||||
local T1K_PROTO_DATA = fmt("%s%s%s", char(consts.TAG_VERSION), utils.int_to_char_length(#T1K_PROTO), T1K_PROTO)
|
||||
|
||||
local function read_request_body(opt_req_body_size)
|
||||
local ok, err
|
||||
local req_body, req_body_size
|
||||
|
||||
local content_length = tonumber(ngx_var.http_content_length) or 0
|
||||
local transfer_encoding = ngx_var.http_transfer_encoding
|
||||
if content_length == 0 and not transfer_encoding then
|
||||
return true, nil, nil
|
||||
end
|
||||
|
||||
ngx_req.read_body()
|
||||
req_body = ngx_req.get_body_data()
|
||||
if req_body then
|
||||
req_body_size = #req_body
|
||||
if req_body_size > opt_req_body_size then
|
||||
nlog(debug_fmt("request body is too long: %d bytes, cut to %d bytes", req_body_size, opt_req_body_size))
|
||||
req_body = sub(req_body, 1, opt_req_body_size)
|
||||
end
|
||||
|
||||
return true, nil, req_body
|
||||
end
|
||||
|
||||
local path = ngx_req.get_body_file()
|
||||
if not path then
|
||||
return true, nil, nil
|
||||
end
|
||||
|
||||
ok, err, req_body = file.read(path, opt_req_body_size)
|
||||
if not ok then
|
||||
err = fmt("failed to read temporary file %s: %s", path, err)
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
return true, nil, req_body
|
||||
end
|
||||
|
||||
local function get_remote_addr(remote_addr_var, remote_addr_idx)
|
||||
local addr
|
||||
if remote_addr_var then
|
||||
addr = utils.get_indexed_element(ngx_var[remote_addr_var], remote_addr_idx)
|
||||
end
|
||||
return addr or ngx_var.remote_addr
|
||||
end
|
||||
|
||||
local function parse_v(v)
|
||||
if type(v) == "table" then
|
||||
return concat(v, ", ")
|
||||
end
|
||||
return tostring(v)
|
||||
end
|
||||
|
||||
local function build_header()
|
||||
local http_version = ngx_req.http_version()
|
||||
if http_version < 2.0 then
|
||||
return true, nil, ngx_req.raw_header()
|
||||
end
|
||||
|
||||
local headers, err = ngx_req.get_headers(0, true)
|
||||
if err then
|
||||
err = fmt("failed to call ngx_req.get_headers: %s", err)
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
local buf = buffer:new()
|
||||
buf:add(fmt("%s %s HTTP/%.1f\r\n", ngx_req.get_method(), ngx_var.request_uri, http_version))
|
||||
|
||||
for k, v in pairs(headers) do
|
||||
buf:add(fmt("%s: %s\r\n", k, parse_v(v)))
|
||||
end
|
||||
buf:add("\r\n")
|
||||
|
||||
return true, nil, buf
|
||||
end
|
||||
|
||||
local function build_body(opts)
|
||||
local ok, err
|
||||
local body
|
||||
|
||||
local req_body_size = opts.req_body_size * 1024
|
||||
ok, err, body = read_request_body(req_body_size)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
return true, nil, body
|
||||
end
|
||||
|
||||
local function build_extra(opts)
|
||||
local err
|
||||
|
||||
local src_ip = get_remote_addr(opts.remote_addr_var, opts.remote_addr_idx)
|
||||
if not src_ip then
|
||||
err = fmt("failed to get remote_addr, var: %s, idx %d", opts.remote_addr_var, opts.remote_addr_idx)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local src_port = ngx_var.remote_port
|
||||
if not src_port then
|
||||
err = "failed to get ngx_var.remote_port"
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
local local_ip = ngx_var.server_addr
|
||||
if not local_ip then
|
||||
err = "failed to get ngx_var.server_addr"
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
local local_port = ngx_var.server_port
|
||||
if not local_port then
|
||||
err = "failed to get ngx_var.server_port"
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
local extra = buffer:new({
|
||||
KEY_EXTRA_UUID, ":", uuid.generate_v4(), "\n",
|
||||
KEY_EXTRA_REMOTE_ADDR, ":", src_ip, "\n",
|
||||
KEY_EXTRA_REMOTE_PORT, ":", src_port, "\n",
|
||||
KEY_EXTRA_LOCAL_ADDR, ":", local_ip, "\n",
|
||||
KEY_EXTRA_LOCAL_PORT, ":", local_port, "\n",
|
||||
KEY_EXTRA_SCHEME, ":", ngx_var.scheme, "\n",
|
||||
KEY_EXTRA_SERVER_NAME, ":", ngx_var.server_name, "\n",
|
||||
KEY_EXTRA_PROXY_NAME, ":", ngx_var.hostname, "\n",
|
||||
KEY_EXTRA_REQ_BEGIN_TIME, ":", fmt("%.0f", ngx_req.start_time() * 1000000), "\n",
|
||||
KEY_EXTRA_HAS_RSP_IF_OK, ":n\n",
|
||||
KEY_EXTRA_HAS_RSP_IF_BLOCK, ":n\n"
|
||||
})
|
||||
|
||||
return true, nil, extra
|
||||
end
|
||||
|
||||
local function do_send(sock, data)
|
||||
local ok, err = sock:send(data)
|
||||
if not ok then
|
||||
return ok, err
|
||||
end
|
||||
return true, nil
|
||||
end
|
||||
|
||||
local function receive_data(s, srv)
|
||||
local t = {}
|
||||
local ft = true
|
||||
local finished
|
||||
|
||||
repeat
|
||||
local err
|
||||
local tag, length, packet, rsp_body
|
||||
|
||||
packet, err = s:receive(consts.T1K_HEADER_SIZE)
|
||||
if err then
|
||||
err = fmt("failed to receive info packet from t1k server %s: %s", srv, err)
|
||||
return nil, err, nil
|
||||
end
|
||||
if not packet then
|
||||
err = fmt("empty packet from t1k server %s", srv)
|
||||
return nil, err, nil
|
||||
end
|
||||
|
||||
if ft then
|
||||
if not utils.is_mask_first(byte(packet, 1, 1)) then
|
||||
err = fmt("first packet is not MASK_FIRST from t1k server %s", srv)
|
||||
return nil, err, nil
|
||||
end
|
||||
ft = false
|
||||
end
|
||||
|
||||
finished, tag, length = utils.packet_parser(packet)
|
||||
if length > 0 then
|
||||
rsp_body, err = s:receive(length)
|
||||
if not rsp_body or #rsp_body ~= length then
|
||||
err = fmt("failed to receive payload from t1k server %s: %s", srv, err)
|
||||
return nil, err, nil
|
||||
end
|
||||
t[tag] = rsp_body
|
||||
end
|
||||
|
||||
until (finished)
|
||||
|
||||
return true, nil, t
|
||||
end
|
||||
|
||||
local function get_socket(opts)
|
||||
local ok, err
|
||||
local count, sock, server
|
||||
|
||||
sock, err = ngx_socket.tcp()
|
||||
if not sock then
|
||||
err = fmt("failed to create socket: %s", err)
|
||||
return nil, err, nil, nil
|
||||
end
|
||||
|
||||
sock:settimeouts(opts.connect_timeout, opts.send_timeout, opts.read_timeout)
|
||||
|
||||
if opts.uds then
|
||||
server = opts.host
|
||||
ok, err = sock:connect(opts.host)
|
||||
else
|
||||
server = fmt("%s:%d", opts.host, opts.port)
|
||||
ok, err = sock:connect(opts.host, opts.port)
|
||||
end
|
||||
if not ok then
|
||||
sock:close()
|
||||
err = fmt("failed to connect to t1k server %s: %s", server, err)
|
||||
return ok, err, nil, nil
|
||||
end
|
||||
nlog(debug_fmt("successfully connected to t1k server %s", server))
|
||||
|
||||
count, err = sock:getreusedtimes()
|
||||
if not count then
|
||||
nlog(warn_fmt("failed to get reused times from t1k server %s: %s", server, err))
|
||||
end
|
||||
|
||||
if count == 0 then
|
||||
ok, err = sock:setoption("keepalive", true)
|
||||
if not ok then
|
||||
nlog(warn_fmt("failed to set keepalive for t1k server %s: %s", server, err))
|
||||
end
|
||||
ok, err = sock:setoption("reuseaddr", true)
|
||||
if not ok then
|
||||
nlog(warn_fmt("failed to set reuseaddr for t1k server %s: %s", server, err))
|
||||
end
|
||||
ok, err = sock:setoption("tcp-nodelay", true)
|
||||
if not ok then
|
||||
nlog(warn_fmt("failed to set tcp-nodelay for t1k server %s: %s", server, err))
|
||||
end
|
||||
end
|
||||
|
||||
return true, nil, sock, server
|
||||
end
|
||||
|
||||
local function do_socket(opts, header, body, extra)
|
||||
local ok, err
|
||||
local t, sock, server
|
||||
|
||||
ok, err, sock, server = get_socket(opts)
|
||||
if not ok then
|
||||
err = fmt("failed to get socket: %s", err)
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err = do_send(sock, { char(TAG_HEAD_WITH_MASK_FIRST), utils.int_to_char_length(header:len()), header })
|
||||
if not ok then
|
||||
sock:close()
|
||||
err = fmt("failed to send header data to t1k server %s: %s", server, err)
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
if body ~= nil then
|
||||
ok, err = do_send(sock, { char(consts.TAG_BODY), utils.int_to_char_length(body:len()), body })
|
||||
if not ok then
|
||||
sock:close()
|
||||
err = fmt("failed to send body data to t1k server %s: %s", server, err)
|
||||
return ok, err, nil
|
||||
end
|
||||
end
|
||||
|
||||
ok, err = do_send(sock, { T1K_PROTO_DATA, char(TAG_EXTRA_WITH_MASK_LAST), utils.int_to_char_length(extra:len()), extra })
|
||||
if not ok then
|
||||
sock:close()
|
||||
err = fmt("failed to send extra data to t1k server %s: %s", server, err)
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err, t = receive_data(sock, server)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err = sock:setkeepalive(opts.keepalive_timeout, opts.keepalive_size)
|
||||
if not ok then
|
||||
nlog(warn_fmt("failed to set keepalive: %s", err))
|
||||
sock:close()
|
||||
end
|
||||
|
||||
return true, nil, t
|
||||
end
|
||||
|
||||
function _M.do_request(opts)
|
||||
local ok, err
|
||||
local header, body, extra, t
|
||||
|
||||
ok, err, header = build_header(opts)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err, body = build_body(opts)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err, extra = build_extra(opts)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
ok, err, t = do_socket(opts, header, body, extra)
|
||||
if not ok then
|
||||
return ok, err, nil
|
||||
end
|
||||
|
||||
if opts.mode == consts.MODE_BLOCK then
|
||||
local extra_header = t[consts.TAG_EXTRA_HEADER]
|
||||
if extra_header then
|
||||
ngx.ctx.t1k_extra_header = extra_header
|
||||
end
|
||||
end
|
||||
|
||||
local result = {
|
||||
action = t[consts.TAG_HEAD],
|
||||
status = t[consts.TAG_BODY],
|
||||
event_id = utils.get_event_id(t[consts.TAG_EXTRA_BODY]),
|
||||
}
|
||||
|
||||
return true, nil, result
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,123 @@
|
||||
local consts = require "resty.t1k.constants"
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local band = bit.band
|
||||
local bnot = bit.bnot
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
|
||||
local abs = math.abs
|
||||
local char = string.char
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_re = ngx.re
|
||||
local re_match = ngx_re.match
|
||||
local re_gmatch = ngx_re.gmatch
|
||||
local re_gsub = ngx_re.gsub
|
||||
|
||||
local NOT_MASK_FIRST = bnot(consts.MASK_FIRST)
|
||||
local NOT_MASK_LAST = bnot(consts.MASK_LAST)
|
||||
|
||||
function _M.int_to_char_length(x)
|
||||
return char(band(x, 0xff)) .. char(band(rshift(x, 8), 0xff)) ..
|
||||
char(band(rshift(x, 16), 0xff)) .. char(band(rshift(x, 24), 0xff))
|
||||
end
|
||||
|
||||
function _M.char_to_int_length(l)
|
||||
return l:byte(1, 1) + lshift(l:byte(2, 2), 8) + lshift(l:byte(3, 3), 16) + lshift(l:byte(4, 4), 24)
|
||||
end
|
||||
|
||||
function _M.is_mask_first(b)
|
||||
return band(b, consts.MASK_FIRST) == consts.MASK_FIRST
|
||||
end
|
||||
|
||||
function _M.is_mask_last(b)
|
||||
return band(b, consts.MASK_LAST) == consts.MASK_LAST
|
||||
end
|
||||
|
||||
local function tag_parser(tag)
|
||||
return band(band(tag, NOT_MASK_FIRST), NOT_MASK_LAST)
|
||||
end
|
||||
|
||||
function _M.packet_parser(header)
|
||||
if #header ~= consts.T1K_HEADER_SIZE then
|
||||
return true, nil, 0
|
||||
end
|
||||
|
||||
local fb = header:byte(1, 1)
|
||||
local finished = _M.is_mask_last(fb)
|
||||
local tag = tag_parser(fb)
|
||||
local length = _M.char_to_int_length(header:sub(2, 5))
|
||||
|
||||
return finished, tag, length
|
||||
end
|
||||
|
||||
function _M.starts_with(str, start)
|
||||
return str:sub(1, #start) == start
|
||||
end
|
||||
|
||||
function _M.to_var_idx(o)
|
||||
local var = o
|
||||
local idx
|
||||
|
||||
local _, p = o:find(":")
|
||||
if p then
|
||||
var = o:sub(1, p - 1)
|
||||
idx = tonumber(o:sub(p + 1))
|
||||
end
|
||||
|
||||
var = re_gsub(var:lower(), "-", "_")
|
||||
if not _M.starts_with(var, consts.NGX_HTTP_HEADER_PREFIX) then
|
||||
var = consts.NGX_HTTP_HEADER_PREFIX .. var
|
||||
end
|
||||
|
||||
return var, idx
|
||||
end
|
||||
|
||||
function _M.get_indexed_element(str, idx)
|
||||
if not str or not idx or idx == 0 then
|
||||
return str
|
||||
end
|
||||
|
||||
local it, err = re_gmatch(str, [[([^,\s]+)]], "jo")
|
||||
if err then
|
||||
return nil
|
||||
end
|
||||
|
||||
local t = {}
|
||||
for m, e in it do
|
||||
if e then
|
||||
return nil
|
||||
end
|
||||
table.insert(t, m[1])
|
||||
end
|
||||
|
||||
local len = #t
|
||||
if len < abs(idx) then
|
||||
return nil
|
||||
end
|
||||
|
||||
return t[idx > 0 and idx or (len + idx + 1)]
|
||||
end
|
||||
|
||||
function _M.get_event_id(str)
|
||||
if not str then
|
||||
return nil
|
||||
end
|
||||
|
||||
local m, err = re_match(str, [[<!-- event_id: ([A-Za-z0-9]+)(?: TYPE: [a-zA-Z])? -->]], "jo")
|
||||
if err then
|
||||
return nil
|
||||
end
|
||||
|
||||
if m then
|
||||
return m[1]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,100 @@
|
||||
local bit = require "bit"
|
||||
local ffi = require "ffi"
|
||||
|
||||
local log = require "resty.t1k.log"
|
||||
|
||||
local _M = {
|
||||
_VERSION = '1.0.0'
|
||||
}
|
||||
|
||||
local C = ffi.C
|
||||
local N_BYTES = 32
|
||||
local random = math.random
|
||||
|
||||
local nlog = ngx.log
|
||||
|
||||
local warn_fmt = log.warn_fmt
|
||||
|
||||
ffi.cdef [[
|
||||
int RAND_bytes(unsigned char *buf, int num);
|
||||
]]
|
||||
|
||||
local function _rand_bytes(buf, len)
|
||||
return C.RAND_bytes(buf, len)
|
||||
end
|
||||
|
||||
local function rand_bytes(len)
|
||||
local buf = ffi.new("char[?]", len)
|
||||
local ok, ret = pcall(_rand_bytes, buf, len)
|
||||
|
||||
if not ok or ret ~= 1 then
|
||||
nlog(warn_fmt("call RAND_bytes failed: %s", ret))
|
||||
return nil
|
||||
end
|
||||
|
||||
return ffi.string(buf, len)
|
||||
end
|
||||
|
||||
do
|
||||
local band = bit.band
|
||||
local bor = bit.bor
|
||||
local tohex = bit.tohex
|
||||
|
||||
local fmt = string.format
|
||||
local byte = string.byte
|
||||
|
||||
function _M.generate_v4()
|
||||
local bytes = rand_bytes(N_BYTES)
|
||||
|
||||
-- fallback to math.random based method
|
||||
if not bytes then
|
||||
return (fmt('%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
|
||||
tohex(bor(band(random(0, 255), 0x0F), 0x40), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
|
||||
tohex(bor(band(random(0, 255), 0x3F), 0x80), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2),
|
||||
tohex(random(0, 255), 2)))
|
||||
end
|
||||
|
||||
return fmt('%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',
|
||||
tohex(byte(bytes, 1, 2), 2),
|
||||
tohex(byte(bytes, 3, 4), 2),
|
||||
tohex(byte(bytes, 5, 6), 2),
|
||||
tohex(byte(bytes, 7, 8), 2),
|
||||
|
||||
tohex(byte(bytes, 9, 10), 2),
|
||||
tohex(byte(bytes, 11, 12), 2),
|
||||
|
||||
tohex(bor(band(byte(bytes, 13, 14), 0x0F), 0x40), 2),
|
||||
tohex(byte(bytes, 15, 16), 2),
|
||||
|
||||
tohex(bor(band(byte(bytes, 17, 18), 0x3F), 0x80), 2),
|
||||
tohex(byte(bytes, 19, 20), 2),
|
||||
|
||||
tohex(byte(bytes, 21, 22), 2),
|
||||
tohex(byte(bytes, 23, 24), 2),
|
||||
tohex(byte(bytes, 25, 26), 2),
|
||||
tohex(byte(bytes, 27, 28), 2),
|
||||
tohex(byte(bytes, 29, 30), 2),
|
||||
tohex(byte(bytes, 31, 32), 2))
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(_M, {
|
||||
__call = _M.generate_v4
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k-main"
|
||||
version = "0-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
branch = "main",
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.0.0-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.0.0"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.0.1-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.0.1"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.0.2-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.0.2"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.0.3-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.0.3"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.0-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.0"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.1-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.1"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.2-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.2"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.3-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.3"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.4-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.4"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package = "lua-resty-t1k"
|
||||
version = "1.1.5-0"
|
||||
source = {
|
||||
url = "git://github.com/chaitin/lua-resty-t1k",
|
||||
tag = "v1.1.5"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Lua implementation of the T1K protocol for Chaitin SafeLine Web Application Firewall",
|
||||
detailed = [[
|
||||
Check https://waf-ce.chaitin.cn/ for more information about Chaitin SafeLine Web Application Firewall.
|
||||
]],
|
||||
homepage = "https://github.com/chaitin/lua-resty-t1k",
|
||||
license = "Apache License 2.0",
|
||||
maintainer = "Xudong Wang <xudong.wang@chaitin.com>"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.t1k"] = "lib/resty/t1k.lua",
|
||||
["resty.t1k.buffer"] = "lib/resty/t1k/buffer.lua",
|
||||
["resty.t1k.constants"] = "lib/resty/t1k/constants.lua",
|
||||
["resty.t1k.file"] = "lib/resty/t1k/file.lua",
|
||||
["resty.t1k.filter"] = "lib/resty/t1k/filter.lua",
|
||||
["resty.t1k.handler"] = "lib/resty/t1k/handler.lua",
|
||||
["resty.t1k.log"] = "lib/resty/t1k/log.lua",
|
||||
["resty.t1k.request"] = "lib/resty/t1k/request.lua",
|
||||
["resty.t1k.utils"] = "lib/resty/t1k/utils.lua",
|
||||
["resty.t1k.uuid"] = "lib/resty/t1k/uuid.lua",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: buffer add
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local buffer = require "resty.t1k.buffer"
|
||||
local b = buffer:new()
|
||||
b:add("hello")
|
||||
b:add(" ")
|
||||
b:add("world")
|
||||
b:add("!")
|
||||
ngx.say(b[1], b[2], b[3], b[4])
|
||||
ngx.say(b:len())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
hello world!
|
||||
12
|
||||
--- no_error_log
|
||||
[error]
|
||||
@@ -0,0 +1,136 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: read full file
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local file = require "resty.t1k.file"
|
||||
local path = ngx.var.document_root .. "/foo.bar"
|
||||
local ok, err, content = file.read(path, 2 ^ 15)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
ngx.print(table.concat(content))
|
||||
}
|
||||
}
|
||||
--- user_files eval
|
||||
[
|
||||
["foo.bar" => "a" x (2 ** 14) ],
|
||||
]
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
"a" x (2 ** 14)
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 2: read partial file
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local file = require "resty.t1k.file"
|
||||
local path = ngx.var.document_root .. "/foo.bar"
|
||||
local ok, err, content = file.read(path, 2 ^ 13)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
ngx.print(table.concat(content))
|
||||
}
|
||||
}
|
||||
--- user_files eval
|
||||
[
|
||||
["foo.bar" => "a" x (2 ** 14) ],
|
||||
]
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
"a" x (2 ** 13)
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 3: read negative bytes
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local file = require "resty.t1k.file"
|
||||
local path = ngx.var.document_root .. "/foo.bar"
|
||||
local ok, err, content = file.read(path, -1)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
ngx.print(table.concat(content))
|
||||
}
|
||||
}
|
||||
--- user_files
|
||||
>>> foo.bar
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
""
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 4: read empty file
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local file = require "resty.t1k.file"
|
||||
local path = ngx.var.document_root .. "/foo.bar"
|
||||
local ok, err, content = file.read(path, 1)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
ngx.print(table.concat(content))
|
||||
}
|
||||
}
|
||||
--- user_files
|
||||
>>> foo.bar
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
""
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 5: read non-existent file
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local file = require "resty.t1k.file"
|
||||
local ok, err, buffer = file.read("/opt/non_existent_file", 0)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
/opt/non_existent_file: No such file or directory
|
||||
--- no_error_log
|
||||
[error]
|
||||
@@ -0,0 +1,39 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 5);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: do_header_filter
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
ngx.ctx.t1k_extra_header = "k1:v1\nk2:v2\nk3:v3\n"
|
||||
}
|
||||
|
||||
header_filter_by_lua_block {
|
||||
local filter = require "resty.t1k.filter"
|
||||
filter.do_header_filter()
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("hi")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_headers
|
||||
k1: v1
|
||||
k2: v2
|
||||
k3: v3
|
||||
--- no_error_log
|
||||
[error]
|
||||
@@ -0,0 +1,138 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: handle passed action
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local handler = require "resty.t1k.handler"
|
||||
|
||||
local t = {
|
||||
action = ".",
|
||||
}
|
||||
|
||||
local ok, err = handler.handle(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 2: handle blocked action
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local handler = require "resty.t1k.handler"
|
||||
|
||||
local t = {
|
||||
action = "?",
|
||||
status = 405,
|
||||
event_id = "c0c039a7c348486eaffd9e2f9846b66b",
|
||||
}
|
||||
|
||||
local ok, err = handler.handle(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
}
|
||||
|
||||
header_filter_by_lua_block {
|
||||
local filter = require "resty.t1k.filter"
|
||||
filter.do_header_filter()
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
{"code": 405, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c0c039a7c348486eaffd9e2f9846b66b"}
|
||||
--- error_code eval
|
||||
"405"
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 3: handle unknown action
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local handler = require "resty.t1k.handler"
|
||||
|
||||
local t = {
|
||||
action = "~"
|
||||
}
|
||||
|
||||
local ok, err = handler.handle(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_log
|
||||
lua-resty-t1k: unknown action from t1k server: ~
|
||||
|
||||
|
||||
|
||||
=== TEST 4: handle nil result
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local handler = require "resty.t1k.handler"
|
||||
|
||||
local ok, err = handler.handle(nil)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_log
|
||||
lua-resty-t1k: invalid result type: nil
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 2 + 2);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: err_fmt
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local log = require "resty.t1k.log"
|
||||
ngx.log(log.err_fmt("%s - %04d - %.4f", "test", 1, 1))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: test - 0001 - 1.0000
|
||||
--- log_level: error
|
||||
|
||||
|
||||
|
||||
=== TEST 2: warn_fmt
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local log = require "resty.t1k.log"
|
||||
ngx.log(log.warn_fmt("%s - %04d - %.4f", "test", 1, 1))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: test - 0001 - 1.0000
|
||||
--- log_level: warn
|
||||
|
||||
|
||||
|
||||
=== TEST 3: debug_fmt
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local log = require "resty.t1k.log"
|
||||
ngx.log(log.debug_fmt("%s - %04d - %.4f", "test", 1, 1))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: test - 0001 - 1.0000
|
||||
--- log_level: debug
|
||||
@@ -0,0 +1,169 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3 + 1);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: do_access nil option
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local ok, err, _ = t1k.do_access(nil)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[debug]
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 2: do_access disabled
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local t = {
|
||||
mode = "off",
|
||||
}
|
||||
|
||||
local ok, err, _ = t1k.do_access(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: t1k is not enabled
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 3: do_access invalid mode
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local t = {
|
||||
mode = "invalid",
|
||||
}
|
||||
|
||||
local ok, err, _ = t1k.do_access(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
passed
|
||||
--- error_log
|
||||
lua-resty-t1k: invalid t1k mode: invalid
|
||||
|
||||
|
||||
|
||||
=== TEST 4: do_access invalid host
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local t = {
|
||||
mode = "block"
|
||||
}
|
||||
|
||||
local ok, err, _ = t1k.do_access(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_log
|
||||
lua-resty-t1k: invalid t1k host: nil
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 5: do_access invalid port
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local t1k = require "resty.t1k"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
|
||||
local ok, err, _ = t1k.do_access(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
content_by_lua_block {
|
||||
ngx.say("passed")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_log
|
||||
lua-resty-t1k: invalid t1k port: nil
|
||||
--- log_level: debug
|
||||
@@ -0,0 +1,640 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3 + 14);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: do_request blocked
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
ngx.say(result["status"])
|
||||
ngx.say(result["event_id"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
?
|
||||
405
|
||||
c0c039a7c348486eaffd9e2f9846b66b
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 2: do_request blocked with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
ngx.say(result["status"])
|
||||
ngx.say(result["event_id"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
?
|
||||
405
|
||||
c0c039a7c348486eaffd9e2f9846b66b
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 3: do_request passed
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00."
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
.
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 4: do_request passed with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00."
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
.
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 5: do_request trim request body
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00."
|
||||
--- request
|
||||
GET /t
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
--- response_body
|
||||
.
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: request body is too long: 123 bytes, cut to 64 bytes
|
||||
lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 6: do_request trim request body with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00."
|
||||
--- request
|
||||
GET /t
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
--- response_body
|
||||
.
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: request body is too long: 123 bytes, cut to 64 bytes
|
||||
lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 7: do_request refuse connection
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", result)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
result: nil
|
||||
--- error_log
|
||||
failed to connect to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 8: do_request refuse connection with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", result)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
result: nil
|
||||
--- error_log
|
||||
failed to connect to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 9: do_request timeout
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 100,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", result)
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply_delay: 200ms
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
result: nil
|
||||
--- error_log
|
||||
failed to receive info packet from t1k server 127.0.0.1:18000: timeout
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 10: do_request timeout with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 100,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", result)
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply_delay: 200ms
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- response_body
|
||||
result: nil
|
||||
--- error_log
|
||||
failed to receive info packet from t1k server unix:t1k.sock: timeout
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 11: do_request invalid action
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("action: ", result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00~"
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
action: ~
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
successfully connected to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 12: do_request invalid action with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 0.0625,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say("action: ", result["action"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply eval
|
||||
"\xc1\x01\x00\x00\x00~"
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
action: ~
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
successfully connected to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 13: do_request remote address
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
access_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("ngx.var.http_x_real_ip or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_real_ip) or ngx.var.remote_addr)
|
||||
ngx.say("ngx.var.http_x_forwarded_for: 2 or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 2) or ngx.var.remote_addr)
|
||||
ngx.say("ngx.var.http_x_forwarded_for: -2 or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -2) or ngx.var.remote_addr)
|
||||
ngx.say("ngx.var.http_non_existent_header or ngx.var.remote_addr is ", utils.get_indexed_element(ngx.var.http_non_existent_header) or ngx.var.remote_addr)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- more_headers
|
||||
X-Forwarded-For: 1.1.1.1, 2.2.2.2, 2001:db8:3333:4444:5555:6666:7777:8888, 3.3.3.3
|
||||
X-Real-IP: 100.100.100.100
|
||||
--- response_body
|
||||
ngx.var.http_x_real_ip or ngx.var.remote_addr is 100.100.100.100
|
||||
ngx.var.http_x_forwarded_for: 2 or ngx.var.remote_addr is 2.2.2.2
|
||||
ngx.var.http_x_forwarded_for: -2 or ngx.var.remote_addr is 2001:db8:3333:4444:5555:6666:7777:8888
|
||||
ngx.var.http_non_existent_header or ngx.var.remote_addr is 127.0.0.1
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 14: do_request http2
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "127.0.0.1",
|
||||
port = 18000,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
ngx.say(result["status"])
|
||||
ngx.say(result["event_id"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 18000
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- http2
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- tcp_query eval
|
||||
qr/.*HTTP\/2.0.*/
|
||||
--- response_body
|
||||
?
|
||||
405
|
||||
c0c039a7c348486eaffd9e2f9846b66b
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server 127.0.0.1:18000
|
||||
--- log_level: debug
|
||||
|
||||
|
||||
|
||||
=== TEST 15: do_request http2 with unix domain socket
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local request = require "resty.t1k.request"
|
||||
|
||||
local t = {
|
||||
mode = "block",
|
||||
host = "unix:t1k.sock",
|
||||
uds = true,
|
||||
connect_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
req_body_size = 1024,
|
||||
keepalive_size = 16,
|
||||
keepalive_timeout = 10000,
|
||||
}
|
||||
|
||||
local ok, err, result = request.do_request(t)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
end
|
||||
|
||||
ngx.say(result["action"])
|
||||
ngx.say(result["status"])
|
||||
ngx.say(result["event_id"])
|
||||
}
|
||||
}
|
||||
--- tcp_listen: t1k.sock
|
||||
--- tcp_reply eval
|
||||
"\x41\x01\x00\x00\x00?\x02\x03\x00\x00\x00405\xa4\x33\x00\x00\x00<!-- event_id: c0c039a7c348486eaffd9e2f9846b66b -->"
|
||||
--- http2
|
||||
--- request
|
||||
GET /t/shell.php
|
||||
--- tcp_query eval
|
||||
qr/.*HTTP\/2.0.*/
|
||||
--- response_body
|
||||
?
|
||||
405
|
||||
c0c039a7c348486eaffd9e2f9846b66b
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- error_log
|
||||
lua-resty-t1k: successfully connected to t1k server unix:t1k.sock
|
||||
--- log_level: debug
|
||||
@@ -0,0 +1,264 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: int_to_char_length
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("255 to char length: ", utils.int_to_char_length(255))
|
||||
ngx.print("16777216 to char length: ", utils.int_to_char_length(16777216))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
"255 to char length: \x{ff}\x{00}\x{00}\x{00}
|
||||
16777216 to char length: \x{00}\x{00}\x{00}\x{01}"
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 2: int_to_char_length
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("0xff 0x00 0x00 0x00 to int length: ", utils.char_to_int_length("\xff\x00\x00\x00"))
|
||||
ngx.say("0x00 0x00 0x00 0x01 to int length: ", utils.char_to_int_length("\x00\x00\x00\x01"))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
0xff 0x00 0x00 0x00 to int length: 255
|
||||
0x00 0x00 0x00 0x01 to int length: 16777216
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 3: is_mask_first
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("0x30 is MASK FIRST: ", utils.is_mask_first(0x30))
|
||||
ngx.say("0x41 is MASK FIRST: ", utils.is_mask_first(0x41))
|
||||
ngx.say("0xc0 is MASK FIRST: ", utils.is_mask_first(0xc0))
|
||||
ngx.say("0xc1 is MASK FIRST: ", utils.is_mask_first(0xc1))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
0x30 is MASK FIRST: false
|
||||
0x41 is MASK FIRST: true
|
||||
0xc0 is MASK FIRST: true
|
||||
0xc1 is MASK FIRST: true
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 4: is_mask_last
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("0x41 is MASK LAST: ", utils.is_mask_last(65))
|
||||
ngx.say("0x80 is MASK LAST: ", utils.is_mask_last(128))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
0x41 is MASK LAST: false
|
||||
0x80 is MASK LAST: true
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 5: packet_parser unfinished
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
local finished, tag, length = utils.packet_parser("\x41\x59\x00\x00\x00")
|
||||
ngx.say("finished: ", finished)
|
||||
ngx.say("tag == TAG_HEAD: ", tag == 1)
|
||||
ngx.say("length: ", 89)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
finished: false
|
||||
tag == TAG_HEAD: true
|
||||
length: 89
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 6: packet_parser finished
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
local finished, tag, length = utils.packet_parser("\xa0\x08\x00\x00\x00")
|
||||
ngx.say("finished: ", finished)
|
||||
ngx.say("tag == TAG_VERSION: ", tag == 32)
|
||||
ngx.say("length: ", 8)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
finished: true
|
||||
tag == TAG_VERSION: true
|
||||
length: 8
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 7: starts_with
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("http://www.baidu.com starts with \"http\": ", utils.starts_with("http://www.baidu.com", "http"))
|
||||
ngx.say("http://www.baidu.com starts with \"https\": ", utils.starts_with("http://www.baidu.com", "https"))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
http://www.baidu.com starts with "http": true
|
||||
http://www.baidu.com starts with "https": false
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 8: to_var_idx
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
local fmt = string.format
|
||||
ngx.say(fmt("http_x_real_ip is %s and %s", utils.to_var_idx("http_x_real_ip")))
|
||||
ngx.say(fmt("X_REAL_IP is %s and %s", utils.to_var_idx("X_REAL_IP")))
|
||||
ngx.say(fmt("X-Forwarded-For: 1 is %s and %d", utils.to_var_idx("X-Forwarded-For: 1")))
|
||||
ngx.say(fmt("X-Forwarded-For: -1 is %s and %d", utils.to_var_idx("X-Forwarded-For: -1")))
|
||||
ngx.say(fmt("X-FORWARDED-FOR:100 is %s and %d", utils.to_var_idx("X-FORWARDED-FOR:-100")))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
http_x_real_ip is http_x_real_ip and nil
|
||||
X_REAL_IP is http_x_real_ip and nil
|
||||
X-Forwarded-For: 1 is http_x_forwarded_for and 1
|
||||
X-Forwarded-For: -1 is http_x_forwarded_for and -1
|
||||
X-FORWARDED-FOR:100 is http_x_forwarded_for and -100
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 9: get_indexed_element
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say("X-Forwarded-For: 1 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 1))
|
||||
ngx.say("X-Forwarded-For: 2 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 2))
|
||||
ngx.say("X-Forwarded-For: 3 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 3))
|
||||
ngx.say("X-Forwarded-For: 4 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 4))
|
||||
ngx.say("X-Forwarded-For: 5 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 5))
|
||||
ngx.say("X-Forwarded-For: -1 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -1))
|
||||
ngx.say("X-Forwarded-For: -2 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -2))
|
||||
ngx.say("X-Forwarded-For: -3 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -3))
|
||||
ngx.say("X-Forwarded-For: -4 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -4))
|
||||
ngx.say("X-Forwarded-For: -5 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, -5))
|
||||
ngx.say("X-Forwarded-For: 0 is ", utils.get_indexed_element(ngx.var.http_x_forwarded_for, 0))
|
||||
ngx.say("X-Non-Existent-Header is ", utils.get_indexed_element(ngx.var.http_non_existent_header))
|
||||
ngx.say("X-Real-IP is ", utils.get_indexed_element(ngx.var.http_x_real_ip))
|
||||
ngx.say("X-Real-IP: 1 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, 1))
|
||||
ngx.say("X-Real-IP: 2 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, 2))
|
||||
ngx.say("X-Real-IP: -1 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, -1))
|
||||
ngx.say("X-Real-IP: -2 is ", utils.get_indexed_element(ngx.var.http_x_real_ip, -2))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- more_headers
|
||||
X-Forwarded-For: 1.1.1.1, 2.2.2.2
|
||||
X-Forwarded-For: 3.3.3.3, 4.4.4.4
|
||||
X-Real-IP: 10.10.10.10
|
||||
--- response_body
|
||||
X-Forwarded-For: 1 is 1.1.1.1
|
||||
X-Forwarded-For: 2 is 2.2.2.2
|
||||
X-Forwarded-For: 3 is 3.3.3.3
|
||||
X-Forwarded-For: 4 is 4.4.4.4
|
||||
X-Forwarded-For: 5 is nil
|
||||
X-Forwarded-For: -1 is 4.4.4.4
|
||||
X-Forwarded-For: -2 is 3.3.3.3
|
||||
X-Forwarded-For: -3 is 2.2.2.2
|
||||
X-Forwarded-For: -4 is 1.1.1.1
|
||||
X-Forwarded-For: -5 is nil
|
||||
X-Forwarded-For: 0 is 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4
|
||||
X-Non-Existent-Header is nil
|
||||
X-Real-IP is 10.10.10.10
|
||||
X-Real-IP: 1 is 10.10.10.10
|
||||
X-Real-IP: 2 is nil
|
||||
X-Real-IP: -1 is 10.10.10.10
|
||||
X-Real-IP: -2 is nil
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 10: get_event_id
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local utils = require "resty.t1k.utils"
|
||||
ngx.say(utils.get_event_id("<!-- event_id: 0988987de04844c7a3ce6d27865c9513 -->"))
|
||||
ngx.say(utils.get_event_id("<!-- event_id: 8bae6adf33864c7f8bf715a9b7a65b2c TYPE: A -->"))
|
||||
ngx.say(utils.get_event_id("<!-- TYPE B -->"))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
0988987de04844c7a3ce6d27865c9513
|
||||
8bae6adf33864c7f8bf715a9b7a65b2c
|
||||
nil
|
||||
--- no_error_log
|
||||
[error]
|
||||
@@ -0,0 +1,29 @@
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
our $HttpConfig = <<'_EOC_';
|
||||
lua_package_path "lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
_EOC_
|
||||
|
||||
repeat_each(3);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: generate_v4
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local uuid = require "resty.t1k.uuid"
|
||||
ngx.say(uuid.generate_v4())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like
|
||||
^[0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}$
|
||||
--- no_error_log
|
||||
[error]
|
||||
Reference in New Issue
Block a user