feat: add sdk

This commit is contained in:
xiaobing.wang
2024-07-04 17:19:44 +08:00
parent f01652de22
commit 1f3f7262e0
48 changed files with 4339 additions and 5 deletions

4
.gitmodules vendored
View File

@@ -1,7 +1,5 @@
[submodule "blazehttp"]
path = blazehttp
url = https://github.com/chaitin/blazehttp
[submodule "lua-resty-t1k"]
path = lua-resty-t1k
url = https://github.com/chaitin/lua-resty-t1k

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/Safeline.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

13
.idea/material_theme_project_new.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="61f9103c:183e5ad9d68:-8000" />
<option name="version" value="8.12.6" />
</MTProjectMetadataState>
</option>
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Safeline.iml" filepath="$PROJECT_DIR$/.idea/Safeline.iml" />
</modules>
</component>
</project>

9
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/blazehttp" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lua-resty-t1k" vcs="Git" />
<mapping directory="$PROJECT_DIR$/plugins" vcs="Git" />
</component>
</project>

Submodule lua-resty-t1k deleted from 6446166301

View File

@@ -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 }}

View File

@@ -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/

25
sdk/lua-resty-t1k/.gitignore vendored Normal file
View File

@@ -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

View File

@@ -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

201
sdk/lua-resty-t1k/LICENSE Normal file
View File

@@ -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.

View File

@@ -0,0 +1,76 @@
# lua-resty-t1k
[![LuaRocks](https://img.shields.io/luarocks/v/blaisewang/lua-resty-t1k?style=flat-square)](https://luarocks.org/modules/blaisewang/lua-resty-t1k)
[![Releases](https://img.shields.io/github/v/release/chaitin/lua-resty-t1k?style=flat-square)](https://github.com/chaitin/lua-resty-t1k/releases)
[![License](https://img.shields.io/github/license/chaitin/lua-resty-t1k?color=ff69b4&style=flat-square)](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.
[![Test](https://img.shields.io/github/actions/workflow/status/chaitin/lua-resty-t1k/test.yml?logo=github&style=flat-square)](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 | ❌ | ✅ |
&ast; 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/).

View File

@@ -0,0 +1 @@
/bytecode

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,360 @@
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 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 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(ngx_req.get_method())
buf:add(" ")
buf:add(ngx_var.request_uri)
buf:add(fmt(" HTTP/%.1f\r\n", http_version))
for k, v in pairs(headers) do
buf:add(k .. ": " .. v .. "\r\n")
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

View File

@@ -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

View File

@@ -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
})

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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",
},
}

View File

@@ -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]

136
sdk/lua-resty-t1k/t/file.t Normal file
View File

@@ -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]

View File

@@ -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]

View File

@@ -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

67
sdk/lua-resty-t1k/t/log.t Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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

264
sdk/lua-resty-t1k/t/utils.t Normal file
View File

@@ -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]

View File

@@ -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]