├── series_10
├── api_doc
│ ├── .nojekyll
│ ├── index.html
│ ├── readme.md
│ ├── node.md
│ ├── mario.md
│ ├── conf.md
│ ├── metrics.md
│ ├── account.md
│ └── serializer.json
├── plugin_demo
│ ├── 发现大量40X就封IP
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 半小时内发现3次高危攻击就封IP
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 发现暴力破解
│ │ ├── README.md
│ │ └── brute.lua
│ ├── 统计用户访问的域名
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 发现可能存在的后门
│ │ ├── README.md
│ │ └── backdoor.lua
│ ├── 统计每个域名的访问量
│ │ ├── README.md
│ │ ├── plugin_counter.lua
│ │ └── plugin_ticker.lua
│ ├── ticker_hello_world
│ │ ├── ticker_hello_world.png
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── process_hello_world
│ │ ├── process_hello_world.png
│ │ ├── README.md
│ │ └── plugin.lua
│ └── 提取真实外网IP
│ │ ├── README.md
│ │ └── detect_real_IP.lua
└── plugin_sdk.md
├── series_20
├── plugin_demo
│ ├── 统计域名及其访问量
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 封禁特定地区|基于公开情报进行封禁
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 针对固定路径的Top统计
│ │ ├── README.md
│ │ └── plugin.lua
│ ├── 封禁频繁进行攻击的IP
│ │ ├── README.md
│ │ └── plugin.lua
│ └── 耗时请求告警
│ │ ├── README.md
│ │ └── plugin.lua
├── api_doc
│ ├── index.html
│ ├── readme.md
│ ├── report.md
│ ├── node.md
│ ├── mario.md
│ └── account.md
└── plugin_sdk.md
├── README.md
├── LICENSE
└── assets
└── safeline_logo.svg
/series_10/api_doc/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现大量40X就封IP/README.md:
--------------------------------------------------------------------------------
1 | # 发现大量40X就封IP
2 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/半小时内发现3次高危攻击就封IP/README.md:
--------------------------------------------------------------------------------
1 | # 半小时内发现3次高危攻击就封IP
2 |
3 |
4 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现暴力破解/README.md:
--------------------------------------------------------------------------------
1 | # 发现暴力破解事件
2 |
3 | 分析是否存在某个 IP 短时间内频繁访问登录接口,分析结果会输出在插件日志里。
4 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/统计用户访问的域名/README.md:
--------------------------------------------------------------------------------
1 | # 半小时内发现3次高危攻击就封IP
2 |
3 | 访问新的域名以后会记录一条日志
4 |
5 |
6 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现可能存在的后门/README.md:
--------------------------------------------------------------------------------
1 | # 发现可能存在的后门
2 |
3 | 针对检测引擎发现的后门攻击,进一步分析是否返回了 200,如果返回 200 则认为可能是真实存在的后门,结果输出在插件日志中。
4 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/统计每个域名的访问量/README.md:
--------------------------------------------------------------------------------
1 | # 半小时内发现3次高危攻击就封IP
2 |
3 | 该功能需要两个插件配合
4 |
5 | plugin_counter 负责统计域名的访问量
6 |
7 | plugin_ticker 负责定时输出统计结果、定时清理垃圾数据
8 |
9 |
10 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/ticker_hello_world/ticker_hello_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaitin/safeline-open-platform/HEAD/series_10/plugin_demo/ticker_hello_world/ticker_hello_world.png
--------------------------------------------------------------------------------
/series_10/plugin_demo/process_hello_world/process_hello_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaitin/safeline-open-platform/HEAD/series_10/plugin_demo/process_hello_world/process_hello_world.png
--------------------------------------------------------------------------------
/series_10/plugin_demo/ticker_hello_world/README.md:
--------------------------------------------------------------------------------
1 | # Ticker Hello World
2 |
3 | SafeLine 每隔 10 秒会调用这个插件的 ticker 函数一次,并且通过 log 函数输出当前系统时间,运行效果如图。
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/process_hello_world/README.md:
--------------------------------------------------------------------------------
1 | # Process Hello World
2 |
3 | 每个通过 SafeLine 的 HTTP 请求都会异步调用这个插件的 process 函数,并且通过 log 函数输出当前请求的路径,运行效果如图。
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/ticker_hello_world/plugin.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 | local os = require "os"
3 |
4 | function ticker(dur)
5 | skynet.log("hello world", os.date("%H:%M:%S"))
6 | end
7 |
8 | skynet.register(skynet.TYPE_TICKER, 10, ticker)
9 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/提取真实外网IP/README.md:
--------------------------------------------------------------------------------
1 | 雷池Waf插件
2 |
3 | # 检测真实攻击者IP
4 |
5 | lua语言(5.1)编写;
6 |
7 | 适用于10、20版本,针对XXF多IP、多层CDN跳转、复杂网络,导致Waf无法识别源IP的情况,默认只检测高危行为的外网IP。在记录IP的同时也会记录攻击类型。
8 |
9 | ### 可添加白名单(在white_list中加入)
10 |
11 | 例如:`white = {'8.8.8.8','9.9.9.9'}`
12 |
13 | 如果白名单为空,则默认匹配所有的外网攻击者IP。
14 |
15 | ---
16 |
17 | 如有BUG可以提Issues,不定期修复;
18 |
19 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/统计域名及其访问量/README.md:
--------------------------------------------------------------------------------
1 | # 版本要求
2 |
3 | `>= 19.07.001`
4 |
5 | # 用途描述
6 |
7 | 统计 24 小时内所有被访问的域名及其访问量,数据每 1 分钟更新一次
8 |
9 | # 已知缺陷
10 |
11 | 插件需要对经过雷池的所有请求进行统计,CPU 开销和内存开销巨大,并且在未来没有大幅度改善的可能性,请仅在 QPS 较低的情况下使用
12 |
13 | # 性能参数
14 |
15 | - 单机版单核最大 QPS:20000
16 | - 内存消耗最大可达若干 GB,待进一步测试
17 |
18 | # 可调整参数
19 |
20 | - `arg_range` 整数,统计范围,单位为秒
21 | - `arg_period` 整数,统计周期,单位为秒
22 |
23 | 即:统计 `arg_range` 秒内所有被访问的域名及其访问量,数据每 `arg_period` 秒更新一次
24 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/封禁特定地区|基于公开情报进行封禁/README.md:
--------------------------------------------------------------------------------
1 | # 版本要求
2 |
3 | `>= 19.07.001`
4 |
5 | # 用途描述
6 |
7 | 基于公开的威胁情报(使用 `ipdata.co` 提供的情报源),封禁来自特定地区或已知威胁 ip
8 |
9 | # 适用场景
10 |
11 | 需要 waf 与某些威胁情报系统联动,本插件提供 POC 样例
12 |
13 | # 已知缺陷
14 |
15 | - 使用的情报提供方对 api 调用有限制,单个账号调用次数存在上限
16 |
17 | # 性能参数
18 |
19 | - 暂无测试
20 |
21 | # 可调整参数
22 |
23 | - `api_key` 字符串,`ipdata` 注册账号的私有 api key
24 | - `bantime` 整数,封禁时间,单位为秒
25 | - `blocks` 待封禁地区的 table,格式请参考插件内注释
26 | - `block_by_threat` 威胁情报项开关,参考插件内注释
27 |
28 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现可能存在的后门/backdoor.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 | local match = {
3 | host = ".*",
4 | urlpath = ".*",
5 | type = skynet.MATCH_TYPE_ALL,
6 | target = skynet.MATCH_TARGET_DETECT,
7 | }
8 |
9 | function process()
10 | local info = skynet.get_detailed_info()
11 | if info.attack_type == 5 and info.resp_status_code == "200" then
12 | skynet.log('backdoor', string.format("发现疑似后门: [%s%s], 请求 IP 为 [%s]", info.host, info.urlpath, info.src_ip))
13 | end
14 | end
15 |
16 | skynet.register(skynet.TYPE_PROCESS, match, process)
--------------------------------------------------------------------------------
/series_20/plugin_demo/针对固定路径的Top统计/README.md:
--------------------------------------------------------------------------------
1 | # 版本要求
2 |
3 | `>= 19.07.001`
4 |
5 | # 用途描述
6 |
7 | 统计 10 分钟内访问某一路径最频繁的前 10 个 IP,数据每 5 秒更新一次
8 |
9 | # 适用场景
10 |
11 | 帮助用户了解特点页面/API 的实时访问情况,相比 「网站统计 → 实时访客统计」页面有更强的可定制性
12 |
13 | # 性能参数
14 |
15 | - 单机版单核最大 QPS:20000
16 |
17 | # 可调整参数
18 |
19 | - `arg_host` 统计的目标域名
20 | - `arg_url_path` 统计的目标路径
21 | - `arg_range` 整数,统计范围,单位为秒
22 | - `arg_period` 整数,统计周期(多久统计一次),单位为秒
23 | - `arg_threshold` 整数,统计最访问频繁前 N 个 IP
24 |
25 | 即:统计 `arg_range` 秒内访问 `arg_host` / `arg_url_path` 最频繁的前 `arg_threshold` 个 IP,数据每 `arg_period` 秒更新一次
26 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/process_hello_world/plugin.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | local match = {
4 | ip = "0.0.0.0/0", -- 字符串类型,要匹配的 ip 段,使用 cidr 记法
5 | host = ".*\\.chaitin\\.cn", -- 字符串类型,要匹配的域名,支持 perl 风格的正则表达式
6 | urlpath = ".*", -- 字符串类型,访问路径,同样支持正则
7 | type = skynet.MATCH_TYPE_ALL,
8 | target = skynet.MATCH_TARGET_ALL,
9 | }
10 |
11 |
12 | function process(ip, host, urlpath)
13 | skynet.log("hello world", host .. urlpath)
14 | end
15 |
16 | skynet.register(skynet.TYPE_PROCESS, match, process)
17 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/统计用户访问的域名/plugin.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | match = {
4 | ip = "0.0.0.0/0",
5 | -- host = ".+",
6 | -- urlpath = ".+",
7 | type = skynet.MATCH_TYPE_ALL,
8 | target = skynet.MATCH_TARGET_ALL,
9 | }
10 |
11 | function process(ip, host, urlpath)
12 | if skynet.db_get(skynet.DB_LOCAL, host) == nil then
13 | skynet.db_set(skynet.DB_LOCAL, host, host)
14 | skynet.log("new-ip", host)
15 | -- or skynet.http_post(...)
16 | end
17 | end
18 |
19 | skynet.register(skynet.TYPE_PROCESS, match, process)
20 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/封禁频繁进行攻击的IP/README.md:
--------------------------------------------------------------------------------
1 | # 版本要求
2 |
3 | `>= 19.07.001`
4 |
5 | # 用途描述
6 |
7 | 封禁在 30 分钟内发起 3 次或以上攻击的 IP,生效周期为 5 秒
8 |
9 | # 适用场景
10 |
11 | 客户业务敏感,拒绝为有攻击行为的 IP 提供服务时可以使用此插件
12 |
13 | # 已知缺陷
14 |
15 | - 目前 Plumber SQL 不支持以攻击检测日志作为数据源,从访问日志中筛选攻击请求导致了一些额外开销
16 | - 当前的实现会重复封禁 IP,对管理后台压力较大
17 |
18 | # 性能参数
19 |
20 | - 单机版单核最大 QPS:15000
21 | - 每封禁一个 IP 带来的单次执行开销约为 5ms
22 |
23 | # 可调整参数
24 |
25 | - `arg_range` 整数,统计范围,单位为秒
26 | - `arg_period` 整数,统计周期(多久统计一次),单位为秒
27 | - `arg_threshold` 整数,封禁阈值,当攻击数超过该值时执行封禁操作
28 |
29 | 即:封禁在 `arg_range` 秒内发起 `arg_threshold` 次或以上攻击的 IP,生效周期为 `arg_period` 秒
30 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/耗时请求告警/README.md:
--------------------------------------------------------------------------------
1 | # 版本要求
2 |
3 | `>= 19.07.001`
4 |
5 | # 用途描述
6 |
7 | 筛选出请求中耗时超过 0.5 秒的请求,每隔 5 秒以插件日志的形式告警,内容包括:报告用户 IP、目标站点,目标路径以及平均耗时。5 秒内重复的请求(IP,目标站点,目标路径相同视为重复)只汇报一次。
8 |
9 | # 适用场景
10 |
11 | 通常耗时的请求会给服务器带来更大的压力,本插件用于帮助客户筛选出对网站造成压力的请求,帮助客户排查问题(恶意请求 or 正常现象)
12 |
13 | # 已知缺陷
14 |
15 | - 暂不支持 AVG 函数,因此暂时使用 `SUM(time)/COUNT(ip)` 作为替代
16 |
17 | # 性能参数
18 |
19 | - 单机版单核最大 QPS:20000
20 |
21 | # 可调整参数
22 |
23 | - `arg_threshold` 浮点数,请求耗时阈值,耗时超过该值的请求会被记录,单位为秒
24 | - `arg_period` 整数,统计周期,统一周期内地重复请求只回报一次,单位为秒
25 |
26 | 即:筛选出请求中耗时超过 `arg_threshold` 秒的请求,每隔 `arg_period` 秒以插件日志的形式告警,`arg_period` 秒内重复的请求(IP,目标站点,目标路径相同视为重复)只汇报一次。
27 |
--------------------------------------------------------------------------------
/series_10/api_doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/耗时请求告警/plugin.lua:
--------------------------------------------------------------------------------
1 | local safeline = require("safeline")
2 |
3 | local arg_threshold = 1.0
4 | local arg_period = 5
5 |
6 | local query = string.format([[
7 | SELECT ip, host, url_path, SUM(time)/COUNT(ip) AS avg_time
8 | FROM access_log WHERE time > %f
9 | GROUP BY TUMBLE_WINDOW(NOW(), %d), ip, host, url_path
10 | ]], arg_threshold, arg_period)
11 |
12 | function process(key, rows)
13 | local ip_list = ""
14 | for row in rows do
15 | ip_list = ip_list .. string.format("IP %s 访问 %s/%s 耗时 %f 秒,告警阈值为 %f 秒\n", row.ip, row.host, row.url_path, row.avg_time, arg_threshold)
16 | end
17 | if ip_list ~= "" then
18 | safeline.log("警告", ip_list)
19 | end
20 | end
21 |
22 | safeline.register(safeline.TYPE_QUERY, query, process)
23 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/统计域名及其访问量/plugin.lua:
--------------------------------------------------------------------------------
1 | local safeline = require("safeline")
2 |
3 | local arg_range = 60 * 60 * 24
4 | local arg_period = 60
5 |
6 | local query = string.format([[
7 | SELECT host, COUNT(host) AS count_
8 | FROM access_log
9 | GROUP BY SLIDE_WINDOW(NOW(), %d, %d), host
10 | ORDER BY COUNT(host)
11 | ]], arg_range, arg_period)
12 |
13 | function process(key, rows)
14 | local domain_list = ""
15 | for row in rows do
16 | domain_list = domain_list .. string.format("域名 %s 在最近 %d 小时内被访问了 %d 次\n", row.host, arg_range / (60 * 60), row.count_)
17 | end
18 | if domain_list == "" then
19 | safeline.log("统计", "暂无已知的域名")
20 | else
21 | safeline.log("统计", domain_list)
22 | end
23 | end
24 |
25 | safeline.register(safeline.TYPE_QUERY, query, process)
26 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现大量40X就封IP/plugin.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 | local limit = 50 -- 产生4xx错误的最大次数
3 | local duration = 120 -- 过去几分钟内(两分钟)
4 | local bantime = 2 * 24 * 3600 -- 封禁的秒数
5 |
6 | -- 匹配规则是对所有IP进行匹配
7 |
8 | local match = {
9 | host = ".*",
10 | urlpath = ".*",
11 | type = skynet.MATCH_TYPE_DEFAULT,
12 | target = skynet.MATCH_TARGET_ALL
13 | }
14 |
15 | skynet.register(skynet.TYPE_PROCESS, match,
16 | function(ip, host, urlpath)
17 | -- 将会统计所有的IP的在过去两分钟内的次数。
18 | local ipkey = {ip = ip}
19 | local stat = skynet.stat_resp_code(ipkey, duration)
20 |
21 | -- 封大于50次的人两天
22 | local st4xx = stat[skynet.RESP_CODE_4XX]
23 | if st4xx ~= nil and st4xx > limit then
24 | skynet.action_ban(ipkey, bantime)
25 | end
26 | end
27 | )
28 |
--------------------------------------------------------------------------------
/series_20/api_doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Skyview Document
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/统计每个域名的访问量/plugin_counter.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | local match = {
4 | ip = "0.0.0.0/0",
5 | type = skynet.MATCH_TYPE_ALL,
6 | target = skynet.MATCH_TARGET_ACCESS,
7 | }
8 |
9 | local key_prefix = "domain"
10 | local index = key_prefix .. ":" .. "index"
11 |
12 | function process(ip, host, urlpath)
13 | local key = key_prefix .. ":" .. host
14 |
15 | -- Add host to domain index
16 | if tonumber(skynet.db_get(skynet.DB_GLOBAL, key)) == nil then
17 | domains = skynet.db_get(skynet.DB_GLOBAL, index)
18 | if domains == nil then
19 | skynet.db_set(skynet.DB_GLOBAL, index, host)
20 | else
21 | skynet.db_set(skynet.DB_GLOBAL, index, domains .. " " .. host)
22 | end
23 | end
24 |
25 | skynet.db_add(skynet.DB_GLOBAL, key, 1)
26 | end
27 |
28 | skynet.register(skynet.TYPE_PROCESS, match, process)
29 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/封禁频繁进行攻击的IP/plugin.lua:
--------------------------------------------------------------------------------
1 | local safeline = require("safeline")
2 |
3 | local arg_range = 60 * 30
4 | local arg_period = 5
5 | local arg_threshold = 3
6 |
7 | local query = string.format([[
8 | SELECT ip
9 | FROM access_log
10 | WHERE req_block_reason = "web"
11 | GROUP BY SLIDE_WINDOW(NOW(), %d, %d), ip
12 | HAVING COUNT(ip) >= %d
13 | ]], arg_range, arg_period, arg_threshold)
14 |
15 | function process(key, rows)
16 | local total_num = 0
17 | local err_num = 0
18 | for row in rows do
19 | err = safeline.action_ban(safeline.ACTION_SCOPE_ALL, { ip = row.ip }, 3600)
20 | total_num = total_num + 1
21 | if err ~= nil then
22 | err_num = err_num + 1
23 | end
24 | end
25 | if total_num ~= 0 then
26 | safeline.log("信息", string.format("本次需要封禁 %d 个 IP,其中 %d 个封禁失败", total_num, err_num))
27 | end
28 | end
29 |
30 | safeline.register(safeline.TYPE_QUERY, query, process)
31 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/针对固定路径的Top统计/plugin.lua:
--------------------------------------------------------------------------------
1 | local safeline = require("safeline")
2 |
3 | local arg_host = "chaitin.cn"
4 | local arg_url_path = "/safeline.html"
5 | local arg_range = 60 * 10
6 | local arg_period = 5
7 | local arg_threshold = 10
8 |
9 | local query = string.format([[
10 | SELECT ip, COUNT(ip) AS count_
11 | FROM access_log
12 | WHERE (host = %q) AND (url_path = %q)
13 | GROUP BY SLIDE_WINDOW(NOW(), %d, %d), ip
14 | ORDER BY COUNT(host)
15 | LIMIT %d
16 | ]], arg_host, arg_url_path, arg_range, arg_period, arg_threshold)
17 |
18 | function process(key, rows)
19 | local top_list = ""
20 | local i = 0
21 | for row in rows do
22 | top_list = top_list .. string.format("#%d. IP %s 在最近 %d 分钟内访问了 %s%s %d 次\n", i, row.ip, arg_range/60, arg_host, arg_url_path, row.count_)
23 | i = i + 1
24 | end
25 | if top_list == "" then
26 | safeline.log("统计", string.format("最近 %d 分钟内没有任何访问 %s%s 的请求", arg_range/60, arg_host, arg_url_path))
27 | else
28 | safeline.log("统计", top_list)
29 | end
30 | end
31 |
32 | safeline.register(safeline.TYPE_QUERY, query, process)
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 长亭 SafeLine 开放平台
2 |
3 | ## SafeLine(雷池)
4 |
5 |
6 |
7 |
8 |
9 |
10 | SafeLine(雷池)是由长亭科技推出的一款以智能语义分析算法著称的 Web 攻击防护产品,在大幅降低了漏报率和误报率的同时还能保持极高的检测效率,可以为用户提供高质量的 Web 攻击防护、CC 攻击防护、访问控制、防护统计等功能。
11 |
12 |
13 | [长亭科技](https://www.chaitin.cn/) | [长亭 SafeLine](https://www.chaitin.cn/zh/safeline)
14 |
15 |
16 |
17 | ## SafeLine Open API
18 |
19 |
20 |
21 | ## SafeLine 插件平台
22 |
23 | 作为 Web 流量处理网关,SafeLine 离线流量分析引擎提供了丰富的扩展接口,支持以更多自定义的方式来处理收到的 HTTP 请求,使用者可以通过自主编写 SafeLine 插件的方式完成用户的流量分析逻辑。
24 |
25 | 多数插件平台的使用者会自主编写插件来输出场景化的 Web 访问统计与 Web 攻击统计,或者编写插件与其他安全产品及风控系统整和对攻击者进行多维度深度检测与阻断。
26 |
27 | SafeLine 插件平台可以解决的问题包括但不限于复杂 CC 攻击、薅羊毛、恶意刷注册、恶意刷评论、暴力破解、脱裤、以及绝大部分用户遇到的业务安全问题。
28 |
29 | SafeLine 插件平台 SDK 文档见
30 | [10 系列](./series_10/plugin_sdk.md) | [20 系列](./series_20/plugin_sdk.md)
31 |
32 | SafeLine 插件 Demo 见
33 | [10 系列](./series_10/plugin_demo/) | [20 系列(可参考 10 系列)](./series_20/plugin_demo/)
34 |
35 |
36 | ## License
37 |
38 | [MIT](./LICENSE)
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/series_10/api_doc/readme.md:
--------------------------------------------------------------------------------
1 | ## SkyView 文档
2 |
3 | ```js
4 | // _oo0oo_
5 | // o8888888o
6 | // 88" . "88
7 | // (| -_- |)
8 | // 0\ = /0
9 | // ___/`---'\___
10 | // .' \\| |// '.
11 | // / \\||| : |||// \
12 | // / _||||| -:- |||||- \
13 | // | | \\\ - /// | |
14 | // | \_| ''\---/'' |_/ |
15 | // \ .-\__ '-' ___/-. /
16 | // ___'. .' /--.--\ `. .'___
17 | // ."" '< `.___\_<|>_/___.' >' "".
18 | // | | : `- \`.;`\ _ /`;.`/ - ` : | |
19 | // \ \ `_. \_ __\ /__ _/ .-` / /
20 | // =====`-.____`.___ \_____/___.-`___.-'=====
21 | // `=---='
22 | //
23 | //
24 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 | //
26 | // 佛祖保佑 永无BUG
27 | ```
28 |
29 | 模块列表
30 |
31 | - [mario](mario.md)
32 | - [conf2](conf2.md)
33 | - [acl](acl.md)
34 | - [metrics](metrics.md)
35 | - [conf](conf.md)
36 | - [account](account.md)
37 | - [node](node.md)
38 | - [management](management.md)
39 |
40 |
41 | 最后更新时间: `2018-12-20 09:17:53.100041`
--------------------------------------------------------------------------------
/series_10/plugin_demo/发现暴力破解/brute.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | local match = {
4 | host = ".*",
5 | urlpath = ".*",
6 | type = skynet.MATCH_TYPE_DEFAULT,
7 | target = skynet.MATCH_TARGET_ACCESS
8 | }
9 |
10 | function check_urlpath(urlpath)
11 | urlpath_l = string.lower(urlpath)
12 | kws = {"login"}
13 | for i, s in ipairs(kws) do
14 | if string.find(urlpath_l, s, 0) ~= nil then
15 | return true
16 | end
17 | end
18 | return false
19 | end
20 |
21 | function cat(host, urlpath)
22 | if string.sub(host, -1) == "/" and string.sub(urlpath, 0, 1) == '/' then
23 | return string.sub(host, 0, -2)..urlpath
24 | elseif string.sub(host, -1) ~= "/" and string.sub(urlpath, 0, 1) ~= '/' then
25 | return host.."/"..urlpath
26 | else
27 | return host..urlpath
28 | end
29 | end
30 |
31 | function process(ip, host, urlpath)
32 | ip_domain_paths = skynet.top_n(skynet.TOP_IP_HOST_URLPATH, 100, 5)
33 | for i, record in pairs(ip_domain_paths) do
34 | if record["count"] >= 10 then
35 | if check_urlpath(record["urlpath"]) then
36 | skynet.log("[brute]", string.format("发现 IP [%s] 疑似在尝试对 [%s] 进行暴力破解攻击", record["ip"], cat(record["host"], record["urlpath"])))
37 | end
38 | end
39 | end
40 | end
41 |
42 | skynet.register(skynet.TYPE_PROCESS, match, process)
43 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/统计每个域名的访问量/plugin_ticker.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | local duration = 60 * 60 * 24
4 |
5 | local key_prefix = "domain"
6 | local index = key_prefix .. ":" .. "index"
7 |
8 | function split(str)
9 | fields = {}
10 | for token in string.gmatch(str, "[^%s]+") do
11 | table.insert(fields, token)
12 | end
13 | return fields
14 | end
15 |
16 | function ticker(dur)
17 | local result = ""
18 | local domains = skynet.db_get(skynet.DB_GLOBAL, index)
19 | local map = {}
20 |
21 | if domains ~= nil then
22 | -- Domains deduplicate
23 | domains = split(domains)
24 | for i, domain in ipairs(domains) do
25 | map[domain] = true
26 | end
27 | end
28 |
29 | for domain, _ in pairs(map) do
30 | local key = key_prefix .. ":" .. domain
31 | local val = tonumber(skynet.db_get(skynet.DB_GLOBAL, key))
32 | if val ~= nil then
33 | if result == "" then
34 | result = domain .. " " .. val
35 | else
36 | result = result .. ", " .. domain .. " " .. val
37 | end
38 | end
39 | end
40 |
41 | skynet.db_del(skynet.DB_GLOBAL, index)
42 | for domain, _ in pairs(map) do
43 | local key = key_prefix .. ":" .. domain
44 | skynet.db_del(skynet.DB_GLOBAL, key)
45 | end
46 |
47 | if result == "" then
48 | result = ""
49 | end
50 | skynet.log("domain-ticker", result)
51 | end
52 |
53 | skynet.register(skynet.TYPE_TICKER, duration, ticker)
54 |
--------------------------------------------------------------------------------
/series_20/api_doc/readme.md:
--------------------------------------------------------------------------------
1 | ## Skyview 文档
2 |
3 | ```js
4 | _ _
5 | | | (_)
6 | ___ | | __ _ _ __ __ _ ___ __ __
7 | / __| | |/ / | | | | \ \ / / | | / _ \ \ \ /\ / /
8 | \__ \ | < | |_| | \ V / | | | __/ \ V V /
9 | |___/ |_|\_\ \__, | \_/ |_| \___| \_/\_/
10 | __/ |
11 | |___/
12 |
13 |
14 | // _oo0oo_
15 | // o8888888o
16 | // 88" . "88
17 | // (| -_- |)
18 | // 0\ = /0
19 | // ___/`---'\___
20 | // .' \\| |// '.
21 | // / \\||| : |||// \
22 | // / _||||| -:- |||||- \
23 | // | | \\\ - /// | |
24 | // | \_| ''\---/'' |_/ |
25 | // \ .-\__ '-' ___/-. /
26 | // ___'. .' /--.--\ `. .'___
27 | // ."" '< `.___\_<|>_/___.' >' "".
28 | // | | : `- \`.;`\ _ /`;.`/ - ` : | |
29 | // \ \ `_. \_ __\ /__ _/ .-` / /
30 | // =====`-.____`.___ \_____/___.-`___.-'=====
31 | // `=---='
32 | //
33 | //
34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35 | //
36 | // 佛祖保佑 永无BUG
37 | ```
38 |
39 | 模块列表
40 |
41 | - [options](options.md)
42 | - [node](node.md)
43 | - [account](account.md)
44 | - [mario](mario.md)
45 | - [report](report.md)
46 | - [website](website.md)
47 | - [policy](policy.md)
48 | - [acl](acl.md)
49 | - [management](management.md)
50 |
51 |
52 | 最后更新时间: `2018-12-11 07:32:44.670974`
--------------------------------------------------------------------------------
/series_10/plugin_demo/半小时内发现3次高危攻击就封IP/plugin.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | match = {
4 | ip = "0.0.0.0/0",
5 | host = ".+",
6 | urlpath = ".+",
7 | type = skynet.MATCH_TYPE_ALL,
8 | target = skynet.MATCH_TARGET_DETECT,
9 | }
10 |
11 | function clear(ip)
12 | skynet.db_del(skynet.DB_LOCAL, ip..":ts")
13 | skynet.db_del(skynet.DB_LOCAL, ip..":attack")
14 | end
15 |
16 | function reset(log, ip)
17 | skynet.db_set(skynet.DB_LOCAL, ip..":ts", tostring(log.timestamp))
18 | skynet.db_add(skynet.DB_LOCAL, ip..":attack", 1)
19 | end
20 |
21 | function process(ip, host, urlpath)
22 | local attack_limit = 3 -- 累积攻击次数
23 | local check_dur = 60 * 30 -- 攻击次数统计时间,单位秒
24 | local ban_dur = 60 * 60 -- 封禁时间,单位秒
25 | local ban_risk = 3 -- 需要统计的威胁等级,3 代表只统计高危,2 代表统计中高危,1代表统计中高低危
26 |
27 | local log = skynet.get_detailed_info()
28 |
29 | if log.risk_level < ban_risk then
30 | return
31 | end
32 |
33 | local ts = skynet.db_get(skynet.DB_LOCAL, ip..":ts")
34 |
35 | if ts == nil then
36 | -- first time record
37 | reset(log, ip)
38 | else
39 | if log.timestamp - tonumber(ts) < check_dur then
40 | local attack = skynet.db_get(skynet.DB_LOCAL, ip..":attack")
41 | if attack ~= nil and tonumber(attack) >= attack_limit then
42 | -- exceed attack limit
43 | local key = {ip = ip}
44 | skynet.action_ban(key, ban_dur)
45 | skynet.log("process", "ban "..ip)
46 | clear(ip)
47 | else
48 | -- normal record
49 | skynet.db_add(skynet.DB_LOCAL, ip..":attack", 1)
50 | end
51 | else
52 | -- exceed check duration, reset record
53 | clear(ip)
54 | reset(log, ip)
55 | end
56 | end
57 | end
58 |
59 | skynet.register(skynet.TYPE_PROCESS, match, process)
60 |
--------------------------------------------------------------------------------
/series_10/plugin_demo/提取真实外网IP/detect_real_IP.lua:
--------------------------------------------------------------------------------
1 | local skynet = require "skynet"
2 |
3 | match = {
4 | ip = "0.0.0.0/0",
5 | host = ".+",
6 | urlpath = ".+",
7 | type = skynet.MATCH_TYPE_ALL,
8 | target = skynet.MATCH_TARGET_DETECT,
9 | }
10 |
11 | function legalIp(ip)
12 |
13 | white = {'8.8.8.8','9.9.9.9'}
14 | c = ip
15 | ip_part = {}
16 | flag = 0
17 | for i,value in pairs(white) do
18 | if value==ip then
19 | return nil
20 | end
21 | end
22 |
23 | for i in string.gmatch(c,"%d+") do
24 | if tonumber(i)>255 or tonumber(i)<0 then
25 | return nil
26 | end
27 | ip_part[#ip_part+1]=i
28 | end
29 | if ip_part[1]=='10' or ip_part[1]=='127' then
30 | return nil
31 | elseif ip_part[1]=='172' and (tonumber(ip_part[2])>=16 and tonumber(ip_part[2])<=31) then
32 | return nil
33 | elseif ip_part[1]=='192' and ip_part[2]=='168' then
34 | return nil
35 | end
36 | if flag ==0 then
37 | return 1
38 | end
39 | end
40 |
41 |
42 | function getIp(content)
43 | local ipArray = ""
44 | for c in string.gmatch(content,"%d+%.%d+%.%d+%.%d+") do
45 | if c ~= nil and legalIp(c) then
46 | if string.find(ipArray,c) == nil then
47 | ipArray = ipArray ..c..", "
48 | content = string.gsub(content,c,'')
49 | end
50 | end
51 | end
52 | return ipArray
53 | end
54 |
55 |
56 | function process(ip, host, urlpath)
57 | local ban_risk = 3
58 |
59 | local log = skynet.get_detailed_info()
60 | local attack_num = log.attack_type+1
61 | if log.risk_level < ban_risk then
62 | return
63 | end
64 | local ans = getIp(log.req_header_raw)
65 | att_type ={'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'}
66 |
67 | if ans == "" then
68 | ans = log.src_ip
69 | skynet.log(att_type[attack_num],ans)
70 | else
71 | new_ans = string.gsub(ans,log.dest_ip,'')
72 | skynet.log(att_type[attack_num],string.sub(new_ans,1,-3))
73 | end
74 | end
75 |
76 | skynet.register(skynet.TYPE_PROCESS, match, process)
--------------------------------------------------------------------------------
/series_20/api_doc/report.md:
--------------------------------------------------------------------------------
1 | # /api/ReportSubscriptionAPI
2 |
3 | ## GET
4 |
5 |
6 |
7 | ### - 获取报告订阅列表
8 |
9 |
10 |
11 | ?> 本 API 支持翻页参数
12 |
13 | 响应
14 |
15 | ```js
16 | {
17 | "err": null,
18 | "data": [
19 | {
20 | "id": 5,
21 | "name": "foobar",
22 | "type": "day",
23 | "targets": [
24 | "https://foobar.com"
25 | ],
26 | "create_time": "1544513554"
27 | }
28 | ],
29 | "msg": null
30 | }
31 | ```
32 |
33 | ## POST
34 |
35 | ### [数据格式定义]
36 | 数据格式
37 |
38 | |字段名|数据类型|是否必填/默认值|NULL|其他|
39 | |:--|:-:|:-:|:-:|:-:|
40 | |name | 字符串 | 必填 | False | - |
41 | |targets | 列表 | 非必填/ | False | [ - 详见下方表格](#uodxcanjdyrszdnudcuhkyhwgujttqtq) |
42 | |type | 指定选项 | 必填 | False | 选项是: ['day', 'week', 'month'] |
43 |
44 | targets
45 |
46 | |字段名|数据类型|是否必填/默认值|NULL|其他|
47 | |:--|:-:|:-:|:-:|:-:|
48 | |子字段 | 字符串 | 必填 | False | URL; |
49 |
50 |
51 |
52 | ### - 创建一个报告订阅
53 |
54 |
55 |
56 | 请求
57 |
58 | ```js
59 | {
60 | "name": "foobar",
61 | "targets": [
62 | "https://foobar.com"
63 | ],
64 | "type": "day"
65 | }
66 | ```
67 |
68 | 响应
69 |
70 | ```js
71 | {
72 | "err": null,
73 | "data": {
74 | "id": 3,
75 | "name": "foobar",
76 | "type": "day",
77 | "targets": [
78 | "https://foobar.com"
79 | ],
80 | "create_time": "1544513553"
81 | },
82 | "msg": null
83 | }
84 | ```
85 |
86 | ## DELETE
87 |
88 | ### [数据格式定义]
89 | 数据格式
90 |
91 | |字段名|数据类型|是否必填/默认值|NULL|其他|
92 | |:--|:-:|:-:|:-:|:-:|
93 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#dvbcmnyztjozvkybrqukdeptuhwtypjm) |
94 |
95 | id__in
96 |
97 | |字段名|数据类型|是否必填/默认值|NULL|其他|
98 | |:--|:-:|:-:|:-:|:-:|
99 | |子字段 | 整型数字 | 必填 | False | - |
100 |
101 |
102 |
103 | ### - 删除一个报告订阅
104 |
105 |
106 |
107 | 请求
108 |
109 | ```js
110 | {
111 | "id": [
112 | 4
113 | ]
114 | }
115 | ```
116 |
117 | 响应
118 |
119 | ```js
120 | {
121 | "err": null,
122 | "data": null,
123 | "msg": null
124 | }
125 | ```
126 |
127 | # /api/ManualGenerateReportAPI
128 |
129 | ## POST
130 |
131 | ### [数据格式定义]
132 | 数据格式
133 |
134 | |字段名|数据类型|是否必填/默认值|NULL|其他|
135 | |:--|:-:|:-:|:-:|:-:|
136 | |time_range | 字符串 | 必填 | False | 时间范围,比如 1234-4567, 1234-0; |
137 | |name | 字符串 | 必填 | False | - |
138 | |targets | 列表 | 非必填/ | False | [ - 详见下方表格](#iewakbfkzsrnavgjkymvtcrdmejtuiow) |
139 | |sync | 布尔 | 非必填/False | False | - |
140 | |type | 指定选项 | 必填 | False | 选项是: ['day', 'week', 'custom', 'month'] |
141 |
142 | targets
143 |
144 | |字段名|数据类型|是否必填/默认值|NULL|其他|
145 | |:--|:-:|:-:|:-:|:-:|
146 | |子字段 | 字符串 | 必填 | False | URL; |
147 |
148 |
149 |
150 | ### - 手动生成一个报告
151 |
152 |
153 |
154 | 请求
155 |
156 | ```js
157 | {
158 | "name": "foobar",
159 | "targets": [],
160 | "time_range": "1541001600-1541087999",
161 | "sync": true,
162 | "type": "day"
163 | }
164 | ```
165 |
166 | 响应
167 |
168 | ```js
169 | {
170 | "err": null,
171 | "data": {
172 | "id": 1,
173 | "name": "foobar",
174 | "type": "day",
175 | "targets": [],
176 | "create_time": "1544513552"
177 | },
178 | "msg": null
179 | }
180 | ```
181 |
182 | # /api/ReportResultAPI
183 |
184 | ## GET
185 |
186 | ### [数据格式定义]
187 | 数据格式
188 |
189 | |字段名|数据类型|是否必填/默认值|NULL|其他|
190 | |:--|:-:|:-:|:-:|:-:|
191 | |type | 指定选项 | 非必填/无默认值 | False | 选项是: ['day', 'week', 'custom', 'month'] |
192 | |targets | 列表 | 非必填/ | False | [ - 详见下方表格](#nnjecfyyqsuiwazlidtiomsldwonwzxc) |
193 | |name__like | 列表 | 非必填/ | False | [ - 详见下方表格](#ankoyivlfqjzxvnexohrxykydddgtpxu) |
194 | |timestamp__range | 列表 | 必填 | False | [ - 详见下方表格](#seixqrvenscoqlyuazzxocpcbnppqadc) |
195 |
196 | targets
197 |
198 | |字段名|数据类型|是否必填/默认值|NULL|其他|
199 | |:--|:-:|:-:|:-:|:-:|
200 | |子字段 | 字符串 | 必填 | False | - |
201 |
202 |
203 | name__like
204 |
205 | |字段名|数据类型|是否必填/默认值|NULL|其他|
206 | |:--|:-:|:-:|:-:|:-:|
207 | |子字段 | 字符串 | 必填 | False | - |
208 |
209 |
210 | timestamp__range
211 |
212 | |字段名|数据类型|是否必填/默认值|NULL|其他|
213 | |:--|:-:|:-:|:-:|:-:|
214 | |子字段 | 字符串 | 必填 | False | 时间范围,比如 1234-4567, 1234-0; |
215 |
216 |
217 |
218 | ### - 获取报告列表
219 |
220 |
221 |
222 | ?> 本 API 支持翻页参数
223 |
224 | 请求
225 |
226 | ```js
227 | {
228 | "type": [
229 | "custom",
230 | "day"
231 | ],
232 | "name__like": [
233 | "f"
234 | ]
235 | }
236 | ```
237 |
238 | 响应
239 |
240 | ```js
241 | {
242 | "err": null,
243 | "data": [
244 | {
245 | "id": 3,
246 | "time_range": "1531971605-1531981729",
247 | "state": "Pending",
248 | "create_time": "1544513553",
249 | "message_id": "",
250 | "end_time": null,
251 | "name": "foobar",
252 | "type": "day",
253 | "targets": []
254 | }
255 | ],
256 | "msg": null
257 | }
258 | ```
259 |
260 | ## DELETE
261 |
262 | ### [数据格式定义]
263 | 数据格式
264 |
265 | |字段名|数据类型|是否必填/默认值|NULL|其他|
266 | |:--|:-:|:-:|:-:|:-:|
267 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#mjhjnvfejyeyblouxsqztdccbonrwtdf) |
268 |
269 | id__in
270 |
271 | |字段名|数据类型|是否必填/默认值|NULL|其他|
272 | |:--|:-:|:-:|:-:|:-:|
273 | |子字段 | 整型数字 | 必填 | False | - |
274 |
275 |
276 |
277 | ### - 删除一个报告结果
278 |
279 |
280 |
281 | 请求
282 |
283 | ```js
284 | {
285 | "id": [
286 | 2
287 | ]
288 | }
289 | ```
290 |
291 | 响应
292 |
293 | ```js
294 | {
295 | "err": null,
296 | "data": null,
297 | "msg": null
298 | }
299 | ```
300 |
301 |
--------------------------------------------------------------------------------
/series_10/api_doc/node.md:
--------------------------------------------------------------------------------
1 | # /api/v1/node/config
2 |
3 | ## GET
4 |
5 |
6 |
7 | ### 获取提供给 minion 的配置
8 |
9 |
10 |
11 | 响应
12 |
13 | ```js
14 | {
15 | "err": null,
16 | "msg": "",
17 | "data": {
18 | "services_config": {
19 | "tengine": {
20 | "version": 6,
21 | "config": [
22 | {
23 | "sites": [
24 | {
25 | "domains": [
26 | "*"
27 | ],
28 | "backend": {
29 | "servers": [
30 | {
31 | "address": "127.0.0.1",
32 | "port": 8080,
33 | "weight": 1
34 | }
35 | ],
36 | "health_check": {}
37 | },
38 | "ssl_options": {},
39 | "reverse_proxy_options": {},
40 | "listen_options": [
41 | {
42 | "port": 80,
43 | "address": "0.0.0.0"
44 | }
45 | ],
46 | "client_ip_source": "remote_addr",
47 | "forbidden_page_response_code": 403,
48 | "name": "68",
49 | "forbidden_page_content": "",
50 | "x_forwarded_for_option": "AppendXFFHeader"
51 | }
52 | ],
53 | "interface_name": "br0"
54 | },
55 | {
56 | "interface_name": "br1"
57 | },
58 | {
59 | "interface_name": "br2"
60 | },
61 | {
62 | "interface_name": "br3"
63 | },
64 | {
65 | "interface_name": "br4"
66 | }
67 | ]
68 | }
69 | }
70 | }
71 | }
72 | ```
73 |
74 | ## POST
75 |
76 | 数据格式
77 |
78 | |字段名|数据类型|是否必填/默认值|NULL|其他|
79 | |:--|:-:|:-:|:-:|:-:|
80 | |id | 字符串 | 必填 | False | - |
81 | |services_status | 字典 | - | - | 详见下面表格 |
82 |
83 | services_status
84 |
85 | |字段名|数据类型|是否必填/默认值|NULL|其他|
86 | |:--|:-:|:-:|:-:|:-:|
87 | |tengine | 字典 | - | - | 详见下面表格 |
88 |
89 | tengine
90 |
91 | |字段名|数据类型|是否必填/默认值|NULL|其他|
92 | |:--|:-:|:-:|:-:|:-:|
93 | |version | 整型数字 | 必填 | False | - |
94 | |status | 字典 | - | - | 详见下面表格 |
95 |
96 | status
97 |
98 | |字段名|数据类型|是否必填/默认值|NULL|其他|
99 | |:--|:-:|:-:|:-:|:-:|
100 | |status | 字符串 | 必填 | False | - |
101 | |last_result | 字符串 | 必填 | False | 最小长度: 0; |
102 | |sites | 字典类型 | - | - | 字典类型 |
103 |
104 | sites
105 |
106 | |字段名|数据类型|是否必填/默认值|NULL|其他|
107 | |:--|:-:|:-:|:-:|:-:|
108 | |子字段 | 字典 | - | - | 详见下面表格 |
109 |
110 | 子字段
111 |
112 | |字段名|数据类型|是否必填/默认值|NULL|其他|
113 | |:--|:-:|:-:|:-:|:-:|
114 | |status | 字符串 | 必填 | False | - |
115 | |upstreams | 字典类型 | - | - | 字典类型 |
116 |
117 | upstreams
118 |
119 | |字段名|数据类型|是否必填/默认值|NULL|其他|
120 | |:--|:-:|:-:|:-:|:-:|
121 | |子字段 | 字符串 | 必填 | False | - |
122 |
123 |
124 |
125 | ### minion 报告状态获取节点配置
126 |
127 |
128 |
129 | 请求
130 |
131 | ```js
132 | {
133 | "id": "test",
134 | "services_status": {
135 | "tengine": {
136 | "version": 0,
137 | "status": {
138 | "status": "HEALTHY",
139 | "last_result": "",
140 | "sites": {
141 | "69": {
142 | "status": "HEALTHY",
143 | "upstreams": {
144 | "backend1.com:80": "HEALTHY",
145 | "backend1.com:90": "HEALTHY"
146 | }
147 | }
148 | }
149 | }
150 | }
151 | }
152 | }
153 | ```
154 |
155 | 响应
156 |
157 | ```js
158 | {
159 | "err": null,
160 | "msg": "",
161 | "data": {
162 | "services_config": {
163 | "tengine": {
164 | "version": 6,
165 | "config": [
166 | {
167 | "sites": [
168 | {
169 | "domains": [
170 | "*"
171 | ],
172 | "backend": {
173 | "servers": [
174 | {
175 | "address": "127.0.0.1",
176 | "port": 8080,
177 | "weight": 1
178 | }
179 | ],
180 | "health_check": {}
181 | },
182 | "ssl_options": {},
183 | "reverse_proxy_options": {},
184 | "listen_options": [
185 | {
186 | "port": 80,
187 | "address": "0.0.0.0"
188 | }
189 | ],
190 | "client_ip_source": "remote_addr",
191 | "forbidden_page_response_code": 403,
192 | "name": "69",
193 | "forbidden_page_content": "",
194 | "x_forwarded_for_option": "AppendXFFHeader"
195 | }
196 | ],
197 | "interface_name": "br0"
198 | },
199 | {
200 | "interface_name": "br1"
201 | },
202 | {
203 | "interface_name": "br2"
204 | },
205 | {
206 | "interface_name": "br3"
207 | },
208 | {
209 | "interface_name": "br4"
210 | }
211 | ]
212 | }
213 | }
214 | }
215 | }
216 | ```
217 |
218 |
--------------------------------------------------------------------------------
/series_20/api_doc/node.md:
--------------------------------------------------------------------------------
1 | # /api/NodeInfoAPI
2 |
3 | ## GET
4 |
5 | ### 权限
6 | - System Monitor
7 |
8 | ### - 获取节点数据
9 |
10 |
11 |
12 | 响应
13 |
14 | ```js
15 | {
16 | "err": null,
17 | "data": [
18 | {
19 | "id": "minion",
20 | "node": {
21 | "timestamp": 1544513539,
22 | "cpu": 0.02,
23 | "mem": 0.02,
24 | "uptime": 100000,
25 | "network_io": [
26 | {
27 | "time_from": 150000000,
28 | "time_to": 150000000,
29 | "interface": "lo",
30 | "read": 255.0,
31 | "write": 825.0
32 | }
33 | ],
34 | "disk_info": {
35 | "time_from": 150000000,
36 | "time_to": 150000000,
37 | "total": 100,
38 | "usage": 12,
39 | "read": 1000,
40 | "write": 1000
41 | },
42 | "detector": {
43 | "node": "deploy",
44 | "time_from": 1526541111.271,
45 | "time_to": 1526541116.272,
46 | "ioread": 554,
47 | "iowrite": 651,
48 | "detector_version": "9",
49 | "policy_version": {
50 | "current_version": "1"
51 | },
52 | "req_num_total": 10086,
53 | "rsp_num_total": 0,
54 | "req_num_blocked": 0,
55 | "req_num_blocked_acl": 0,
56 | "req_num_blocked_web": 0,
57 | "req_num_attack": 0,
58 | "req_num_attack_acl": 0,
59 | "req_num_attack_web": 0,
60 | "rsp_num_blocked": 0,
61 | "rsp_num_blocked_acl": 0,
62 | "rsp_num_blocked_web": 0,
63 | "rsp_num_attack": 0,
64 | "rsp_num_attack_acl": 0,
65 | "rsp_num_attack_web": 0,
66 | "req_detect_time": 0,
67 | "req_detect_count": 0,
68 | "rsp_detect_time": 0,
69 | "rsp_detect_count": 0,
70 | "req_ngx_time_distribution": {
71 | "tengine-1": {
72 | "time": 4803.0,
73 | "count": 6
74 | }
75 | }
76 | },
77 | "tengine": {
78 | "config_version": 1,
79 | "stats": {
80 | "time_from": 150000000,
81 | "time_to": 150000000,
82 | "accepts": 100,
83 | "handled": 100,
84 | "requests": 100,
85 | "avg_time": 800.5
86 | },
87 | "hostname": "tengine-1"
88 | },
89 | "mario": null,
90 | "last_timestamp": -1,
91 | "status": "STARTING",
92 | "received_timestamp": 1544513539.213878
93 | },
94 | "ha": {
95 | "services": [
96 | {
97 | "name": "mgt-api",
98 | "healthy": true,
99 | "estate": "master",
100 | "mstate": "idle"
101 | },
102 | {
103 | "name": "tengine",
104 | "healthy": false,
105 | "estate": "slave",
106 | "mstate": "idle"
107 | }
108 | ]
109 | },
110 | "services": [
111 | {
112 | "name": "mgt-api",
113 | "hostname": "mgt-api-ilibili-slb1",
114 | "version": "18.02.1",
115 | "status": "HEALTHY",
116 | "cpu": 0.02,
117 | "mem": 0.02,
118 | "uptime": 100000
119 | },
120 | {
121 | "name": "tengine",
122 | "hostname": "tengine-1",
123 | "version": "18.02.1",
124 | "status": "HEALTHY",
125 | "cpu": 0.02,
126 | "mem": 0.02,
127 | "uptime": 100000,
128 | "status_hint": ""
129 | },
130 | {
131 | "name": "detector-srv",
132 | "hostname": "detector-srv",
133 | "version": "18.02.1",
134 | "status": "HEALTHY",
135 | "cpu": 0.02,
136 | "mem": 0.02,
137 | "uptime": 100000,
138 | "status_hint": ""
139 | }
140 | ],
141 | "sites": []
142 | }
143 | ],
144 | "msg": null
145 | }
146 | ```
147 |
148 | # /api/ServerTimeAPI
149 |
150 | ## GET
151 |
152 | ### 权限
153 | - System Management
154 |
155 | ### - 获取服务器时间
156 |
157 |
158 |
159 | 响应
160 |
161 | ```js
162 | {
163 | "err": null,
164 | "data": [
165 | {
166 | "node": "ws-test-client",
167 | "data": {
168 | "err": null,
169 | "data": 1533711846
170 | },
171 | "channel_name": "specific.aaa"
172 | }
173 | ],
174 | "msg": null
175 | }
176 | ```
177 |
178 | ## PUT
179 |
180 | ### [数据格式定义]
181 | 数据格式
182 |
183 | |字段名|数据类型|是否必填/默认值|NULL|其他|
184 | |:--|:-:|:-:|:-:|:-:|
185 | |value | 浮点数字 | 必填 | False | - |
186 |
187 | ### 权限
188 | - System Management
189 |
190 | ### - 设置服务器时间
191 |
192 |
193 |
194 | 请求
195 |
196 | ```js
197 | {
198 | "value": 1533711846
199 | }
200 | ```
201 |
202 | 响应
203 |
204 | ```js
205 | {
206 | "err": null,
207 | "data": null,
208 | "msg": null
209 | }
210 | ```
211 |
212 | # /api/HACommandAPI
213 |
214 | ## POST
215 |
216 | ### [数据格式定义]
217 | 数据格式
218 |
219 | |字段名|数据类型|是否必填/默认值|NULL|其他|
220 | |:--|:-:|:-:|:-:|:-:|
221 | |node | 字符串 | 必填 | False | - |
222 | |service | 字符串 | 必填 | False | - |
223 | |command | 指定选项 | 必填 | False | 选项是: ['recover', 'halt'] |
224 |
225 | ### 权限
226 | - System Management
227 |
228 | ### - HA service 重连
229 |
230 |
231 |
232 | 请求
233 |
234 | ```js
235 | {
236 | "command": "recover",
237 | "node": "minion-1",
238 | "service": "mgt-api"
239 | }
240 | ```
241 |
242 | 响应
243 |
244 | ```js
245 | {
246 | "err": null,
247 | "data": null,
248 | "msg": null
249 | }
250 | ```
251 |
252 |
--------------------------------------------------------------------------------
/assets/safeline_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/series_20/api_doc/mario.md:
--------------------------------------------------------------------------------
1 | # /api/MarioPluginAPI
2 |
3 | ## GET
4 |
5 | ### 权限
6 | - Website And Security Policy Management
7 |
8 | ### - 获取插件信息列表
9 |
10 |
11 |
12 | ?> 本 API 支持翻页参数
13 |
14 | 响应
15 |
16 | ```js
17 | {
18 | "err": null,
19 | "data": [
20 | {
21 | "id": 9,
22 | "state": "running",
23 | "statistic": {
24 | "total_execution": 123.0,
25 | "total_error_execution": 123.0,
26 | "execution_per_second": 123.5,
27 | "average_execution_time": 0.007,
28 | "error_execution_per_second": 123.5,
29 | "timestamp": 1544513537.0
30 | },
31 | "name": "foobar",
32 | "comment": "comment",
33 | "create_time": "1544513537",
34 | "last_update_time": "1544513537",
35 | "is_enabled": true,
36 | "code": "print('Hello World!')",
37 | "hash": "e0906b1de689ae9a9daa3ed52d8ee0ac"
38 | }
39 | ],
40 | "msg": null
41 | }
42 | ```
43 |
44 | ## POST
45 |
46 | ### [数据格式定义]
47 | 数据格式
48 |
49 | |字段名|数据类型|是否必填/默认值|NULL|其他|
50 | |:--|:-:|:-:|:-:|:-:|
51 | |name | 字符串 | 必填 | False | - |
52 | |comment | 字符串 | 非必填/ | False | 最小长度: 0; |
53 | |is_enabled | 布尔 | 必填 | False | - |
54 | |code | 字符串 | 必填 | False | lua 脚本; |
55 |
56 | ### 权限
57 | - Website And Security Policy Management
58 |
59 | ### - 创建一个插件
60 |
61 |
62 |
63 | 请求
64 |
65 | ```js
66 | {
67 | "name": "foobar",
68 | "comment": "comment",
69 | "is_enabled": true,
70 | "code": "print('Hello World!')"
71 | }
72 | ```
73 |
74 | 响应
75 |
76 | ```js
77 | {
78 | "err": null,
79 | "data": {
80 | "id": 5,
81 | "state": "unknown",
82 | "statistic": {
83 | "total_execution": 0.0,
84 | "total_error_execution": 0.0,
85 | "execution_per_second": 0.0,
86 | "average_execution_time": 0.0,
87 | "error_execution_per_second": 0.0,
88 | "timestamp": 0.0
89 | },
90 | "name": "foobar",
91 | "comment": "comment",
92 | "create_time": "1544513536",
93 | "last_update_time": "1544513536",
94 | "is_enabled": true,
95 | "code": "print('Hello World!')",
96 | "hash": "e0906b1de689ae9a9daa3ed52d8ee0ac"
97 | },
98 | "msg": null
99 | }
100 | ```
101 |
102 | ## PUT
103 |
104 | ### [数据格式定义]
105 | 数据格式
106 |
107 | |字段名|数据类型|是否必填/默认值|NULL|其他|
108 | |:--|:-:|:-:|:-:|:-:|
109 | |id | 整型数字 | 必填 | False | - |
110 | |name | 字符串 | 必填 | False | - |
111 | |comment | 字符串 | 非必填/ | False | 最小长度: 0; |
112 | |is_enabled | 布尔 | 必填 | False | - |
113 | |code | 字符串 | 必填 | False | lua 脚本; |
114 |
115 | ### 权限
116 | - Website And Security Policy Management
117 |
118 | ### - 修改一个插件的信息
119 |
120 |
121 |
122 | 请求
123 |
124 | ```js
125 | {
126 | "name": "new_foobar",
127 | "comment": "comment",
128 | "is_enabled": true,
129 | "code": "print('test_edit_mario_plugin')",
130 | "id": 7
131 | }
132 | ```
133 |
134 | 响应
135 |
136 | ```js
137 | {
138 | "err": null,
139 | "data": {
140 | "id": 7,
141 | "state": "unknown",
142 | "statistic": {
143 | "total_execution": 0.0,
144 | "total_error_execution": 0.0,
145 | "execution_per_second": 0.0,
146 | "average_execution_time": 0.0,
147 | "error_execution_per_second": 0.0,
148 | "timestamp": 0.0
149 | },
150 | "name": "new_foobar",
151 | "comment": "comment",
152 | "create_time": "1544513537",
153 | "last_update_time": "1544513537",
154 | "is_enabled": true,
155 | "code": "print('test_edit_mario_plugin')",
156 | "hash": "1112745ea3468f8485bb054701b74be4"
157 | },
158 | "msg": null
159 | }
160 | ```
161 |
162 | ## DELETE
163 |
164 | ### [数据格式定义]
165 | 数据格式
166 |
167 | |字段名|数据类型|是否必填/默认值|NULL|其他|
168 | |:--|:-:|:-:|:-:|:-:|
169 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#xageqgfpcyiomciyldcwxqkytpvoxysn) |
170 |
171 | id__in
172 |
173 | |字段名|数据类型|是否必填/默认值|NULL|其他|
174 | |:--|:-:|:-:|:-:|:-:|
175 | |子字段 | 整型数字 | 必填 | False | - |
176 |
177 | ### 权限
178 | - Website And Security Policy Management
179 |
180 | ### - 删除一个插件
181 |
182 |
183 |
184 | 请求
185 |
186 | ```js
187 | {
188 | "id": 6
189 | }
190 | ```
191 |
192 | 响应
193 |
194 | ```js
195 | {
196 | "err": null,
197 | "data": null,
198 | "msg": null
199 | }
200 | ```
201 |
202 | # /api/MarioPluginStateUpdateAPI
203 |
204 | ## POST
205 |
206 | ### [数据格式定义]
207 | 数据格式
208 |
209 | |字段名|数据类型|是否必填/默认值|NULL|其他|
210 | |:--|:-:|:-:|:-:|:-:|
211 | |timestamp | 浮点数字 | 必填 | False | - |
212 | |id | 字符串 | 必填 | False | - |
213 | |state | 指定选项 | 必填 | False | 选项是: ['running', 'compile-error'] |
214 | |total_execution | 整型数字 | 必填 | False | - |
215 | |total_error_execution | 整型数字 | 必填 | False | - |
216 | |execution_per_second | 浮点数字 | 必填 | False | - |
217 | |error_execution_per_second | 浮点数字 | 必填 | False | - |
218 | |average_execution_time | 浮点数字 | 必填 | False | - |
219 |
220 | ### 权限
221 | - Minion
222 |
223 | ### - 插件状态更新
224 |
225 |
226 |
227 | 请求
228 |
229 | ```js
230 | [
231 | {
232 | "timestamp": 1544513537,
233 | "id": 10,
234 | "state": "running",
235 | "total_execution": 123,
236 | "total_error_execution": 123,
237 | "execution_per_second": 123.5,
238 | "error_execution_per_second": 123.5,
239 | "average_execution_time": 0.007
240 | }
241 | ]
242 | ```
243 |
244 | 响应
245 |
246 | ```js
247 | {
248 | "err": null,
249 | "data": null,
250 | "msg": null
251 | }
252 | ```
253 |
254 | # /api/PluginLogAPI
255 |
256 | ## GET
257 |
258 | ### [数据格式定义]
259 | 数据格式
260 |
261 | |字段名|数据类型|是否必填/默认值|NULL|其他|
262 | |:--|:-:|:-:|:-:|:-:|
263 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#grujmzwrfudpeebakdozancmdqlnnugs) |
264 | |timestamp__range | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#mpogkbwxumdbnnuyxzniqxfxqyilowhq) |
265 | |plugin_id | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#vybkwoncchnjlvfnwvneoakmumiaslrx) |
266 | |tag__like | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#zghrtwgurvydlmahckxuhaiwxvypzahq) |
267 | |content__like | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#jhswphxsdjjatywrzpnljvedkqdeuexe) |
268 |
269 | id__in
270 |
271 | |字段名|数据类型|是否必填/默认值|NULL|其他|
272 | |:--|:-:|:-:|:-:|:-:|
273 | |子字段 | 整型数字 | 必填 | False | - |
274 |
275 |
276 | timestamp__range
277 |
278 | |字段名|数据类型|是否必填/默认值|NULL|其他|
279 | |:--|:-:|:-:|:-:|:-:|
280 | |子字段 | 字符串 | 必填 | False | 时间范围,比如 1234-4567, 1234-0; |
281 |
282 |
283 | plugin_id
284 |
285 | |字段名|数据类型|是否必填/默认值|NULL|其他|
286 | |:--|:-:|:-:|:-:|:-:|
287 | |子字段 | 整型数字 | 必填 | False | - |
288 |
289 |
290 | tag__like
291 |
292 | |字段名|数据类型|是否必填/默认值|NULL|其他|
293 | |:--|:-:|:-:|:-:|:-:|
294 | |子字段 | 字符串 | 必填 | False | - |
295 |
296 |
297 | content__like
298 |
299 | |字段名|数据类型|是否必填/默认值|NULL|其他|
300 | |:--|:-:|:-:|:-:|:-:|
301 | |子字段 | 字符串 | 必填 | False | - |
302 |
303 | ### 权限
304 | - Website And Security Policy Management
305 |
306 | ### - 获取插件信息列表
307 |
308 |
309 |
310 | ?> 本 API 支持翻页参数
311 |
312 | 请求
313 |
314 | ```js
315 | {
316 | "plugin_id": [
317 | "1"
318 | ],
319 | "tag__like": [
320 | "t"
321 | ],
322 | "content__like": [
323 | "c"
324 | ],
325 | "timestamp__range": [
326 | "1544513438-1544513538"
327 | ]
328 | }
329 | ```
330 |
331 | 响应
332 |
333 | ```js
334 | {
335 | "err": null,
336 | "data": [],
337 | "msg": null
338 | }
339 | ```
340 |
341 | # /api/DownloadPluginLogAPI
342 |
343 | ## POST
344 |
345 | ### [数据格式定义]
346 | 数据格式
347 |
348 | |字段名|数据类型|是否必填/默认值|NULL|其他|
349 | |:--|:-:|:-:|:-:|:-:|
350 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#ujrlngizbotoaguqwitnevlelwwbllvn) |
351 | |timestamp__range | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#krzymkipklphnkrixnkqnizzookbjtin) |
352 | |plugin_id | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#twlgkxqcncnjqbryofxlpklmwyzfkocj) |
353 | |tag__like | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#cbchbnrhfnkcmugcuzlsldxgyuwkllsa) |
354 | |content__like | 列表 | 非必填/无默认值 | False | [ - 详见下方表格](#vrlbcbgteivvdticnpkviwzxcidpcuzk) |
355 |
356 | id__in
357 |
358 | |字段名|数据类型|是否必填/默认值|NULL|其他|
359 | |:--|:-:|:-:|:-:|:-:|
360 | |子字段 | 整型数字 | 必填 | False | - |
361 |
362 |
363 | timestamp__range
364 |
365 | |字段名|数据类型|是否必填/默认值|NULL|其他|
366 | |:--|:-:|:-:|:-:|:-:|
367 | |子字段 | 字符串 | 必填 | False | 时间范围,比如 1234-4567, 1234-0; |
368 |
369 |
370 | plugin_id
371 |
372 | |字段名|数据类型|是否必填/默认值|NULL|其他|
373 | |:--|:-:|:-:|:-:|:-:|
374 | |子字段 | 整型数字 | 必填 | False | - |
375 |
376 |
377 | tag__like
378 |
379 | |字段名|数据类型|是否必填/默认值|NULL|其他|
380 | |:--|:-:|:-:|:-:|:-:|
381 | |子字段 | 字符串 | 必填 | False | - |
382 |
383 |
384 | content__like
385 |
386 | |字段名|数据类型|是否必填/默认值|NULL|其他|
387 | |:--|:-:|:-:|:-:|:-:|
388 | |子字段 | 字符串 | 必填 | False | - |
389 |
390 | ### 权限
391 | - Website And Security Policy Management
392 |
393 | ### - 下载插件日志
394 |
395 |
396 |
397 | 请求
398 |
399 | ```js
400 | {
401 | "tag__like": [
402 | "t"
403 | ],
404 | "content__like": [
405 | "c"
406 | ],
407 | "timestamp__range": [
408 | "1544513435-1544513535"
409 | ]
410 | }
411 | ```
412 |
413 | 响应
414 |
415 | ```js
416 | {
417 | "err": null,
418 | "data": null,
419 | "msg": null
420 | }
421 | ```
422 |
423 |
--------------------------------------------------------------------------------
/series_10/api_doc/mario.md:
--------------------------------------------------------------------------------
1 | # /api/v1/mario/plugin
2 |
3 | ## GET
4 |
5 |
6 |
7 | ### 获取 mario 插件
8 |
9 |
10 |
11 | 本 API 支持翻页参数
12 |
13 | 响应
14 |
15 | ```js
16 | {
17 | "err": null,
18 | "msg": "",
19 | "data": [
20 | {
21 | "id": "bbe6087e83324efba5ef7fec1c59ff95",
22 | "create_time": 1545297433.275232,
23 | "update_time": 1545297433.275248,
24 | "statistic": {
25 | "total_execution": 0.0,
26 | "total_error_execution": 0.0,
27 | "execution_per_second": 0.0,
28 | "average_execution_time": 0.0,
29 | "error_execution_per_second": 0.0,
30 | "timestamp": 0.0
31 | },
32 | "running_state": "unknown",
33 | "name": "plugin1",
34 | "comment": "",
35 | "type": "user_plugin",
36 | "state": "enabled",
37 | "code": "print(\"test\")",
38 | "hash": "231496871f9a2ed5404086f91b6ef55f"
39 | }
40 | ]
41 | }
42 | ```
43 |
44 | ## POST
45 |
46 | 数据格式
47 |
48 | |字段名|数据类型|是否必填/默认值|NULL|其他|
49 | |:--|:-:|:-:|:-:|:-:|
50 | |name | 字符串 | 必填 | False | 最大长度: 64; |
51 | |comment | 字符串 | 非必填/'' | False | 最小长度: 0; |
52 | |state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled'] |
53 | |code | 字符串 | 必填 | False | lua 脚本; |
54 |
55 |
56 |
57 | ### 新建 mario 插件
58 |
59 |
60 |
61 | 请求
62 |
63 | ```js
64 | {
65 | "name": "plugin1",
66 | "state": "enabled",
67 | "code": "print(\"test\")"
68 | }
69 | ```
70 |
71 | 响应
72 |
73 | ```js
74 | {
75 | "err": null,
76 | "msg": "",
77 | "data": {
78 | "id": "85d81d46fc474edf8311bdeb2c597668",
79 | "create_time": 1545297433.000389,
80 | "update_time": 1545297433.000405,
81 | "statistic": {
82 | "total_execution": 0.0,
83 | "total_error_execution": 0.0,
84 | "execution_per_second": 0.0,
85 | "average_execution_time": 0.0,
86 | "error_execution_per_second": 0.0,
87 | "timestamp": 0.0
88 | },
89 | "running_state": "unknown",
90 | "name": "plugin1",
91 | "comment": "",
92 | "type": "user_plugin",
93 | "state": "enabled",
94 | "code": "print(\"test\")",
95 | "hash": "231496871f9a2ed5404086f91b6ef55f"
96 | }
97 | }
98 | ```
99 |
100 | ## PUT
101 |
102 | 数据格式
103 |
104 | |字段名|数据类型|是否必填/默认值|NULL|其他|
105 | |:--|:-:|:-:|:-:|:-:|
106 | |name | 字符串 | 必填 | False | 最大长度: 64; |
107 | |comment | 字符串 | 非必填/'' | False | 最小长度: 0; |
108 | |state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled'] |
109 | |code | 字符串 | 必填 | False | lua 脚本; |
110 | |id | UUID | 必填 | False | UUID4 |
111 |
112 |
113 |
114 | ### 修改 mario 插件
115 |
116 |
117 |
118 | 请求
119 |
120 | ```js
121 | {
122 | "name": "plugin1",
123 | "state": "enabled",
124 | "code": "print(\"test\")",
125 | "id": "7da7a4c1933c42be90f629d5ca8dc0fd"
126 | }
127 | ```
128 |
129 | 响应
130 |
131 | ```js
132 | {
133 | "err": null,
134 | "msg": "",
135 | "data": {
136 | "id": "7da7a4c1933c42be90f629d5ca8dc0fd",
137 | "create_time": 1545297433.415531,
138 | "update_time": 1545297433.429221,
139 | "statistic": {
140 | "total_execution": 0.0,
141 | "total_error_execution": 0.0,
142 | "execution_per_second": 0.0,
143 | "average_execution_time": 0.0,
144 | "error_execution_per_second": 0.0,
145 | "timestamp": 0.0
146 | },
147 | "running_state": "unknown",
148 | "name": "plugin1",
149 | "comment": "",
150 | "type": "user_plugin",
151 | "state": "enabled",
152 | "code": "print(\"test\")",
153 | "hash": "231496871f9a2ed5404086f91b6ef55f"
154 | }
155 | }
156 | ```
157 |
158 | ## DELETE
159 |
160 | 数据格式
161 |
162 | |字段名|数据类型|是否必填/默认值|NULL|其他|
163 | |:--|:-:|:-:|:-:|:-:|
164 | |id | UUID | 必填 | False | UUID4 |
165 |
166 |
167 |
168 | ### 删除 mario 插件
169 |
170 |
171 |
172 | 请求
173 |
174 | ```js
175 | {
176 | "id": "6ff551dd85f54865a58df83e0de026e7"
177 | }
178 | ```
179 |
180 | 响应
181 |
182 | ```js
183 | {
184 | "err": null,
185 | "msg": "",
186 | "data": null
187 | }
188 | ```
189 |
190 | # /api/v1/mario/plugin/state
191 |
192 | ## POST
193 |
194 | 数据格式
195 |
196 | |字段名|数据类型|是否必填/默认值|NULL|其他|
197 | |:--|:-:|:-:|:-:|:-:|
198 | |timestamp | 浮点数字 | 必填 | False | - |
199 | |id | UUID | 必填 | False | UUID4 |
200 | |hash | 字符串 | 必填 | False | 最大长度: 32; |
201 | |state | 字符串 | 必填 | False | 最大长度: 32; |
202 | |total_execution | 整型数字 | 必填 | False | - |
203 | |total_error_execution | 整型数字 | 必填 | False | - |
204 | |execution_per_second | 整型数字 | 必填 | False | - |
205 | |error_execution_per_second | 整型数字 | 必填 | False | - |
206 | |average_execution_time | 浮点数字 | 必填 | False | - |
207 |
208 |
209 |
210 | ### mario 更新插件状态
211 |
212 |
213 |
214 | 请求
215 |
216 | ```js
217 | [
218 | {
219 | "timestamp": 1545297433,
220 | "id": "325e4a11a0994053b918d19b14214648",
221 | "hash": "cfcd208495d565ef66e7dff9f98764da",
222 | "state": "running",
223 | "total_execution": 123,
224 | "total_error_execution": 123,
225 | "execution_per_second": 123,
226 | "error_execution_per_second": 123,
227 | "average_execution_time": 0.007
228 | },
229 | {
230 | "timestamp": 1545297433,
231 | "id": "78eb088083174a5cbb5cafd9222b9616",
232 | "hash": "c4ca4238a0b923820dcc509a6f75849b",
233 | "state": "running",
234 | "total_execution": 123,
235 | "total_error_execution": 123,
236 | "execution_per_second": 123,
237 | "error_execution_per_second": 123,
238 | "average_execution_time": 0.007
239 | },
240 | {
241 | "timestamp": 1545297433,
242 | "id": "0258fce859254377907de1a255d2975a",
243 | "hash": "c81e728d9d4c2f636f067f89cc14862c",
244 | "state": "running",
245 | "total_execution": 123,
246 | "total_error_execution": 123,
247 | "execution_per_second": 123,
248 | "error_execution_per_second": 123,
249 | "average_execution_time": 0.007
250 | },
251 | {
252 | "timestamp": 1545297433,
253 | "id": "7fabee0b40a3498b910c18ccffff16c1",
254 | "hash": "eccbc87e4b5ce2fe28308fd9f2a7baf3",
255 | "state": "running",
256 | "total_execution": 123,
257 | "total_error_execution": 123,
258 | "execution_per_second": 123,
259 | "error_execution_per_second": 123,
260 | "average_execution_time": 0.007
261 | }
262 | ]
263 | ```
264 |
265 | 响应
266 |
267 | ```js
268 | {
269 | "err": null,
270 | "msg": "",
271 | "data": [
272 | {
273 | "id": "325e4a11a0994053b918d19b14214648",
274 | "action": "nop",
275 | "code": "0"
276 | },
277 | {
278 | "id": "78eb088083174a5cbb5cafd9222b9616",
279 | "action": "update",
280 | "code": "new code"
281 | },
282 | {
283 | "id": "0258fce859254377907de1a255d2975a",
284 | "action": "delete",
285 | "code": ""
286 | },
287 | {
288 | "id": "7fabee0b40a3498b910c18ccffff16c1",
289 | "action": "delete",
290 | "code": ""
291 | },
292 | {
293 | "id": "edf14336e5a344eea6a54e74b47f587a",
294 | "action": "new",
295 | "code": "4"
296 | }
297 | ]
298 | }
299 | ```
300 |
301 | # /api/v1/mario/plugin/state/log
302 |
303 | ## POST
304 |
305 | 数据格式
306 |
307 | |字段名|数据类型|是否必填/默认值|NULL|其他|
308 | |:--|:-:|:-:|:-:|:-:|
309 | |timestamp | 浮点数字 | 必填 | False | - |
310 | |id | UUID | 必填 | False | UUID4 |
311 | |tag | 字符串 | 必填 | False | 最大长度: 32; |
312 | |msg | 字符串 | 必填 | False | - |
313 |
314 |
315 |
316 | ### mario 更新 plugin 日志
317 |
318 |
319 |
320 | 请求
321 |
322 | ```js
323 | [
324 | {
325 | "timestamp": 1545297434,
326 | "id": "a7276226aa5c414196b02f1d332b8ea9",
327 | "tag": "system",
328 | "msg": "test msg"
329 | },
330 | {
331 | "timestamp": 1545297434,
332 | "id": "d5ebeaa7974f461891ece33a48e34fc7",
333 | "tag": "system",
334 | "msg": "test msg"
335 | }
336 | ]
337 | ```
338 |
339 | 响应
340 |
341 | ```js
342 | {
343 | "err": null,
344 | "msg": "",
345 | "data": null
346 | }
347 | ```
348 |
349 | # /api/v1/mario/plugin/log
350 |
351 | ## GET
352 |
353 | 数据格式
354 |
355 | |字段名|数据类型|是否必填/默认值|NULL|其他|
356 | |:--|:-:|:-:|:-:|:-:|
357 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
358 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
359 | |plugin_id | 字符串 | 必填 | False | - |
360 | |tag | 字符串 | 非必填/无默认值 | False | - |
361 |
362 |
363 |
364 | ### 获取插件 log
365 |
366 |
367 |
368 | 本 API 支持翻页参数
369 |
370 | 请求
371 |
372 | ```js
373 | {
374 | "plugin_id": "91a1769c6d6d4fe5bc39e5e00aa661fa",
375 | "count": 2,
376 | "offset": 1,
377 | "time_before": 1545297433.688,
378 | "time_after": 1545297430.688
379 | }
380 | ```
381 |
382 | 响应
383 |
384 | ```js
385 | {
386 | "err": null,
387 | "msg": "",
388 | "data": {
389 | "items": [
390 | {
391 | "id": 8,
392 | "timestamp": 1545297431.688,
393 | "tag": "",
394 | "msg": ""
395 | },
396 | {
397 | "id": 9,
398 | "timestamp": 1545297430.688,
399 | "tag": "",
400 | "msg": ""
401 | }
402 | ],
403 | "total": 3
404 | }
405 | }
406 | ```
407 |
408 | ## DELETE
409 |
410 | 数据格式
411 |
412 | |字段名|数据类型|是否必填/默认值|NULL|其他|
413 | |:--|:-:|:-:|:-:|:-:|
414 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
415 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
416 | |plugin_id | 字符串 | 必填 | False | - |
417 | |tag | 字符串 | 非必填/无默认值 | False | - |
418 |
419 |
420 |
421 | ### 删除插件 log
422 |
423 |
424 |
425 | 请求
426 |
427 | ```js
428 | {
429 | "plugin_id": "1df641ff9ecb488d980dda1553bc7e2b"
430 | }
431 | ```
432 |
433 | 响应
434 |
435 | ```js
436 | {
437 | "err": null,
438 | "msg": "",
439 | "data": null
440 | }
441 | ```
442 |
443 | # /api/v1/mario/plugin/log/download
444 |
445 | ## GET
446 |
447 | 数据格式
448 |
449 | |字段名|数据类型|是否必填/默认值|NULL|其他|
450 | |:--|:-:|:-:|:-:|:-:|
451 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
452 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
453 | |plugin_id | 字符串 | 必填 | False | - |
454 | |tag | 字符串 | 非必填/无默认值 | False | - |
455 |
456 |
457 |
458 | ### 下载插件 log
459 |
460 |
461 |
462 | 请求
463 |
464 | ```js
465 | {
466 | "plugin_id": "b0a6c5863e024c1381c2671d917511f4"
467 | }
468 | ```
469 |
470 | 响应
471 |
472 | ```js
473 | "不支持展示的数据类型,如文件下载等"
474 | ```
475 |
476 |
--------------------------------------------------------------------------------
/series_10/plugin_sdk.md:
--------------------------------------------------------------------------------
1 | # SafeLine 10 扩展插件文档
2 |
3 | SafeLine 扩展插件系统(以下简称插件系统)使用 Lua 作为脚本语言,**目前语法兼容标准 Lua 5.1。**
4 |
5 | 在「入侵检测」→「扩展插件管理」页面可对插件进行新增,查看,删除等操作。本文档将说明插件的编写方法以及可用的相关接口。
6 |
7 |
8 |
9 | ## 导入
10 |
11 | 在编写插件时,首先需要导入 skynet 模块,skynet 模块包含了插件系统提供的所有接口。
12 |
13 | ```lua
14 | local skynet = require "skynet"
15 | ```
16 |
17 | 注意: 出于安全性的考量,插件脚本中只允许使用有限的标准库函数,其中 IO 库中的所有函数,OS 库中的 `execute`, `exit`, `remove`, `setlocale`, `tmpname` 等函数均被禁用。
18 |
19 | ## 注册
20 |
21 | 每个插件都需要调用注册函数,注册函数的参数根据插件的类型有所不同。
22 |
23 | 其函数签名如下:
24 |
25 | ```lua
26 | function skynet.register(type, ...)
27 | ```
28 |
29 | `type` 代表插件的类型,可能的取值为:
30 | - `skynet.TYPE_PROCESS` process 插件
31 | - `skynet.TYPE_TICKER` ticker 插件
32 |
33 | ### process 插件
34 |
35 | process 类型的插件会被特定的请求触发,其注册方式如下:
36 |
37 | ```lua
38 | local skynet = require "skynet"
39 |
40 | local match = {
41 | ip = "0.0.0.0/0", -- 字符串类型,要匹配的 ip 段,使用 cidr 记法
42 | host = ".*\\.chaitin\\.cn", -- 字符串类型,要匹配的域名,支持 perl 风格的正则表达式
43 | urlpath = ".*", -- 字符串类型,访问路径,同样支持正则
44 | type = [type value], -- 指定 plugin 被触发的模式(可省略)
45 | -- skynet.MATCH_TYPE_DEFAULT:优化的触发模式,每秒钟最多一次(默认值)
46 | -- skynet.MATCH_TYPE_ALL:每个请求都被触发
47 | target = [target value], -- 指定plugin要命中的请求类型(可省略)
48 | -- skynet.MATCH_TARGET_ALL:命中全部请求(默认值)
49 | -- skynet.MATCH_TARGET_ACCESS:命中未被拦截的请求
50 | -- skynet.MATCH_TARGET_DETECT:命中产生攻击日志的请求(包括被拦截的以及未拦截但产生日志的请求)
51 | }
52 |
53 |
54 | function process(ip, host, urlpath)
55 | -- 在此处理请求
56 | end
57 |
58 | skynet.register(skynet.TYPE_PROCESS, match, process)
59 | ```
60 |
61 | - `match` 是一个 table,用来声明插件的触发规则,其中,`ip` `host` `urlpath` 规定了能够触发插件的请求的形式。如果只关心请求中的某些数据,可以据此省略 table 中的某些数据域(field)。比如只关心请求中的 IP,可以在 `match` 中仅指定 IP。
62 | ```
63 | 注意: 域的组合不是任意的,目前支持的组合有 (ip)、(ip,host)、(host,urlpath)、(ip,host,urlpath) 四种。不同的组合选择将导致不同的匹配行为。
64 | ```
65 |
66 | 例如,如下两种组合理论上能够匹配完全相同的请求:
67 | 1. `{ip = "0.0.0.0/0", host = "localhost", urlpath = "/"}`
68 | 2. `{host = "localhost", urlpath = "/"}`
69 |
70 | 但是,这两种组合却会导致对插件的不同触发行为。第一个组合会针对不同的 ip 进行单独触发,而第二个在触发时并不关心 ip 的情况,只针对不同的 `host` 和 `urlpath` 进行触发。因此第二个组合会减少实际的触发次数,从而获得更好的性能。所以在实现 plugin 时,如果对于某个域并不关心,最好的做法是不写该域,而不是使用类似于`0.0.0.0/0`或`.*`这样的通配符。
71 | ```
72 | 注意: 当 match 的 target = skynet.MATCH_TARGET_ALL 且用户在 SafeLine 的「入侵检测」→「防护策略管理」中添加了「自定义规则」时,插件可能会接收到类型分别为 skynet.MATCH_TARGET_ACCESS 和 skynet.MATCH_TARGET_DETECT 的重复请求,如果需要去重,请使用 skynet.get_target() 判断类型后再做处理。
73 | ```
74 | - `process` 是插件的的回调函数,当插件触发时被调用,参数的值即为请求对应的客户端 IP 地址(`ip`)、访问域名(`host`)、访问路径(`urlpath`)。例如,对于一个访问 `https://chaitin.cn/safeline.html` 的请求,其域名是 `chaitin.cn`,访问路径是 `/safeline.html` )。
75 | ```
76 | 注意:
77 | 为了保证性能,默认设置插件的 match.type = skynet.MATCH_TYPE_DEFAULT,同样的请求(即 ip、host、urlpath 相同)在一秒内只会被触发一次。
78 |
79 | 对于搭载了插件平台版本 >= 1.0.0 的 SafeLine(详询技术人员), 当插件的 target = skynet.MATCH_TARGET_DETECT 时,此模式不起作用,等同于 skynet.MATCH_TYPE_ALL
80 |
81 | host 和 urlpath 中的正则表达式均匹配整个字符串(插件平台在正则后插入了 $),例如 urlpath = "/safeline" 仅匹配访问 /safeline 或者 /safeline?key=xxxx 的请求,而不匹配访问 /safeline.html 的请求。
82 | ```
83 |
84 | - 回调函数无返回值。
85 |
86 | ### ticker 插件
87 |
88 | ticker 类型的插件会被定时触发,其注册方式如下:
89 |
90 | ```lua
91 | local skynet = require "skynet"
92 |
93 | local duration = 10
94 |
95 | function ticker(dur)
96 | -- 在此处理定时任务
97 | end
98 |
99 | skynet.register(skynet.TYPE_TICKER, duration, ticker)
100 | ```
101 | - `durtaion` 是一个整数,用来声明插件的触发间隔,其单位为秒。
102 |
103 | - `ticker` 是插件的回调函数,当插件触发时被调用,参数 `dur` 的值为插件的触发间隔,其单位为秒。
104 |
105 | - ticker 插件通常用于定时获取所有请求的统计信息,例如获取访问频率最高的某个 URL(详见本文档「接口函数」→「top 类」一节)。
106 | ```
107 | 注意: 并非所有的接口函数都可以在 ticker 插件的回调函数中被调用,以接口函数的说明为准。
108 | ```
109 |
110 | - 回调函数无返回值。
111 |
112 | ## 接口函数
113 |
114 | skynet 包中提供的函数以下统称为接口函数。
115 |
116 | ### key table
117 | key 是一个在接口函数中广泛使用的数据结构。
118 |
119 | ```lua
120 | key = {
121 | ip = "",
122 | host = "",
123 | urlpath = "",
124 | }
125 | ```
126 | `key` 的结构和 process 插件的 `match` 相似,但只保留了描述请求的域,和 match 不同的是,key 中的数据只能是确定值,即,`ip` 只能是单个的 ip 地址,`host` 和 `urlpath` 不支持正则表达式。
127 |
128 | 用户可将其作为参数传递给接口函数,查询指定 `ip/host/urlpath` 的访问频率,风险级别等,或者对其进行封禁。
129 |
130 | 以下函数中的 `key` 参数为上述的 `key table`,代表符合条件的一类请求。
131 |
132 | ### stat 类
133 |
134 | 获取(非攻击)请求的统计信息,理论上可查询的最大时间范围为 60 分钟 + 60 秒。
135 |
136 | `function skynet.stat_visit(key, time) `:
137 | - 返回在过去 `time` 秒内符合 `key` 约束的请求数。
138 | - `time` 为整型,以秒为单位。
139 | - 返回值为整型。
140 |
141 | `function skynet.stat_time(key, time) `:
142 | - 返回在过去 time 秒内符合 key 约束的请求消耗服务器的时间。
143 | - `time` 为整型,以秒为单位。
144 | - 返回值为整型,以秒为单位。
145 |
146 | `function skynet.stat_resp_code(key, time)` :
147 | - 返回一个以 HTTP 状态码为索引的 table,其值为在过去 time 秒内符合 key 约束的不同 HTTP 状态码的请求数。
148 | - `time` 为整型,以秒为单位。
149 | - 返回值为 table。
150 | - skynet.RESP_CODE_1XX 请求已接受,需要继续处理
151 | - skynet.RESP_CODE_2XX 请求已成功
152 | - skynet.RESP_CODE_3XX 请求重定向
153 | - skynet.RESP_CODE_4XX 客户端错误
154 | - skynet.RESP_CODE_5XX 服务端错误
155 |
156 | ### top 类
157 |
158 | `function skynet.top_n(type, n, duration)`:
159 | - 查询在 `duration` 秒内出现频率最高的前 n 条数据,数据类型由 `type` 指定。
160 | - `type` 为整型,其可能的取值如下:
161 | - `skynet.TOP_IP` 查询指定时间段内 所有请求中出现频率最高的 **IP**
162 | - `skynet.TOP_IP_HOST` 查询指定时间段内,所有请求中出现频率最高的 **IP**,**域名** 组合
163 | - `skynet.TOP_HOST_URLPATH` 查询指定时间段内,所有请求中出现频率最高的 **域名**,**访问路径** 组合
164 | - `skynet.TOP_IP_HOST_URLPATH` 查询指定时间段内,所有请求中出现频率最高的 **IP**,**域名**,**访问路径** 的组合
165 | - `skynet.TOP_SESSION` 查询指定时间段内,所有请求中出现频率最高的 **session**
166 | - `skynet.TOP_IP_SESSION` 查询指定时间段内,所有请求中出现频率最高的 **IP**,**session**组合
167 | - `skynet.TOP_SESSION_HOST` 查询指定时间段内,所有请求中出现频率最高的的 **session**,**域名** 组合
168 | - `skynet.TOP_SESSION_HOST_URLPATH` 查询指定时间段内,所有请求中出现频率最高的的 **session**,**域名** ,**访问路径** 组合
169 | ```
170 | 注意:此处的 session 特指 SafeLine 的「Session 策略」中所指定的 session,请在「站点管理」→「防护站点管理」中选择一个虚拟服务器,在其子站点的编辑窗口中即可指定 session 策略。
171 | ```
172 | - `n`, `duration` 均为整型,`duration` 可能的取值如下:
173 | - skynet.TOP_DURATION_5S 查询 5 秒内的数据
174 | - 返回一个 key table 的数组,key table 中额外的 count 域保存了访问次数。
175 | ```
176 | 注意:当插件平台的部署模式为集群时,该函数取得的数据为当前节点下的数据,在之后的版本中会支持获取全部节点合并后的数据。
177 | ```
178 |
179 | ### action 类
180 |
181 | SafeLine 的「访问控制规则」的部分接口。
182 |
183 | `function skynet.action_ban(key, duration)`:
184 | - 封禁符合 `key` 约束的请求,有效时间为 `duration` 秒。
185 | - `duration` 为整型,以秒为单位。
186 | - 提交请求成功时函数返回 `nil`,失败时返回包含错误信息的字符串。
187 |
188 | `function skynet.action_limit(key, duration, period, limit)`:
189 | - 限制符合 `key` 约束的请求的访问频率,在 `period` 内只允许访问 `limit` 次,有效时间为 `duration` 秒。
190 | - `duration` 为整型,以秒为单位;`period` 为整型,以秒为单位;`limit` 为整型。
191 | - 提交请求成功时函数返回 `nil`,失败时返回包含错误信息的字符串。
192 |
193 | ### db 类
194 |
195 | 插件系统内置的键值对型数据库(key-value database)接口,所有接口函数的第一个参数需要指定在哪个 DB 中操作,可选值为 `skynet.DB_GLOBAL` 或 `skynet.DB_LOCAL`,LOCAL DB 对于每个插件是独立的,不同插件之间互不干扰。GLOBAL DB 是所有插件共享的,不同插件可以通过 GLOBAL DB 交换数据。
196 |
197 | key 和 value 的类型均为字符串,对于 `skynet.db_add` 以及 `skynet.db_sub` 函数,其返回值会自动转换为数字。
198 |
199 | 从性能角度考虑,如果不是必须的话,尽量使用 LOCAL DB,可以获得更高的性能。
200 |
201 | ```
202 | function skynet.db_get(db, key)
203 | ```
204 | ```
205 | function skynet.db_set(db, key, val)
206 | ```
207 | ```
208 | function skynet.db_del(db, key)
209 | ```
210 | ```
211 | function skynet.db_size(db)
212 | ```
213 | ```
214 | function skynet.db_clear(db)
215 | ```
216 | ```
217 | function skynet.db_add(db, key, val)
218 | ```
219 | 执行:`db[key] += val`,不需要事先创建,初始值为 0
220 | ```
221 | function skynet.db_sub(db, key, val)
222 | ```
223 | 执行:`db[key] -= val`,不需要事先创建,初始值为 0
224 |
225 | ### http 类
226 |
227 | 插件系统内置的 HTTP 客户端接口。
228 | `function skynet.http_get(url, header)`:
229 | - 对地址 `url` 发起一次 HTTP GET 请求,`url` 中可携带 GET 参数,在 `header` 中可指定 HTTP header。
230 | - `url` 为字符串类型,`header` 为 table 类型。
231 | - 返回值为 `(resp, err)`,`err` 为字符串类型,包含请求错误信息,无错误时值为 `nil`,当无错误发生时 `resp` 为 table 类型,包含 HTTP 请求的响应,其结构示例如下:
232 | ```
233 | {
234 | status_code = 200,
235 | header = {
236 | -- ...
237 | },
238 | body = "Response body",
239 | }
240 | ```
241 |
242 | `function skynet.http_post(url, header, data)`:
243 | - 对地址 `url` 发起一次 HTTP POST 请求,在 `header` 中可指定 HTTP header,`data` 为 POST 数据。
244 | - `url` 为字符串类型,`header` 为 table 类型,`data` 为字符串类型。
245 | - 返回值同 `skynet.http_get()`。
246 |
247 | ### log 类
248 |
249 | `function skynet.log(tag, msg)`:
250 | - 产生一条插件日志,日志会被记录到日志文件中或者显示在 SafeLine 的界面上。
251 | - `tag` 和 `msg` 均为字符串类型,`tag` 用于标记日志的类别,值为 “system” 的 tag 被插件系统内部保留使用,`msg` 是日志内容,`tag` 和 `msg` 均不得为空。
252 | - 函数无返回值。
253 | ```
254 | 注意:大量发送日志会给 SafeLine 后端造成压力,因此插件平台会限制日志的发送频率,超过阈值的日志会被直接丢弃。
255 | ```
256 |
257 | ### 其他类
258 |
259 | `function skynet.get_target()`:获取当前请求的 target 信息,可能的返回值如下:
260 | - `skynet.MATCH_TARGET_ACCESS` 普通的访问请求
261 | - `skynet.MATCH_TARGET_DETECT` 被 SafeLine 命中的请求,可能是一个攻击请求
262 |
263 | ```
264 | 注意: 该函数不适用于 ticker 插件。
265 | ```
266 |
267 | `function skynet.get_sesion()`:
268 | - 获取当前请求的 session 信息。
269 | - 返回值为二维数组,内层数组长度固定为 2。其第一个元素为 session id,第二个元素为 session value。
270 | ```
271 | [
272 | ["session_id", "session_value"],
273 | ["session_id", "session_value"],
274 | ...
275 | ]
276 | ```
277 | ```
278 | 注意: 该函数不适用于 ticker 插件。
279 | ```
280 |
281 | `function skynet.get_detailed_info()`:
282 | - 获取当前请求的详细信息,返回值为 table,其索引随着 target 的不同。
283 | ```
284 | 注意: 该函数不适用于 ticker 插件。
285 | ```
286 | - 返回值为 table。
287 |
288 |
289 | 当请求的 target 是 `skynet.MATCH_TARGET_ACCESS` 是,其内容为:
290 | ```
291 | {
292 | ip = "", -- IP,可能是代理服务器的 IP 地址
293 | time = 0, -- 消耗后端服务器的时间,以秒为单位
294 | host = "", -- 域名
295 | uri = "", -- 访问路径
296 | src_ip = "", -- 真正的源 IP,可能为空
297 | }
298 | ```
299 |
300 | 当请求的 target 是 `skynet.MATCH_TARGET_DETECT` 时,说明这是一个被 SafeLine 拦截的请求,其部分内容为(完整内容待补全):
301 | ```
302 |
303 | {
304 | node = "", -- 检测服务器节点名称
305 | timestamp = 0, -- Unix 时间戳
306 | host = "", -- 域名
307 | attack_type = 1, -- 攻击类型
308 | risk_level = 0, -- 风险级别,其取值和 skynet.stat_risk() 返回的 table 的索引相同
309 | event_id = "", -- 该次拦截的事件 ID
310 | req_header_raw = "", -- 经过编码的请求的 HTTP 头
311 | rule_id = "", -- 命中该次拦截的规则 ID
312 | src_ip = "", -- 源 IP
313 | user_agent = "", -- User Agent
314 | urlpath = "", -- 访问路径
315 | action = 1, -- SafeLine 对此请求执行的动作
316 | method = "", -- 请求的方法,比如 GET/POST 等
317 | dest_ip = "", -- 请求的目的 IP
318 | resp_header_raw = "", -- 原始响应头
319 | resp_body = "", -- 原始响应内容
320 | }
321 | ```
322 |
323 | ## 示例
324 | 一个典型的插件如下所示:
325 | ```lua
326 | local skynet = require "skynet"
327 |
328 | match = {
329 | ip = "0.0.0.0/0",
330 | host = ".+",
331 | urlpath = ".+",
332 | type = skynet.MATCH_TYPE_ALL,
333 | target = skynet.MATCH_TARGET_ALL,
334 | }
335 |
336 | function process(ip, host, urlpath)
337 | skynet.log("process", "matched")
338 |
339 | local m = {
340 | ip = ip,
341 | host = host,
342 | }
343 |
344 | for i, v in ipairs(skynet.get_session()) do
345 | skynet.log("process", v[0] .. ": " .. v[1])
346 | end
347 |
348 | local c = skynet.stat_visit(m, 10)
349 |
350 | if c > 30 then
351 | skynet.action_ban(m, 60)
352 | end
353 | end
354 |
355 | skynet.register(skynet.TYPE_PROCESS, match, process)
356 | ```
357 |
--------------------------------------------------------------------------------
/series_10/api_doc/conf.md:
--------------------------------------------------------------------------------
1 | # /api/v1/conf/waf_state/log_ip_source
2 |
3 | ## POST
4 |
5 | 数据格式
6 |
7 | |字段名|数据类型|是否必填/默认值|NULL|其他|
8 | |:--|:-:|:-:|:-:|:-:|
9 | |log_ip_source | 列表 | 必填 | False | - 详见下方表格 |
10 |
11 | log_ip_source
12 |
13 | |字段名|数据类型|是否必填/默认值|NULL|其他|
14 | |:--|:-:|:-:|:-:|:-:|
15 | |子字段 | 字符串 | 必填 | False | 最大长度: 64; |
16 |
17 |
18 |
19 | ### 设置检测日志 ip 来源
20 |
21 | 对于 x-forwarded-for,还可以添加 :-1 :-2 等后缀表示取第几个ip
22 |
23 | 请求
24 |
25 | ```js
26 | {
27 | "log_ip_source": [
28 | "Socket",
29 | "X-Forwarded-For",
30 | "My-Header",
31 | "X-Forwarded-For:-1"
32 | ]
33 | }
34 | ```
35 |
36 | 响应
37 |
38 | ```js
39 | {
40 | "err": null,
41 | "msg": "",
42 | "data": {
43 | "log_ip_source": [
44 | "Socket",
45 | "X-Forwarded-For",
46 | "My-Header",
47 | "X-Forwarded-For:-1"
48 | ]
49 | }
50 | }
51 | ```
52 |
53 | # /api/v3/snserver/heartbeat
54 |
55 | ## POST
56 |
57 | 数据格式
58 |
59 | |字段名|数据类型|是否必填/默认值|NULL|其他|
60 | |:--|:-:|:-:|:-:|:-:|
61 | |timestamp | 浮点数字 | 必填 | False | - |
62 | |time_from | 浮点数字 | 必填 | False | - |
63 | |node | 字符串 | 必填 | False | 最大长度: 128; |
64 | |cpu | 浮点数字 | 必填 | False | - |
65 | |mem | 浮点数字 | 必填 | False | - |
66 | |req_num_total | 整型数字 | 必填 | False | - |
67 | |req_num_blocked | 整型数字 | 必填 | False | - |
68 | |req_num_accepted | 整型数字 | 必填 | False | - |
69 | |req_num_handled | 整型数字 | 必填 | False | - |
70 | |req_num_dropped | 整型数字 | 必填 | False | - |
71 | |req_average_time | 浮点数字 | 必填 | False | - |
72 | |ioread | 浮点数字 | 必填 | False | - |
73 | |iowrite | 浮点数字 | 必填 | False | - |
74 | |req_ngx_time | 整型数字 | 必填 | False | - |
75 | |req_ngx_count | 整型数字 | 必填 | False | - |
76 | |detector_version | 整型数字 | 必填 | False | - |
77 | |workers | 列表 | 必填 | False | - 详见下方表格 |
78 | |policy_version | 字典 | - | - | 详见下面表格 |
79 |
80 | workers
81 |
82 | |字段名|数据类型|是否必填/默认值|NULL|其他|
83 | |:--|:-:|:-:|:-:|:-:|
84 | |子字段 | 字典 | - | - | 详见下面表格 |
85 |
86 | 子字段
87 |
88 | |字段名|数据类型|是否必填/默认值|NULL|其他|
89 | |:--|:-:|:-:|:-:|:-:|
90 | |index | 整型数字 | 必填 | False | - |
91 | |cpu | 浮点数字 | 必填 | False | - |
92 | |ioread | 浮点数字 | 必填 | False | - |
93 | |iowrite | 浮点数字 | 必填 | False | - |
94 |
95 |
96 | policy_version
97 |
98 | |字段名|数据类型|是否必填/默认值|NULL|其他|
99 | |:--|:-:|:-:|:-:|:-:|
100 | |waf_rule_version | 整型数字 | 必填 | False | - |
101 | |cc_rule_version | 整型数字 | 必填 | False | - |
102 |
103 |
104 |
105 | ### snserver 发送 heartbeat
106 |
107 |
108 |
109 | 请求
110 |
111 | ```js
112 | {
113 | "timestamp": 1545297403.5166395,
114 | "time_from": 1545297398.5166395,
115 | "node": "node",
116 | "policy_version": {
117 | "waf_rule_version": 1,
118 | "cc_rule_version": 1
119 | },
120 | "cpu": 0.9,
121 | "mem": 0.8,
122 | "req_num_total": 100,
123 | "req_num_blocked": 15,
124 | "req_num_accepted": 15,
125 | "req_num_handled": 15,
126 | "req_num_dropped": 10,
127 | "req_average_time": 100,
128 | "ioread": 1000,
129 | "iowrite": 200,
130 | "req_ngx_time": 1000,
131 | "req_ngx_count": 2000,
132 | "detector_version": 4,
133 | "workers": [
134 | {
135 | "index": 1,
136 | "cpu": 0.6,
137 | "ioread": 300,
138 | "iowrite": 500
139 | }
140 | ]
141 | }
142 | ```
143 |
144 | 响应
145 |
146 | ```js
147 | {
148 | "err": null,
149 | "msg": "",
150 | "data": null
151 | }
152 | ```
153 |
154 | # /api/v1/stat/node/delete
155 |
156 | ## POST
157 |
158 | 数据格式
159 |
160 | |字段名|数据类型|是否必填/默认值|NULL|其他|
161 | |:--|:-:|:-:|:-:|:-:|
162 | |node | 字符串 | 必填 | False | - |
163 |
164 |
165 |
166 | ### 删除检测节点数据
167 |
168 |
169 |
170 | 请求
171 |
172 | ```js
173 | {
174 | "node": "node"
175 | }
176 | ```
177 |
178 | 响应
179 |
180 | ```js
181 | {
182 | "err": null,
183 | "msg": "",
184 | "data": null
185 | }
186 | ```
187 |
188 | # /api/v1/stat/node
189 |
190 | ## GET
191 |
192 |
193 |
194 | ### 获取节点状态
195 |
196 |
197 |
198 | 响应
199 |
200 | ```js
201 | {
202 | "err": null,
203 | "msg": "",
204 | "data": [
205 | {
206 | "node": "node",
207 | "timestamp": 1545297403.2744617,
208 | "last_timestamp": 1545297398.2744617,
209 | "last_update_at": 1545297403.2821705,
210 | "cpu": 0.9,
211 | "mem": 0.8,
212 | "num_request": 20.0,
213 | "num_attack": 3.0,
214 | "num_accepted": 3.0,
215 | "num_handled": 3.0,
216 | "num_dropped": 2.0,
217 | "process_time": 100,
218 | "detector_version": 4,
219 | "ioread": 200.0,
220 | "iowrite": 40.0,
221 | "workers": [
222 | {
223 | "index": 1,
224 | "cpu": 0.6,
225 | "ioread": 60.0,
226 | "iowrite": 100.0
227 | }
228 | ],
229 | "ngx_count": 400.0,
230 | "ngx_time": 200.0,
231 | "status": "normal",
232 | "config": "policy_update_failed"
233 | }
234 | ]
235 | }
236 | ```
237 |
238 | # /api/v1/conf/syslog
239 |
240 | ## GET
241 |
242 |
243 |
244 | ### 获取 syslog 配置,目前 syslog 处于 未配置过的状态
245 |
246 |
247 |
248 | 响应
249 |
250 | ```js
251 | {
252 | "err": null,
253 | "msg": "",
254 | "data": {
255 | "ip": null,
256 | "port": null,
257 | "format": {
258 | "type": "json"
259 | }
260 | }
261 | }
262 | ```
263 |
264 | ### 获取 syslog 配置
265 |
266 |
267 |
268 | 响应
269 |
270 | ```js
271 | {
272 | "err": null,
273 | "msg": "",
274 | "data": {
275 | "ip": "1.2.3.4",
276 | "port": 80,
277 | "format": {
278 | "data": [],
279 | "type": "custom"
280 | }
281 | }
282 | }
283 | ```
284 |
285 | ## PUT
286 |
287 | 数据格式
288 |
289 | |字段名|数据类型|是否必填/默认值|NULL|其他|
290 | |:--|:-:|:-:|:-:|:-:|
291 | |ip | 字符串 | 必填 | False | - |
292 | |port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 |
293 | |format | 字典 | - | - | 详见下面表格 |
294 |
295 | format
296 |
297 | |字段名|数据类型|是否必填/默认值|NULL|其他|
298 | |:--|:-:|:-:|:-:|:-:|
299 | |type | 指定选项 | 必填 | False | 选项是: ['json', 'custom'] |
300 | |data | 列表 | 非必填/无默认值 | False | - 详见下方表格 |
301 |
302 | data
303 |
304 | |字段名|数据类型|是否必填/默认值|NULL|其他|
305 | |:--|:-:|:-:|:-:|:-:|
306 |
307 |
308 |
309 | ### 修改 syslog 配置
310 |
311 |
312 |
313 | 请求
314 |
315 | ```js
316 | {
317 | "ip": "1.2.3.4",
318 | "port": 80,
319 | "format": {
320 | "type": "custom",
321 | "data": []
322 | }
323 | }
324 | ```
325 |
326 | 响应
327 |
328 | ```js
329 | {
330 | "err": null,
331 | "msg": "",
332 | "data": {
333 | "ip": "1.2.3.4",
334 | "port": 80,
335 | "format": {
336 | "data": [],
337 | "type": "custom"
338 | }
339 | }
340 | }
341 | ```
342 |
343 | # /api/v1/conf/ha_status
344 |
345 | ## GET
346 |
347 |
348 |
349 | ### 获取 ha 配置
350 |
351 |
352 |
353 | 响应
354 |
355 | ```js
356 | {
357 | "err": null,
358 | "msg": "",
359 | "data": {
360 | "status": true,
361 | "mode": "master",
362 | "nopreempt": true,
363 | "interface_name": "",
364 | "downgraded": false,
365 | "vrrp_state": "master",
366 | "sync_state": "success",
367 | "state": "normal"
368 | }
369 | }
370 | ```
371 |
372 | # /api/v1/conf/ha
373 |
374 | ## PUT
375 |
376 | 数据格式
377 |
378 | |字段名|数据类型|是否必填/默认值|NULL|其他|
379 | |:--|:-:|:-:|:-:|:-:|
380 | |interface_name | 字符串 | 必填 | False | 最大长度: 128; |
381 | |mode | 指定选项 | 必填 | False | 选项是: ['master', 'backup'] |
382 | |status | 布尔 | 必填 | False | - |
383 | |nopreempt | 布尔 | 必填 | False | - |
384 |
385 |
386 |
387 | ### ha 配置
388 |
389 |
390 |
391 | 请求
392 |
393 | ```js
394 | {
395 | "interface_name": "bonding_0",
396 | "mode": "master",
397 | "status": true,
398 | "nopreempt": true
399 | }
400 | ```
401 |
402 | 响应
403 |
404 | ```js
405 | {
406 | "err": null,
407 | "msg": "",
408 | "data": {
409 | "interface_name": "bonding_0",
410 | "mode": "master",
411 | "status": true,
412 | "nopreempt": true
413 | }
414 | }
415 | ```
416 |
417 | # /api/v1/conf/system_service
418 |
419 | ## GET
420 |
421 | 数据格式
422 |
423 | |字段名|数据类型|是否必填/默认值|NULL|其他|
424 | |:--|:-:|:-:|:-:|:-:|
425 | |services | 指定选项 | 必填 | False | 选项是: ['web_management', 'icmp_echo', 'ssh', 'snmp'] |
426 |
427 |
428 |
429 | ### 获取系统服务设置
430 |
431 |
432 |
433 | 请求
434 |
435 | ```js
436 | {
437 | "services": [
438 | "ssh",
439 | "icmp_echo"
440 | ]
441 | }
442 | ```
443 |
444 | 响应
445 |
446 | ```js
447 | {
448 | "err": null,
449 | "msg": "",
450 | "data": {
451 | "icmp_echo": [
452 | {
453 | "interface": "br0",
454 | "interface_comment": "接口-1",
455 | "enabled": false,
456 | "port": 65535
457 | },
458 | {
459 | "interface": "br1",
460 | "interface_comment": "接口-2",
461 | "enabled": false,
462 | "port": 65535
463 | },
464 | {
465 | "interface": "br2",
466 | "interface_comment": "接口-3",
467 | "enabled": false,
468 | "port": 65535
469 | },
470 | {
471 | "interface": "br3",
472 | "interface_comment": "接口-4",
473 | "enabled": false,
474 | "port": 65535
475 | },
476 | {
477 | "interface": "br4",
478 | "interface_comment": "接口-5",
479 | "enabled": false,
480 | "port": 65535
481 | }
482 | ],
483 | "ssh": [
484 | {
485 | "interface": "br0",
486 | "interface_comment": "接口-1",
487 | "enabled": false,
488 | "port": 65535
489 | },
490 | {
491 | "interface": "br1",
492 | "interface_comment": "接口-2",
493 | "enabled": false,
494 | "port": 65535
495 | },
496 | {
497 | "interface": "br2",
498 | "interface_comment": "接口-3",
499 | "enabled": false,
500 | "port": 65535
501 | },
502 | {
503 | "interface": "br3",
504 | "interface_comment": "接口-4",
505 | "enabled": false,
506 | "port": 65535
507 | },
508 | {
509 | "interface": "br4",
510 | "interface_comment": "接口-5",
511 | "enabled": false,
512 | "port": 65535
513 | }
514 | ]
515 | }
516 | }
517 | ```
518 |
519 | ## PUT
520 |
521 | 数据格式
522 |
523 | |字段名|数据类型|是否必填/默认值|NULL|其他|
524 | |:--|:-:|:-:|:-:|:-:|
525 | |type | 指定选项 | 必填 | False | 选项是: ['web_management', 'icmp_echo', 'ssh', 'snmp'] |
526 | |interface | 字符串 | 必填 | False | 最大长度: 128; |
527 | |enabled | 布尔 | 必填 | False | - |
528 | |port | 整型数字 | 非必填/0 | False | 最小值: 1; 最大值: 65535 |
529 |
530 |
531 |
532 | ### 设置系统服务,其中 icmp_echo 不需要端口
533 |
534 |
535 |
536 | 请求
537 |
538 | ```js
539 | {
540 | "interface": "br0",
541 | "type": "ssh",
542 | "enabled": true,
543 | "port": 22
544 | }
545 | ```
546 |
547 | 响应
548 |
549 | ```js
550 | {
551 | "err": null,
552 | "msg": "",
553 | "data": null
554 | }
555 | ```
556 |
557 |
--------------------------------------------------------------------------------
/series_10/api_doc/metrics.md:
--------------------------------------------------------------------------------
1 | # /api/v1/log/delete
2 |
3 | ## POST
4 |
5 | 数据格式
6 |
7 | |字段名|数据类型|是否必填/默认值|NULL|其他|
8 | |:--|:-:|:-:|:-:|:-:|
9 | |domain | 字符串 | 非必填/无默认值 | False | - |
10 | |time_after | 浮点数字 | 非必填/无默认值 | False | - |
11 | |time_before | 浮点数字 | 非必填/无默认值 | False | - |
12 | |risk_level | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'low', 'medium', 'high'] |
13 | |count | 整型数字 | 非必填/无默认值 | False | - |
14 | |offset | 整型数字 | 非必填/无默认值 | False | - |
15 | |src_ip | 字符串 | 非必填/无默认值 | False | - |
16 | |attack_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'] |
17 | |action | 指定选项 | 非必填/无默认值 | False | 选项是: ['allow', 'deny'] |
18 | |event_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; |
19 | |rule_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; |
20 |
21 |
22 |
23 | ### 删除防护日志
24 |
25 |
26 |
27 | 请求
28 |
29 | ```js
30 | {
31 | "risk_level": [
32 | "high",
33 | "low"
34 | ],
35 | "attack_type": [
36 | "sql_injection",
37 | "xss"
38 | ],
39 | "action": [
40 | "deny",
41 | "allow"
42 | ]
43 | }
44 | ```
45 |
46 | 响应
47 |
48 | ```js
49 | {
50 | "err": null,
51 | "msg": "",
52 | "data": {}
53 | }
54 | ```
55 |
56 | ### 删除单条日志
57 |
58 |
59 |
60 | 请求
61 |
62 | ```js
63 | {
64 | "event_id": "064d9798c40f41bcb7cd7c7aec008ade"
65 | }
66 | ```
67 |
68 | 响应
69 |
70 | ```js
71 | {
72 | "err": null,
73 | "msg": "",
74 | "data": {}
75 | }
76 | ```
77 |
78 | # /api/v1/log/detail
79 |
80 | ## GET
81 |
82 |
83 |
84 | ### 获取攻击日志详情
85 |
86 |
87 |
88 | 请求
89 |
90 | ```js
91 | {
92 | "id": 0
93 | }
94 | ```
95 |
96 | 响应
97 |
98 | ```js
99 | {
100 | "err": null,
101 | "msg": "",
102 | "data": {
103 | "id": 0,
104 | "timestamp": 1545297166.357962,
105 | "node": "node",
106 | "province": "北京",
107 | "src_ip": "66.161.242.142",
108 | "src_port": 80,
109 | "dest_ip": "192.168.2.1",
110 | "dest_port": 80,
111 | "payload": "payload",
112 | "location": "location",
113 | "event_id": "064d9798c40f41bcb7cd7c7aec008ade",
114 | "selector_id": "100",
115 | "attack_type": "xss",
116 | "risk_level": "high",
117 | "action": "deny",
118 | "method": "GET",
119 | "host": "test.com",
120 | "urlpath": "/test",
121 | "decode_path": "decode_path",
122 | "session": {
123 | "session": {
124 | "session": "test"
125 | }
126 | },
127 | "req_header_raw": "req_header_raw",
128 | "req_body": "req_body",
129 | "resp_header_raw": "resp_header_raw",
130 | "resp_body": "resp_body",
131 | "module": "",
132 | "reason": "发现 XSS 攻击"
133 | }
134 | }
135 | ```
136 |
137 | # /api/v1/log
138 |
139 | ## GET
140 |
141 | 数据格式
142 |
143 | |字段名|数据类型|是否必填/默认值|NULL|其他|
144 | |:--|:-:|:-:|:-:|:-:|
145 | |domain | 字符串 | 非必填/无默认值 | False | - |
146 | |time_after | 浮点数字 | 非必填/无默认值 | False | - |
147 | |time_before | 浮点数字 | 非必填/无默认值 | False | - |
148 | |risk_level | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'low', 'medium', 'high'] |
149 | |count | 整型数字 | 非必填/无默认值 | False | - |
150 | |offset | 整型数字 | 非必填/无默认值 | False | - |
151 | |src_ip | 字符串 | 非必填/无默认值 | False | - |
152 | |attack_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'] |
153 | |action | 指定选项 | 非必填/无默认值 | False | 选项是: ['allow', 'deny'] |
154 | |event_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; |
155 | |rule_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; |
156 |
157 |
158 |
159 | ### 获取日志列表
160 |
161 |
162 |
163 | 响应
164 |
165 | ```js
166 | {
167 | "err": null,
168 | "msg": "",
169 | "data": {
170 | "total": 3,
171 | "items": [
172 | {
173 | "id": 0,
174 | "timestamp": 1545297166.616753,
175 | "node": "node",
176 | "province": "北京",
177 | "src_ip": "124.89.111.235",
178 | "src_port": 80,
179 | "dest_ip": "192.168.2.1",
180 | "dest_port": 80,
181 | "payload": "payload",
182 | "location": "location",
183 | "event_id": "064d9798c40f41bcb7cd7c7aec008ade",
184 | "selector_id": "100",
185 | "attack_type": "xss",
186 | "risk_level": "high",
187 | "action": "deny",
188 | "method": "GET",
189 | "host": "test.com",
190 | "urlpath": "/test",
191 | "decode_path": "decode_path",
192 | "session": {
193 | "session": {
194 | "session": "test"
195 | }
196 | },
197 | "req_header_raw": "req_header_raw",
198 | "req_body": "req_body",
199 | "resp_header_raw": "resp_header_raw",
200 | "resp_body": "resp_body",
201 | "module": "",
202 | "reason": "发现 XSS 攻击"
203 | },
204 | {
205 | "id": 1,
206 | "timestamp": 1545210766.737222,
207 | "node": "node",
208 | "province": "北京",
209 | "src_ip": "249.135.0.145",
210 | "src_port": 80,
211 | "dest_ip": "192.168.2.1",
212 | "dest_port": 80,
213 | "payload": "payload",
214 | "location": "location",
215 | "event_id": "064d9798c40f41bcb7cd7c7aec008ade",
216 | "selector_id": "100",
217 | "attack_type": "xss",
218 | "risk_level": "high",
219 | "action": "deny",
220 | "method": "GET",
221 | "host": "test.com",
222 | "urlpath": "/test",
223 | "decode_path": "decode_path",
224 | "session": {
225 | "session": {
226 | "session": "test"
227 | }
228 | },
229 | "req_header_raw": "req_header_raw",
230 | "req_body": "req_body",
231 | "resp_header_raw": "resp_header_raw",
232 | "resp_body": "resp_body",
233 | "module": "",
234 | "reason": "发现 XSS 攻击"
235 | },
236 | {
237 | "id": 2,
238 | "timestamp": 1545124366.738601,
239 | "node": "node",
240 | "province": "北京",
241 | "src_ip": "82.39.218.183",
242 | "src_port": 80,
243 | "dest_ip": "192.168.2.1",
244 | "dest_port": 80,
245 | "payload": "payload",
246 | "location": "location",
247 | "event_id": "064d9798c40f41bcb7cd7c7aec008ade",
248 | "selector_id": "100",
249 | "attack_type": "xss",
250 | "risk_level": "high",
251 | "action": "deny",
252 | "method": "GET",
253 | "host": "test.com",
254 | "urlpath": "/test",
255 | "decode_path": "decode_path",
256 | "session": {
257 | "session": {
258 | "session": "test"
259 | }
260 | },
261 | "req_header_raw": "req_header_raw",
262 | "req_body": "req_body",
263 | "resp_header_raw": "resp_header_raw",
264 | "resp_body": "resp_body",
265 | "module": "",
266 | "reason": "发现 XSS 攻击"
267 | }
268 | ]
269 | }
270 | }
271 | ```
272 |
273 | # /api/v1/stat/attack_number_trend
274 |
275 | ## GET
276 |
277 | 数据格式
278 |
279 | |字段名|数据类型|是否必填/默认值|NULL|其他|
280 | |:--|:-:|:-:|:-:|:-:|
281 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
282 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
283 | |domain | 字符串 | 非必填/无默认值 | False | - |
284 |
285 |
286 |
287 | ### 获取攻击数时间趋势
288 |
289 |
290 |
291 | 响应
292 |
293 | ```js
294 | {
295 | "err": null,
296 | "msg": "",
297 | "data": [
298 | {
299 | "time": 1545235200,
300 | "count": 1
301 | }
302 | ]
303 | }
304 | ```
305 |
306 | # /api/v1/stat/req_number_trend
307 |
308 | ## GET
309 |
310 | 数据格式
311 |
312 | |字段名|数据类型|是否必填/默认值|NULL|其他|
313 | |:--|:-:|:-:|:-:|:-:|
314 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
315 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
316 |
317 |
318 |
319 | ### 获取请求数时间趋势
320 |
321 |
322 |
323 | 响应
324 |
325 | ```js
326 | {
327 | "err": null,
328 | "msg": "",
329 | "data": [
330 | {
331 | "time": 1545235200,
332 | "count": 100
333 | }
334 | ]
335 | }
336 | ```
337 |
338 | # /api/v1/stat/risk_level
339 |
340 | ## GET
341 |
342 | 数据格式
343 |
344 | |字段名|数据类型|是否必填/默认值|NULL|其他|
345 | |:--|:-:|:-:|:-:|:-:|
346 | |time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; |
347 | |time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; |
348 | |domain | 字符串 | 非必填/无默认值 | False | - |
349 |
350 |
351 |
352 | ### 获取攻击等级数据
353 |
354 |
355 |
356 | 响应
357 |
358 | ```js
359 | {
360 | "err": null,
361 | "msg": "",
362 | "data": [
363 | {
364 | "risk_level": "low",
365 | "count": 1
366 | }
367 | ]
368 | }
369 | ```
370 |
371 | # /api/v1/log/archive/period
372 |
373 | ## GET
374 |
375 |
376 |
377 | ### 获取归档时间
378 |
379 |
380 |
381 | 响应
382 |
383 | ```js
384 | {
385 | "err": null,
386 | "msg": "",
387 | "data": {
388 | "length": 6,
389 | "date": "month"
390 | }
391 | }
392 | ```
393 |
394 | ## PUT
395 |
396 | 数据格式
397 |
398 | |字段名|数据类型|是否必填/默认值|NULL|其他|
399 | |:--|:-:|:-:|:-:|:-:|
400 | |date | 指定选项 | 必填 | False | 选项是: ['month', 'week', 'day'] |
401 | |length | 整型数字 | 必填 | False | 最小值: 1; |
402 |
403 |
404 |
405 | ### 获取归档历史纪录
406 |
407 |
408 |
409 | 请求
410 |
411 | ```js
412 | {
413 | "date": "month",
414 | "length": 5
415 | }
416 | ```
417 |
418 | 响应
419 |
420 | ```js
421 | {
422 | "err": null,
423 | "msg": "",
424 | "data": {
425 | "length": 5,
426 | "date": "month"
427 | }
428 | }
429 | ```
430 |
431 | ### 修改归档时间
432 |
433 |
434 |
435 | 请求
436 |
437 | ```js
438 | {
439 | "date": "month",
440 | "length": 1
441 | }
442 | ```
443 |
444 | 响应
445 |
446 | ```js
447 | {
448 | "err": null,
449 | "msg": "",
450 | "data": {
451 | "length": 1,
452 | "date": "month"
453 | }
454 | }
455 | ```
456 |
457 |
--------------------------------------------------------------------------------
/series_20/plugin_demo/封禁特定地区|基于公开情报进行封禁/plugin.lua:
--------------------------------------------------------------------------------
1 | local safeline = require "safeline"
2 |
3 | -- ipdata.co API 地址
4 | local url = "https://api.ipdata.co/%s?api-key=%s"
5 |
6 | -- api key
7 | local api_key = "****" -- 替换为自己的 key
8 |
9 | -- 封禁时间
10 | local bantime = 60
11 |
12 | -- 拦截地区,字段可省略
13 | local blocks = {
14 | {country_code = "US"}, -- 拦截美国的请求
15 | {country_code = "CN", region = "zhejiang", city = "hangzhou"}, -- 浙江省杭州市,拼音
16 | {country_code = "CN", region = "sichuan"}, -- 四川省,拼音
17 | {country_code = "CN", region = "beijing"} -- 北京直辖市,拼音
18 | }
19 |
20 | -- 按威胁情报拦截,表项为 true 表示封禁该类型的 ip
21 | local block_by_threat = {
22 | is_threat = true, -- 威胁 ip
23 | is_anonymous = true, -- 匿名访问者
24 | is_bogon = true, -- 伪装身份
25 | is_abuser = true, -- 刷子(刷分、羊毛)
26 | is_proxy = true, -- 代理节点
27 | is_tor = true, -- TOR 节点
28 | is_known_attacker = true -- 已知攻击者
29 | }
30 |
31 | match = {
32 | ip = "0.0.0.0/0",
33 | host = ".+",
34 | urlpath = ".+",
35 | type = safeline.MATCH_TYPE_ALL
36 | }
37 |
38 | function make_region_key(t)
39 | local rk = ""
40 | local country = t["country_code"]
41 | if country ~= nil then
42 | rk = rk .. country
43 | else
44 | rk = rk .. "CN"
45 | end
46 | local region = t["region"]
47 | if region ~= nil and string.len(region) > 0 then
48 | rk = rk .. "_" .. region
49 | local city = t["city"]
50 | if city ~= nil and string.len(city) > 0 then
51 | rk = rk .. "_" .. city
52 | end
53 | end
54 | return string.lower(rk)
55 | end
56 |
57 | local block_region_keys = {}
58 | for i, v in ipairs(blocks) do table.insert(block_region_keys, make_region_key(v)) end
59 |
60 | function dump(o)
61 | if type(o) == 'table' then
62 | local s = '{ '
63 | for k, v in pairs(o) do
64 | if type(k) ~= 'number' then k = '"' .. k .. '"' end
65 | s = s .. k .. ':' .. dump(v) .. ','
66 | end
67 | s = s:sub(1, -2)
68 | return s .. '} '
69 | else
70 | if type(o) == 'number' then
71 | return tostring(o)
72 | else
73 | return '"' .. tostring(o) .. '"'
74 | end
75 | end
76 | end
77 |
78 | --[[ json.lua
79 | A compact pure-Lua JSON library.
80 | The main functions are: json.stringify, json.parse.
81 | ## json.stringify:
82 | This expects the following to be true of any tables being encoded:
83 | * They only have string or number keys. Number keys must be represented as
84 | strings in json; this is part of the json spec.
85 | * They are not recursive. Such a structure cannot be specified in json.
86 | A Lua table is considered to be an array if and only if its set of keys is a
87 | consecutive sequence of positive integers starting at 1. Arrays are encoded like
88 | so: `[2, 3, false, "hi"]`. Any other type of Lua table is encoded as a json
89 | object, encoded like so: `{"key1": 2, "key2": false}`.
90 | Because the Lua nil value cannot be a key, and as a table value is considerd
91 | equivalent to a missing key, there is no way to express the json "null" value in
92 | a Lua table. The only way this will output "null" is if your entire input obj is
93 | nil itself.
94 | An empty Lua table, {}, could be considered either a json object or array -
95 | it's an ambiguous edge case. We choose to treat this as an object as it is the
96 | more general type.
97 | To be clear, none of the above considerations is a limitation of this code.
98 | Rather, it is what we get when we completely observe the json specification for
99 | as arbitrary a Lua object as json is capable of expressing.
100 | ## json.parse:
101 | This function parses json, with the exception that it does not pay attention to
102 | \u-escaped unicode code points in strings.
103 | It is difficult for Lua to return null as a value. In order to prevent the loss
104 | of keys with a null value in a json string, this function uses the one-off
105 | table value json.null (which is just an empty table) to indicate null values.
106 | This way you can check if a value is null with the conditional
107 | `val == json.null`.
108 | If you have control over the data and are using Lua, I would recommend just
109 | avoiding null values in your data to begin with.
110 | --]]
111 |
112 | local json = {}
113 |
114 | -- Internal functions.
115 |
116 | local function kind_of(obj)
117 | if type(obj) ~= 'table' then return type(obj) end
118 | local i = 1
119 | for _ in pairs(obj) do
120 | if obj[i] ~= nil then
121 | i = i + 1
122 | else
123 | return 'table'
124 | end
125 | end
126 | if i == 1 then
127 | return 'table'
128 | else
129 | return 'array'
130 | end
131 | end
132 |
133 | local function escape_str(s)
134 | local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
135 | local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
136 | for i, c in ipairs(in_char) do s = s:gsub(c, '\\' .. out_char[i]) end
137 | return s
138 | end
139 |
140 | -- Returns pos, did_find; there are two cases:
141 | -- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
142 | -- 2. Delimiter not found: pos = pos after leading space; did_find = false.
143 | -- This throws an error if err_if_missing is true and the delim is not found.
144 | local function skip_delim(str, pos, delim, err_if_missing)
145 | pos = pos + #str:match('^%s*', pos)
146 | if str:sub(pos, pos) ~= delim then
147 | if err_if_missing then
148 | safeline.log("test",
149 | 'Expected ' .. delim .. ' near position ' .. pos)
150 | end
151 | return pos, false
152 | end
153 | return pos + 1, true
154 | end
155 |
156 | -- Expects the given pos to be the first character after the opening quote.
157 | -- Returns val, pos; the returned pos is after the closing quote character.
158 | local function parse_str_val(str, pos, val)
159 | val = val or ''
160 | local early_end_error = 'End of input found while parsing string.'
161 | if pos > #str then safeline.log("test", early_end_error) end
162 | local c = str:sub(pos, pos)
163 | if c == '"' then return val, pos + 1 end
164 | if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
165 | -- We must have a \ character.
166 | local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
167 | local nextc = str:sub(pos + 1, pos + 1)
168 | if not nextc then safeline.log("test", early_end_error) end
169 | return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
170 | end
171 |
172 | -- Returns val, pos; the returned pos is after the number's final character.
173 | local function parse_num_val(str, pos)
174 | local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
175 | local val = tonumber(num_str)
176 | if not val then
177 | safeline.log("test", 'Error parsing number at position ' .. pos .. '.')
178 | end
179 | return val, pos + #num_str
180 | end
181 |
182 | -- Public values and functions.
183 |
184 | function json.stringify(obj, as_key)
185 | local s = {} -- We'll build the string as an array of strings to be concatenated.
186 | local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise.
187 | if kind == 'array' then
188 | if as_key then
189 | safeline.log("test", 'Can\'t encode array as key.')
190 | end
191 | s[#s + 1] = '['
192 | for i, val in ipairs(obj) do
193 | if i > 1 then s[#s + 1] = ', ' end
194 | s[#s + 1] = json.stringify(val)
195 | end
196 | s[#s + 1] = ']'
197 | elseif kind == 'table' then
198 | if as_key then
199 | safeline.log("test", 'Can\'t encode table as key.')
200 | end
201 | s[#s + 1] = '{'
202 | for k, v in pairs(obj) do
203 | if #s > 1 then s[#s + 1] = ', ' end
204 | s[#s + 1] = json.stringify(k, true)
205 | s[#s + 1] = ':'
206 | s[#s + 1] = json.stringify(v)
207 | end
208 | s[#s + 1] = '}'
209 | elseif kind == 'string' then
210 | return '"' .. escape_str(obj) .. '"'
211 | elseif kind == 'number' then
212 | if as_key then return '"' .. tostring(obj) .. '"' end
213 | return tostring(obj)
214 | elseif kind == 'boolean' then
215 | return tostring(obj)
216 | elseif kind == 'nil' then
217 | return 'null'
218 | else
219 | error('Unjsonifiable type: ' .. kind .. '.')
220 | end
221 | return table.concat(s)
222 | end
223 |
224 | json.null = {} -- This is a one-off table to represent the null value.
225 |
226 | function json.parse(str, pos, end_delim)
227 | pos = pos or 1
228 | if pos > #str then
229 | safeline.log("test", 'Reached unexpected end of input.')
230 | end
231 | local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
232 | local first = str:sub(pos, pos)
233 | if first == '{' then -- Parse an object.
234 | local obj, key, delim_found = {}, true, true
235 | pos = pos + 1
236 | while true do
237 | key, pos = json.parse(str, pos, '}')
238 | if key == nil then return obj, pos end
239 | if not delim_found then
240 | safeline.log("test", 'Comma missing between object items.')
241 | end
242 | pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
243 | obj[key], pos = json.parse(str, pos)
244 | pos, delim_found = skip_delim(str, pos, ',')
245 | end
246 | elseif first == '[' then -- Parse an array.
247 | local arr, val, delim_found = {}, true, true
248 | pos = pos + 1
249 | while true do
250 | val, pos = json.parse(str, pos, ']')
251 | if val == nil then return arr, pos end
252 | if not delim_found then
253 | safeline.log("test", 'Comma missing between array items.')
254 | end
255 | arr[#arr + 1] = val
256 | pos, delim_found = skip_delim(str, pos, ',')
257 | end
258 | elseif first == '"' then -- Parse a string.
259 | return parse_str_val(str, pos + 1)
260 | elseif first == '-' or first:match('%d') then -- Parse a number.
261 | return parse_num_val(str, pos)
262 | elseif first == end_delim then -- End of an object or array.
263 | return nil, pos + 1
264 | else -- Parse true, false, or null.
265 | local literals = {
266 | ['true'] = true,
267 | ['false'] = false,
268 | ['null'] = json.null
269 | }
270 | for lit_str, lit_val in pairs(literals) do
271 | local lit_end = pos + #lit_str - 1
272 | if str:sub(pos, lit_end) == lit_str then
273 | return lit_val, lit_end + 1
274 | end
275 | end
276 | local pos_info_str = 'position ' .. pos .. ': ' ..
277 | str:sub(pos, pos + 10)
278 | safeline.log("test", 'Invalid json syntax starting at ' .. pos_info_str)
279 | end
280 | end
281 |
282 | function banbanban(ip, body)
283 | local banip = {ip = ip}
284 | local num = string.len(body)
285 | local content = json.parse(body, 1, "}")
286 |
287 | -- 检查当前 ip 是否在封禁区域
288 | local max_depth = 1
289 | local matching = {string.lower(content["country_code"])}
290 | if type(content["region"]) == "string" then
291 | table.insert(matching, string.lower(content["region"]))
292 | max_depth = max_depth + 1
293 | end
294 | if type(content["city"]) == "string" then
295 | table.insert(matching, string.lower(content["city"]))
296 | max_depth = max_depth + 1
297 | end
298 |
299 | -- safeline.log("[debug]", dump(matching))
300 | for i, v in ipairs(block_region_keys) do
301 | local match_depth = 1
302 | local do_ban = false
303 | for k in string.gmatch(v, "[^_]+") do
304 | if match_depth > max_depth then break end
305 | if matching[match_depth] == k then
306 | do_ban = true
307 | else
308 | do_ban = false
309 | break
310 | end
311 | match_depth = match_depth + 1
312 | end
313 | if do_ban then
314 | safeline.action_ban(safeline.ACTION_SCOPE_ALL, banip, bantime)
315 | safeline.log("区域封禁", body)
316 | break
317 | end
318 | end
319 |
320 | -- 检查当前 ip 是否命中威胁情报拦截规则
321 | for k, v in pairs(block_by_threat) do
322 | if v == true then
323 | if content["threat"][k] == "true" or content["threat"][k] == true then
324 | safeline.action_ban(safeline.ACTION_SCOPE_ALL, banip, bantime)
325 | safeline.log("威胁情报封禁", body)
326 | break
327 | end
328 | end
329 | end
330 | end
331 |
332 | function process(ip, host, urlpath)
333 | urltmp = string.format(url, ip, api_key)
334 | local resp, err = safeline.http_get(urltmp, {})
335 | if err ~= nil and string.len(err) > 0 then
336 | safeline.log("[http]", dump(err))
337 | else
338 | banbanban(ip, resp["body"])
339 | end
340 | end
341 |
342 | safeline.register(safeline.TYPE_PROCESS, match, process)
343 |
--------------------------------------------------------------------------------
/series_10/api_doc/account.md:
--------------------------------------------------------------------------------
1 | # /api/v1/user/cert
2 |
3 | ## GET
4 |
5 |
6 |
7 | ### 使用 code 下载证书
8 |
9 |
10 |
11 | 请求
12 |
13 | ```js
14 | {
15 | "code": "Z3yjmJlNl5pYYkwO6p"
16 | }
17 | ```
18 |
19 | 响应
20 |
21 | ```js
22 | "不支持展示的数据类型,如文件下载等"
23 | ```
24 |
25 | ### code 不存在
26 |
27 |
28 |
29 | 请求
30 |
31 | ```js
32 | {
33 | "code": "aaaaaaaaaaaaaaaaaa"
34 | }
35 | ```
36 |
37 | 响应
38 |
39 | ```js
40 | {
41 | "err": "error",
42 | "msg": "证书不存在"
43 | }
44 | ```
45 |
46 | ## POST
47 |
48 | 数据格式
49 |
50 | |字段名|数据类型|是否必填/默认值|NULL|其他|
51 | |:--|:-:|:-:|:-:|:-:|
52 | |username | 字符串 | 必填 | False | 最大长度: 32; |
53 |
54 |
55 |
56 | ### 生成证书
57 |
58 |
59 |
60 | 请求
61 |
62 | ```js
63 | {
64 | "username": "username"
65 | }
66 | ```
67 |
68 | 响应
69 |
70 | ```js
71 | {
72 | "err": null,
73 | "msg": "",
74 | "data": {
75 | "code": "ungqMuvOhCNIIyjOfs",
76 | "password": "569ec53c"
77 | }
78 | }
79 | ```
80 |
81 | # /api/v1/user/unlock
82 |
83 | ## POST
84 |
85 | 数据格式
86 |
87 | |字段名|数据类型|是否必填/默认值|NULL|其他|
88 | |:--|:-:|:-:|:-:|:-:|
89 | |username | 字符串 | 必填 | False | 最大长度: 32; |
90 |
91 |
92 |
93 | ### 解锁用户
94 |
95 |
96 |
97 | 请求
98 |
99 | ```js
100 | {
101 | "username": "username"
102 | }
103 | ```
104 |
105 | 响应
106 |
107 | ```js
108 | {
109 | "err": null,
110 | "msg": "",
111 | "data": {
112 | "id": 26,
113 | "create_time": 1545297375.409247,
114 | "is_locked": false,
115 | "username": "username",
116 | "permissions": [
117 | "log_mgmt"
118 | ],
119 | "is_deleted": false,
120 | "ip_policy": [
121 | "*.*.*.*"
122 | ],
123 | "ip_policy_mode": "allow",
124 | "last_change_password": null,
125 | "user_experience_improvement_program": true,
126 | "login_method": "password",
127 | "session_timeout": 172800,
128 | "user_type": "normal"
129 | }
130 | }
131 | ```
132 |
133 | # /api/v1/login
134 |
135 | ## POST
136 |
137 | 数据格式
138 |
139 | |字段名|数据类型|是否必填/默认值|NULL|其他|
140 | |:--|:-:|:-:|:-:|:-:|
141 | |username | 字符串 | 必填 | False | 最大长度: 32; |
142 | |password | 字符串 | 必填 | False | 最大长度: 32; |
143 |
144 |
145 |
146 | ### 用户登录
147 |
148 |
149 |
150 | 请求
151 |
152 | ```js
153 | {
154 | "username": "admin",
155 | "password": "admin"
156 | }
157 | ```
158 |
159 | 响应
160 |
161 | ```js
162 | {
163 | "err": null,
164 | "msg": "",
165 | "data": {
166 | "id": 81,
167 | "create_time": 1545297379.553428,
168 | "is_locked": false,
169 | "username": "admin",
170 | "permissions": [
171 | "log_mgmt"
172 | ],
173 | "is_deleted": false,
174 | "ip_policy": [
175 | "*.*.*.*"
176 | ],
177 | "ip_policy_mode": "allow",
178 | "last_change_password": null,
179 | "user_experience_improvement_program": true,
180 | "login_method": "password",
181 | "session_timeout": 172800,
182 | "user_type": "normal",
183 | "need_change_password": true
184 | }
185 | }
186 | ```
187 |
188 | ### 登录 IP 不在允许范围内
189 |
190 |
191 |
192 | 请求
193 |
194 | ```js
195 | {
196 | "username": "admin",
197 | "password": "admin"
198 | }
199 | ```
200 |
201 | 响应
202 |
203 | ```js
204 | {
205 | "err": "ip-not-allowed",
206 | "msg": "IP不在允许范围内"
207 | }
208 | ```
209 |
210 | ### 该用户只能使用证书登录
211 |
212 |
213 |
214 | 请求
215 |
216 | ```js
217 | {
218 | "username": "admin",
219 | "password": "admin"
220 | }
221 | ```
222 |
223 | 响应
224 |
225 | ```js
226 | {
227 | "err": "login-failed",
228 | "msg": "该用户只能使用证书登录"
229 | }
230 | ```
231 |
232 | ### 用户名或密码错误
233 |
234 |
235 |
236 | 请求
237 |
238 | ```js
239 | {
240 | "username": "admin",
241 | "password": "adminxxx"
242 | }
243 | ```
244 |
245 | 响应
246 |
247 | ```js
248 | {
249 | "err": "login-failed",
250 | "msg": "用户名或密码错误"
251 | }
252 | ```
253 |
254 | # /api/v1/logout
255 |
256 | ## POST
257 |
258 |
259 |
260 | ### 退出登录
261 |
262 |
263 |
264 | 请求
265 |
266 | ```js
267 | {
268 | "action": "logout"
269 | }
270 | ```
271 |
272 | 响应
273 |
274 | ```js
275 | {
276 | "err": null,
277 | "msg": "",
278 | "data": null
279 | }
280 | ```
281 |
282 | # /api/v1/user
283 |
284 | ## GET
285 |
286 |
287 |
288 | ### 使用用户名查询用户信息
289 |
290 |
291 |
292 | 请求
293 |
294 | ```js
295 | {
296 | "username": "test_admin"
297 | }
298 | ```
299 |
300 | 响应
301 |
302 | ```js
303 | {
304 | "err": null,
305 | "msg": "",
306 | "data": {
307 | "id": 44,
308 | "create_time": 1545297376.80086,
309 | "is_locked": false,
310 | "username": "test_admin",
311 | "permissions": [
312 | "log_mgmt",
313 | "user_mgmt",
314 | "waf_mgmt"
315 | ],
316 | "is_deleted": false,
317 | "ip_policy": [
318 | "*.*.*.*"
319 | ],
320 | "ip_policy_mode": "allow",
321 | "last_change_password": null,
322 | "user_experience_improvement_program": true,
323 | "login_method": "password",
324 | "session_timeout": 172800,
325 | "user_type": "normal"
326 | }
327 | }
328 | ```
329 |
330 | ### 获取用户列表
331 |
332 |
333 |
334 | 本 API 支持翻页参数
335 |
336 | 响应
337 |
338 | ```js
339 | {
340 | "err": null,
341 | "msg": "",
342 | "data": [
343 | {
344 | "id": 45,
345 | "create_time": 1545297376.872861,
346 | "is_locked": false,
347 | "username": "test_admin",
348 | "permissions": [
349 | "log_mgmt",
350 | "user_mgmt",
351 | "waf_mgmt"
352 | ],
353 | "is_deleted": false,
354 | "ip_policy": [
355 | "*.*.*.*"
356 | ],
357 | "ip_policy_mode": "allow",
358 | "last_change_password": null,
359 | "user_experience_improvement_program": true,
360 | "login_method": "password",
361 | "session_timeout": 172800,
362 | "user_type": "normal"
363 | },
364 | {
365 | "id": 46,
366 | "create_time": 1545297376.931824,
367 | "is_locked": false,
368 | "username": "other_user",
369 | "permissions": [
370 | "log_mgmt"
371 | ],
372 | "is_deleted": false,
373 | "ip_policy": [
374 | "*.*.*.*"
375 | ],
376 | "ip_policy_mode": "allow",
377 | "last_change_password": null,
378 | "user_experience_improvement_program": true,
379 | "login_method": "password",
380 | "session_timeout": 172800,
381 | "user_type": "normal"
382 | }
383 | ]
384 | }
385 | ```
386 |
387 | ## POST
388 |
389 | 数据格式
390 |
391 | |字段名|数据类型|是否必填/默认值|NULL|其他|
392 | |:--|:-:|:-:|:-:|:-:|
393 | |session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; |
394 | |username | 字符串 | 必填 | False | 最大长度: 32; |
395 | |password | 字符串 | 非必填/无默认值 | False | 最小长度: 0; |
396 | |login_method | 指定选项 | 必填 | False | 选项是: ['cert', 'password'] |
397 | |permissions | 指定选项 | 必填 | False | 选项是: ['user_mgmt', 'waf_mgmt', 'log_mgmt'] |
398 | |ip_policy | 列表 | 必填 | False | - 详见下方表格 |
399 | |ip_policy_mode | 指定选项 | 必填 | False | 选项是: ['allow', 'deny'] |
400 |
401 | ip_policy
402 |
403 | |字段名|数据类型|是否必填/默认值|NULL|其他|
404 | |:--|:-:|:-:|:-:|:-:|
405 | |子字段 | 字符串 | 必填 | False | 最大长度: 64; |
406 |
407 |
408 |
409 | ### 创建日志管理员
410 |
411 |
412 |
413 | 请求
414 |
415 | ```js
416 | {
417 | "username": "other_user",
418 | "password": "password@user",
419 | "permissions": [
420 | "log_mgmt"
421 | ],
422 | "ip_policy": [
423 | "*.*.*.*"
424 | ],
425 | "ip_policy_mode": "allow",
426 | "login_method": "password",
427 | "session_timeout": 60
428 | }
429 | ```
430 |
431 | 响应
432 |
433 | ```js
434 | {
435 | "err": null,
436 | "msg": "",
437 | "data": {
438 | "id": 29,
439 | "create_time": 1545297375.803614,
440 | "is_locked": false,
441 | "username": "other_user",
442 | "permissions": [
443 | "log_mgmt"
444 | ],
445 | "is_deleted": false,
446 | "ip_policy": [
447 | "*.*.*.*"
448 | ],
449 | "ip_policy_mode": "allow",
450 | "last_change_password": null,
451 | "user_experience_improvement_program": true,
452 | "login_method": "password",
453 | "session_timeout": 60,
454 | "user_type": "normal"
455 | }
456 | }
457 | ```
458 |
459 | ### 创建超级管理员
460 |
461 |
462 |
463 | 请求
464 |
465 | ```js
466 | {
467 | "username": "other_user",
468 | "password": "password@user",
469 | "permissions": [
470 | "user_mgmt",
471 | "waf_mgmt",
472 | "log_mgmt"
473 | ],
474 | "ip_policy": [
475 | "*.*.*.*"
476 | ],
477 | "ip_policy_mode": "allow",
478 | "login_method": "password",
479 | "session_timeout": 60
480 | }
481 | ```
482 |
483 | 响应
484 |
485 | ```js
486 | {
487 | "err": null,
488 | "msg": "",
489 | "data": {
490 | "id": 31,
491 | "create_time": 1545297375.938573,
492 | "is_locked": false,
493 | "username": "other_user",
494 | "permissions": [
495 | "user_mgmt",
496 | "waf_mgmt",
497 | "log_mgmt"
498 | ],
499 | "is_deleted": false,
500 | "ip_policy": [
501 | "*.*.*.*"
502 | ],
503 | "ip_policy_mode": "allow",
504 | "last_change_password": null,
505 | "user_experience_improvement_program": true,
506 | "login_method": "password",
507 | "session_timeout": 60,
508 | "user_type": "normal"
509 | }
510 | }
511 | ```
512 |
513 | ### 创建 waf 管理员
514 |
515 |
516 |
517 | 请求
518 |
519 | ```js
520 | {
521 | "username": "other_user",
522 | "password": "password@user",
523 | "permissions": [
524 | "waf_mgmt"
525 | ],
526 | "ip_policy": [
527 | "*.*.*.*"
528 | ],
529 | "ip_policy_mode": "allow",
530 | "login_method": "password",
531 | "session_timeout": 60
532 | }
533 | ```
534 |
535 | 响应
536 |
537 | ```js
538 | {
539 | "err": null,
540 | "msg": "",
541 | "data": {
542 | "id": 38,
543 | "create_time": 1545297376.426044,
544 | "is_locked": false,
545 | "username": "other_user",
546 | "permissions": [
547 | "waf_mgmt"
548 | ],
549 | "is_deleted": false,
550 | "ip_policy": [
551 | "*.*.*.*"
552 | ],
553 | "ip_policy_mode": "allow",
554 | "last_change_password": null,
555 | "user_experience_improvement_program": true,
556 | "login_method": "password",
557 | "session_timeout": 60,
558 | "user_type": "normal"
559 | }
560 | }
561 | ```
562 |
563 | ## PUT
564 |
565 | 数据格式
566 |
567 | |字段名|数据类型|是否必填/默认值|NULL|其他|
568 | |:--|:-:|:-:|:-:|:-:|
569 | |session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; |
570 | |username | 字符串 | 必填 | False | 最大长度: 32; |
571 | |password | 字符串 | 非必填/无默认值 | False | 最小长度: 0; |
572 | |login_method | 指定选项 | 必填 | False | 选项是: ['cert', 'password'] |
573 | |permissions | 指定选项 | 必填 | False | 选项是: ['user_mgmt', 'waf_mgmt', 'log_mgmt'] |
574 | |ip_policy | 列表 | 必填 | False | - 详见下方表格 |
575 | |ip_policy_mode | 指定选项 | 必填 | False | 选项是: ['allow', 'deny'] |
576 |
577 | ip_policy
578 |
579 | |字段名|数据类型|是否必填/默认值|NULL|其他|
580 | |:--|:-:|:-:|:-:|:-:|
581 | |子字段 | 字符串 | 必填 | False | 最大长度: 64; |
582 |
583 |
584 |
585 | ### 修改用户信息
586 |
587 |
588 |
589 | 请求
590 |
591 | ```js
592 | {
593 | "username": "other_user",
594 | "permissions": [
595 | "user_mgmt",
596 | "waf_mgmt",
597 | "log_mgmt"
598 | ],
599 | "ip_policy": [
600 | "*.*.*.*"
601 | ],
602 | "ip_policy_mode": "deny",
603 | "login_method": "password",
604 | "session_timeout": 60
605 | }
606 | ```
607 |
608 | 响应
609 |
610 | ```js
611 | {
612 | "err": null,
613 | "msg": "",
614 | "data": {
615 | "id": 52,
616 | "create_time": 1545297377.20951,
617 | "is_locked": false,
618 | "username": "other_user",
619 | "permissions": [
620 | "user_mgmt",
621 | "waf_mgmt",
622 | "log_mgmt"
623 | ],
624 | "is_deleted": false,
625 | "ip_policy": [
626 | "*.*.*.*"
627 | ],
628 | "ip_policy_mode": "deny",
629 | "last_change_password": null,
630 | "user_experience_improvement_program": true,
631 | "login_method": "password",
632 | "session_timeout": 60,
633 | "user_type": "normal"
634 | }
635 | }
636 | ```
637 |
638 | ### 修改用户密码
639 |
640 | 需要原密码和新密码
641 |
642 | 请求
643 |
644 | ```js
645 | {
646 | "username": "other_user",
647 | "password": "new_password",
648 | "permissions": [
649 | "user_mgmt",
650 | "waf_mgmt",
651 | "log_mgmt"
652 | ],
653 | "ip_policy": [
654 | "*.*.*.*"
655 | ],
656 | "ip_policy_mode": "allow",
657 | "login_method": "password"
658 | }
659 | ```
660 |
661 | 响应
662 |
663 | ```js
664 | {
665 | "err": null,
666 | "msg": "",
667 | "data": {
668 | "id": 62,
669 | "create_time": 1545297377.73444,
670 | "is_locked": false,
671 | "username": "other_user",
672 | "permissions": [
673 | "user_mgmt",
674 | "waf_mgmt",
675 | "log_mgmt"
676 | ],
677 | "is_deleted": false,
678 | "ip_policy": [
679 | "*.*.*.*"
680 | ],
681 | "ip_policy_mode": "allow",
682 | "last_change_password": "2018-12-20T09:16:17.804446Z",
683 | "user_experience_improvement_program": true,
684 | "login_method": "password",
685 | "session_timeout": 172800,
686 | "user_type": "normal"
687 | }
688 | }
689 | ```
690 |
691 | ## DELETE
692 |
693 | 数据格式
694 |
695 | |字段名|数据类型|是否必填/默认值|NULL|其他|
696 | |:--|:-:|:-:|:-:|:-:|
697 | |username | 字符串 | 必填 | False | 最大长度: 32; |
698 |
699 |
700 |
701 | ### 删除用户
702 |
703 |
704 |
705 | 请求
706 |
707 | ```js
708 | {
709 | "username": "other_user"
710 | }
711 | ```
712 |
713 | 响应
714 |
715 | ```js
716 | {
717 | "err": null,
718 | "msg": "",
719 | "data": null
720 | }
721 | ```
722 |
723 | # /api/v1/profile/session_timeout
724 |
725 | ## PUT
726 |
727 | 数据格式
728 |
729 | |字段名|数据类型|是否必填/默认值|NULL|其他|
730 | |:--|:-:|:-:|:-:|:-:|
731 | |session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; |
732 |
733 |
734 |
735 | ### 用户修改自己的session过期时间
736 |
737 |
738 |
739 | 请求
740 |
741 | ```js
742 | {
743 | "session_timeout": 60
744 | }
745 | ```
746 |
747 | 响应
748 |
749 | ```js
750 | {
751 | "err": null,
752 | "msg": "",
753 | "data": null
754 | }
755 | ```
756 |
757 |
--------------------------------------------------------------------------------
/series_20/plugin_sdk.md:
--------------------------------------------------------------------------------
1 | # SafeLine 20 扩展插件文档
2 |
3 |
4 | SafeLine 扩展插件系统(以下简称插件系统)使用 Lua 作为脚本语言,**目前语法兼容标准 Lua 5.1。**
5 |
6 | 在「网站防护」→「扩展插件管理」页面可对插件进行新增,查看,删除等操作。本文档将说明插件的编写方法以及可用的相关接口。
7 |
8 | [toc]
9 |
10 | ## 导入
11 |
12 | 在编写插件时,首先需要导入 safeline 模块,safeline 模块包含了插件系统提供的所有接口。
13 |
14 | ```lua
15 | local safeline = require "safeline"
16 | ```
17 | 注意: 出于安全性的考量,插件脚本中只允许使用有限的标准库函数,其中 IO 库中的所有函数均被禁用,OS 库中仅保留 `time`, `date`, `clock`, `timediff` 等函数。
18 |
19 | ## 注册
20 |
21 | 每个插件至少需要调用一次注册函数以获得一个触发器(Trigger),调用注册函数时通常会指定一个触发条件及其回调函数,当满足条件时该回调函数会被调用。
22 | 注册函数签名如下:
23 | ```lua
24 | function safeline.register(type, ...)
25 | ```
26 | 第一个参数 type 代表触发器的类型,可能的取值及用途见下表。其后的变长参数的数目及类型随着 type 的不同而不同。
27 |
28 | | 触发器类型 | 用途 |
29 | |:--------|:--------:|
30 | | `safeline.TYPE_MATCH` | 处理符合给定条件的请求 |
31 | | `safeline.TYPE_TICKER` | 执行定时任务 |
32 | | `safeline.TYPE_QUERY` | 处理符合给定查询的请求 |
33 |
34 | ### Match 触发器
35 |
36 | Match 触发器需要用户给定一个描述请求特征(仅限于预定义的一些条件)的表 match 和一个回调函数 process,当通过 SafeLine 的请求满足 match 的描述时,回调函数 process 会在该请求的上下文中被调用。其注册方式如下:
37 |
38 | ```lua
39 | local safeline = require "safeline"
40 |
41 | local match = {
42 | ip = "0.0.0.0/0",
43 | host = [[.*\.chaitin\.cn:80]],
44 | url_path = [[/.*safeline]],
45 | target = safeline.MATCH_TARGET_ALL,
46 | }
47 |
48 | function process(ip, host, url_path)
49 | -- 在此处理请求
50 | end
51 |
52 | safeline.register(safeline.TYPE_PROCESS, match, process)
53 | ```
54 | `match` 是一个`table`,用来描述要匹配的请求的特征,其支持的字段见下表:
55 |
56 | | 字段 | 类型 | | 用途 |
57 | |:--------|:--------:| :--------:| :--------: |
58 | | ip | 字符串 | | 匹配请求的源 IP,支持匹配单个 IP 地址(v4 或 v6)或使用 CIDR 记法表示的 IP 段 |
59 | | host | 字符串 | | 匹配请求的目的域名,支持 perl 风格的正则表达式。该域名和 SafeLine 「防护站点」中的「域名」+「监听端口」对应,**即匹配时不省略端口号**。 |
60 | | url_path | 字符串 | | 匹配请求的路径,支持 perl 风格的正则表达式。**路径中不包含[请求参数](https://en.wikipedia.org/wiki/Query_string)**|
61 | | target | 枚举类型 | `safeline.MATCH_TARGET_ALL` | 匹配全部请求(默认值)|
62 | | | | `safeline.MATCH_TARGET_ACCESS` | 匹配普通请求 |
63 | | | | `safeline.MATCH_TARGET_DETECT` | 匹配产生「攻击检测日志」的请求(包括被拦截的以及未拦截但产生日志的请求) |
64 |
65 | ```
66 | 注意:host 和 url_path 中的正则表达式均匹配整个字符串,例如:url_path = "/safeline" 仅匹配访问 “/safeline” 或者 “/safeline?key=xxxx” 的请求,而不匹配访问 “/safeline.html” 的请求。
67 | ```
68 | ```
69 | 提示:
70 | 对于正则表达式,建议使用 Lua 的 raw string 语法 [[ ]] 代替双引号以减少转义符的使用。
71 | 当某个字段被省略时,会被视为匹配所有,即 host 被省略时,和 host = [[.*]] 等价。
72 | ```
73 |
74 | process 是回调函数,当插件触发时被调用,参数的值即为请求对应的源 IP 地址(ip)、目的域名(host)、访问路径(url_path)。函数无返回值。
75 | ```
76 | 例如:对于一个访问 https://www.chaitin.cn/zh/safeline 的请求,其 host 是 "www.chaitin.cn:443",访问路径是 "/zh/safeline"。
77 | ```
78 | 在回调函数中,下列函数可获取当前请求的上下文:
79 | - `target = safeline.get_target()`
80 | - 获取当前请求的 target 信息,可能返回值如下:
81 |
82 | | 返回值 | 含义 |
83 | |:--------:|:--------:|
84 | | safeline.MATCH_TARGET_ACCESS | 普通的访问请求 |
85 | | safeline.MATCH_TARGET_DETECT | 产生「攻击检测日志」的请求(包括被拦截的以及未拦截但产生日志的请求)|
86 |
87 | - `session = safeline.get_session()`
88 | - 获取当前请求的 session 信息
89 | - 返回值为 table,其 key 为 session ID,其 value 为 session value。默认的 session ID 是字符串 "session"。
90 |
91 |
92 | - `info = safeline.get_detailed_info()`
93 | - 获取当前请求的详细信息,返回值为 table,其索引随着 target 的不同。
94 | - 返回值为 table,当请求的 target 是 `safeline.MATCH_TARGET_ACCESS` 是,其内容为:
95 |
96 | | 字段 | 类型 | 含义 |
97 | |:--------:|:--------:|:--------:|
98 | | scheme | 字符串 | 请求的协议,目前可能的取值为 "http"或 "https"|
99 | | method | 字符串 | 请求的[HTTP请求方法](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods) |
100 | | host | 字符串 | 请求的目标域名 |
101 | | port | 整数 | 请求的目标端口 |
102 | | url_path | 字符串 | 请求的路径,不包含请求参数 |
103 | | timestamp | 时间 | 请求到达服务器时的时间戳 |
104 | | time | 浮点数 | 请求消耗业务服务器的时间,单位为秒 |
105 | | status_code| 整数 | 请求的[HTTP响应状态码](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status) |
106 | | ip | 字符串 | 请求的源 IP 地址 |
107 | | req_block_reason | 字符串 | 请求被拦截的原因,可能的取值为 "web" 或 "acl",分别代表被 检测引擎 和 访问控制拦截。请求未被拦截时该字段被省略 |
108 | | resp_block_reason | 字符串 | 该请求的响应被拦截的原因,取值同 req_block_reason |
109 | - 当请求的 target 是 `safeline.MATCH_TARGET_DETECT` 时,其部分内容为:
110 |
111 | | 字段 | 类型 | 含义 |
112 | |:--------:|:--------:|:--------:|
113 | | node | 字符串 | 检测服务器节点名称 |
114 | | timestamp | 整数 | 请求的 Unix 时间戳 |
115 | | host | 字符串 | 请求的目标域名 |
116 | | urlpath | 字符串 | 请求的访问路径 |
117 | | method | 字符串 | 请求的[HTTP请求方法](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods) |
118 | | req_header_raw | 字符串 | 经过编码的 HTTP 请求头 |
119 | | resp_header_raw | 字符串 | 经过编码的 HTTP 响应头 |
120 | | body | 字符串 | 请求体 |
121 | | resp_body | 字符串 | 响应体 |
122 | | src_ip | 字符串 | 请求的源 IP |
123 | | dest_ip | 字符串 | 请求的目的 IP |
124 | | attack_type | 整数 | 请求的攻击类型 |
125 | | risk_level | 字符串 | 请求的风险级别 |
126 | | event_id | 字符串 | 请求的时间 ID |
127 | | rule_id | 字符串 | 命中该次请求的规则 ID |
128 | | user_agent | 字符串 | User Agent |
129 | | action | 整数 | SafeLine 对此请求执行的动作(拦截/放行) |
130 |
131 |
132 | ### Ticker 触发器
133 | Ticker 触发器会根据用户指定的周期定时地调用回调函数,**在集群部署模式下亦能保证该定时器的正确性**。其注册方式如下:
134 | ```lua
135 | local safeline = require "safeline"
136 |
137 | local duration = 10
138 |
139 | function tick(dur)
140 | -- 在此处理定时任务
141 | end
142 |
143 | safeline.register(safeline.TYPE_TICKER, duration, tick)
144 | ```
145 | - durtaion 是一个整数,用来声明插件的触发间隔,其单位为秒。
146 |
147 | - ticker 是插件的回调函数,当插件触发时被调用,参数 dur 的值为插件的触发间隔,其单位为秒。函数无返回值。
148 |
149 | ### Query 触发器
150 | Query 触发器允许用户使用类 SQL 的查询语言对请求进行流式的统计,每次统计结果产生时会将其传递给回调函数。其注册方式如下:
151 | ```lua
152 | safeline = require("safeline")
153 |
154 | query = [[SELECT ip FROM access_log WHERE time > 0.01]]
155 |
156 | function process(key, rows)
157 | -- 在此处理查询结果
158 | end)
159 |
160 | safeline.register(safeline.TYPE_QUERY, query, process)
161 | ```
162 | - query 是用户指定的查询的语句,用法如下
163 | - Plumber SQL 使用
164 | ```sql
165 | --当前支持的语法如下:
166 | SELECT [ DISTINCT ]
167 | expression [ [ AS ] output_name ] [, ...]
168 | [ FROM source_name ]
169 | [ WHERE condition ]
170 | [ GROUP BY window | expression [, ...] ]
171 | [ HAVING condition [, ...] ]
172 | [ ORDER BY expression [ ASC | DESC ] ]
173 | [ LIMIT count ]
174 | [ OFFSET start ];
175 | ```
176 | - Source
177 | ```sql
178 | --Source 代表了一个数据流,当前仅支持在 FROM 子句中直接指定 source_name。
179 | --指定数据源后可在其他字句中直接引用流中的字段名。
180 | SELECT word form article;
181 | SELECT article.word form article;
182 | ```
183 | - 函数
184 | - 窗口函数
185 |
186 | 窗口函数将无边界的数据流按照某种策略划分为有边界的数据分组,每个数据分组被成为一个「窗口」。
187 |
188 | 窗口函数只能在 GROUP BY 子句中使用,在表达式中被引用时,窗口函数的返回值为该窗口的 ID(对于时间窗口,该 ID 为窗口的起始时间戳)。
189 | ```
190 | 注意: 当前仅支持时间窗口。
191 | ```
192 | - `TUMBLE_WINDOW(timestamp, size)`:使用滚动时间窗口,`timestamp` 为划分的时间戳,若数据流中的元素不包含可用的时间戳,可以使用 本地时间 `NOW()` 替代,`size` 为以秒为单位的窗口大小。
193 | - `TUMBLE_WINDOW(timestamp, size, slide)`:使用滑动时间窗口,`timestamp` 为划分的时间戳,若数据流中的元素不包含可用的时间戳,可以使用 本地时间 `NOW()` 替代,`size` 为以秒为单位的窗口大小,`offset` 为以秒为单位的窗口滑动步长。
194 | - 聚合函数
195 | 聚合函数将数据流中的分组聚合为单个元素。
196 | 聚合函数只能在 GROUP BY 中指定了 key 的情况下使用,且不允许在 WHERE 子句中使用。
197 | - `COUNT()`:统计当前分组中元素的个数。
198 | ```
199 | 注意: 暂不支持 COUNT(*)。
200 | ```
201 | - `SUM()`:对当前分组中的元素求和。
202 | - 标记函数
203 | 标量函数可以作为任意表达式使用。
204 | - `NOW()`:返回当前时间。
205 |
206 | - 目前可用的数据源为 "access_log" ,其内容为当前所有请求的概要数据,具体字段如下:
207 |
208 | | 字段 | 类型 | 含义 |
209 | |:--------:|:--------:|:--------:|
210 | | scheme | 字符串 | 请求的协议,目前可能的取值为 "http"或 "https"|
211 | | method | 字符串 | 请求的[HTTP请求方法](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods) |
212 | | host | 字符串 | 请求的目标域名 |
213 | | port | 整数 | 请求的目标端口 |
214 | | url_path | 字符串 | 请求的路径,不包含请求参数 |
215 | | timestamp | 时间 | 请求到达服务器时的时间戳 |
216 | | time | 浮点数 | 请求消耗业务服务器的时间,单位为秒 |
217 | | status_code| 整数 | 请求的[HTTP响应状态码](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status) |
218 | | ip | 字符串 | 请求的源 IP 地址 |
219 | | req_block_reason | 字符串 | 请求被拦截的原因,可能的取值为 "web" 或 "acl",分别代表被 检测引擎 和 访问控制拦截。请求未被拦截时该字段被省略 |
220 | | resp_block_reason | 字符串 | 该请求的响应被拦截的原因,取值同 req_block_reason |
221 | 查询每次返回的结果会作为参数传递给回调函数,若返回结果为数据分组,参数 key 即为分组的 key,参数 rows 即为分组内容;若返回结果为单个元素,则参数 key 为 nil,参数 rows 仅包含一个元素。
222 | ```
223 | 注意:请勿使用 pairs 或 ipairs 遍历 rows 对象,直接 for row in rows do 即可。
224 | ```
225 | ```lua
226 | -- 示例
227 | -- 查询 5 秒内访问超过 100 次的前 10 个 IP:
228 | SELECT ip FROM access_log GROUP BY TUMBLE_WINDOW(timestamp, 5), ip HAING COUNT(ip) > 100 ORDER BY COUNT(ip) DESC LIMIT 10
229 | -- 查询每次请求时间超过 0.01 秒的请求的信息:
230 | SELECT ip, (scheme + "://" + host + url_path) as url FROM access_log WHERE time > 0.01
231 | ```
232 |
233 | ## 接口函数
234 |
235 | 此处列举在扩展插件可供使用的接口函数。所有接口函数均位于 safeline 包内,以 “返回值 = 函数名(参数)”形式描述其签名,多个返回值和参数之间以英文逗号 "," 分隔,变长参数以三个句号 "..." 表示。
236 |
237 | ### 访问频率控制
238 | SafeLine 的「访问控制规则」的部分接口。
239 | 表 key 定义了限制条件:
240 |
241 | | 字段 | 含义 |
242 | |:--------:|:--------:|
243 | | scheme | 限制特定协议的请求,默认为 "http" |
244 | | ip | 限制源 IP 的访问 |
245 | | session | 限制 session 的访问 |
246 | | host | 限制对 host 的访问 |
247 | | url_path | 限制对特定地址的访问 |
248 |
249 | 枚举值 scope 修饰了 key 中的定义:
250 |
251 | | 值 | 含义 |
252 | |:--------:|:--------:|
253 | | safeline.ACTION_SCOPE_ALL | 限制所有请求 |
254 | | safeline.ACTION_SCOPE_URL | 限制访问特定地址的请求 |
255 | | safeline.ACTION_SCOPE_URLPREFIX | 限制访问某特定前缀地址的请求 |
256 |
257 | - `err = safeline.action_ban(scope, key, duration)`
258 | - 封禁符合 key 约束的请求,有效时间为 duration 秒。
259 | - duration 为整型,以秒为单位。
260 | - 提交请求成功时函数返回 nil,失败时返回包含错误信息的字符串。
261 | - `err = safeline.action_limit(scope, key, duration, period, limit)`
262 | - 限制符合 key 约束的请求的访问频率,在 period 内只允许访问 limit 次,有效时间为 duration 秒。
263 | - duration 为整型,以秒为单位;period 为整型,以秒为单位;limit 为整型。
264 | - 提交请求成功时函数返回 nil,失败时返回包含错误信息的字符串。
265 |
266 | ### KV 存储
267 |
268 | 插件系统内置的键值对型数据库(key-value database)接口,所有接口函数的第一个参数需要指定在哪个 DB 中操作,可选值为如下:
269 |
270 | | DB | 用途|
271 | |:--------:|:--------:|
272 | | `safeline.DB_LOCAL` | 对于每个插件是独立的,不同插件之间互不干扰 |
273 | | `safeline.DB_GLOBAL` |在所有插件间共享,不同插件直接可以交换数据 |
274 |
275 | **DB 中的 key 和 value 类型均为字符串**。对于 safeline.db_add 以及 safeline.db_sub 函数,其返回值会自动转换为数字。
276 | ```
277 | 提示:从性能角度考虑,使用 safeline.DB_LOCAL 可以获得更高的性能。
278 | ```
279 |
280 | - `val = safeline.db_get(db, key)`
281 | - 从指定 DB 中获取 key 对应的值。
282 | - 当 key 存在时,返回值为字符串,否则返回 nil。
283 | - `safeline.db_set(db, key, val)`
284 | - 设定指定 DB 中 key 的值为 val。
285 | - 无返回值。
286 | - `safeline.db_del(db, key)`
287 | - 从指定 DB 中删除 key。
288 | - `size = safeline.db_size(db)`
289 | - 获取指定 DB 中 key 数目。
290 | - 返回值为整数。
291 | - `safeline.db_clear(db)`
292 | - 清空指定 DB。
293 | - `result = safeline.db_add(db, key, val)`
294 | - 给指定 DB 中 key 的值加上浮点数 val,即 db[key] += val,key 不需要事先创建,初始值为 0。
295 | - 返回累加后的值,类型为浮点数。
296 | ```
297 | 注意:直接对该 key 调用 safeline.db_get 会返回字符串形式的值。
298 | ```
299 | - `result = safeline.db_sub(db, key, val)`
300 | - 给指定 DB 中 key 的值减去浮点数 val,即 db[key] -= val,key 不需要事先创建,初始值为 0。
301 | - 返回累加后的值,类型为浮点数。
302 | ```
303 | 注意:直接对该 key 调用 safeline.db_get 会返回字符串形式的值。
304 | ```
305 |
306 | ### HTTP 客户端
307 |
308 | - `resp, err = safeline.http_get(url, header)`
309 | - 对地址 url 发起一次 HTTP GET 请求,url 中可携带 GET 参数,在 header 中可指定 HTTP header。
310 | - url 为字符串类型,header 为 table 类型。
311 | - 返回值为中 err 为字符串类型,包含请求错误信息,无错误时值为 nil,当无错误发生时resp 为 table 类型,包含 HTTP 请求的响应,其结构示例如下:
312 | ```
313 | {
314 | status_code = 200,
315 | header = {
316 | -- ...
317 | },
318 | body = "Response body",
319 | }
320 | ```
321 | - `resp, err = safeline.http_post(url, header, data)`
322 | - 对地址 url 发起一次 HTTP POST 请求,在 header 中可指定 HTTP header,data 为 POST 数据。
323 | - url 为字符串类型,header 为 table 类型,data 为字符串类型。
324 | - 返回值同 safeline.http_get()。
325 |
326 | ### 插件日志
327 | - `safeline.log(tag, msg)`
328 | - 产生一条插件日志,日志可以在 SafeLine 管理界面「检测日志」→ 「扩展插件日志」中查看。
329 | - tagtag 被插件系统内部保留使用,msg 是日志内容,tag 和 msg 均不得为空。 和 msg 均为字符串类型,tag 用于标记日志的类别,值为 "system" 的
330 | - 函数无返回值。
331 |
332 |
333 | ### 异步支持
334 | 对于某些需要等待返回结果的操作(比如 action_ban),我们可能需要在较短时间内执行多次(比如封禁 1000 个查询到的违规用户),此时我们就需要将这些任务异步化。
335 |
336 | 本 API 支持 Promise 风格的异步。一个 Promise 定义了一个异步的执行流。Promise 由 Promise 链节点组成,Promise 链中一个节点包含对上一节点返回的结果的处理。
337 |
338 | 比如,我们希望封禁一个用户,并且在日志中记录封禁的成功与否及失败原因。此时,日志记录就应该作为封禁操作的下一个Promise链条节点,因为它需要封禁的结果。
339 |
340 | 和 Promise 有关的 API 有:
341 | - `promiseObject = safeline.promise(func, ...)`
342 | - 创建 Promise 对象并返回,其中 func 是需要执行的异步函数,后面的...是该函数的参数。
343 | - `newPromiseObject = promiseObject(func, ...)`
344 | - 往一个 promise 后添加一个 Promise 链的节点,并返回添加节点后的 Promise,func 是该节点要执行的异步函数,...是该函数的参数。
345 | - 注意,Promise 链条的上一个节点的返回值是当前节点的函数参数,且出现在通过...指定的参数后。
346 | - `promiseObject()`
347 | - 提交一个 Promise 对象,使之能执行。注意,一个 Promise 对象如果不提交,那它不会执行。
348 | - Promise 的执行由 lua 插件的调度器决定在合适的时机执行。
349 | - 以封禁一群用户并记录相应日志为例
350 | ```lua
351 | --同步执行(每封禁一个用户之前必须等待前一个用户的封禁结果返回并日志):
352 | safeline = require("safeline")
353 |
354 | safeline.register(safeline.TYPE_QUERY, [[
355 | SELECT
356 | TUMBLE_WINDOW(timestamp, 1) AS timestamp,
357 | ip,
358 | COUNT(ip) AS count_ip,
359 | SUM(time) AS interval
360 | FROM access_log
361 | GROUP BY TUMBLE_WINDOW(timestamp, 1), ip
362 | HAVING count_ip > 2000
363 | ORDER BY count_ip desc
364 | ]], function(key, rows)
365 | for r in rows do
366 | err = safeline.action_ban(safeline.ACTION_SCOPE_URLPREFIX, {
367 | ip = r.ip,
368 | host = "myhost.com",
369 | urlpath = "/",
370 | scheme = "http"
371 | }, 0)
372 | if err == nil then
373 | safeline.log("Banned user " .. r.ip .. " from accessing.")
374 | else
375 | safeline.log("Cannot ban user " .. r.ip .. " due to error: " .. err)
376 | end
377 | end
378 | end)
379 | --使用 Promise 的例子(封禁不同用户并日志的操作互不相关,在 lua 引擎看来就是不同的执行流):
380 | safeline = require("safeline")
381 |
382 | safeline.register(safeline.TYPE_QUERY, [[
383 | SELECT
384 | TUMBLE_WINDOW(timestamp, 1) AS timestamp,
385 | ip,
386 | COUNT(ip) AS count_ip,
387 | SUM(time) AS interval
388 | FROM access_log
389 | GROUP BY TUMBLE_WINDOW(timestamp, 1), ip
390 | HAVING count_ip > 2000
391 | ORDER BY count_ip desc
392 | ]], function(key, rows)
393 | for r in rows do
394 | err = safeline.action_ban(safeline.ACTION_SCOPE_URLPREFIX, {
395 | ip = r.ip,
396 | host = "myhost.com",
397 | urlpath = "/",
398 | scheme = "http"
399 | }, 0)(function(err)
400 | if err == nil then
401 | safeline.log("Banned user " .. r.ip .. " from accessing.")
402 | else
403 | safeline.log("Cannot ban user " .. r.ip .. " due to error: " .. err)
404 | end
405 | end)()
406 | end
407 | end)
408 | ```
409 |
410 | ## 示例
411 | 一个典型的插件如下所示:
412 | ```lua
413 | local safeline = require "safeline"
414 |
415 | query = [[ SELECT ip, (scheme + '://' + host + url_path) as url, time FROM access_log WHERE time > 0.01 ]]
416 |
417 | function process(key, rows)
418 | for row in rows do
419 | safeline.log("耗时请求", "IP: " .. row.ip .. ", URL: " .. row.url .. ", time: " .. row.time)
420 | end
421 | end
422 |
423 |
424 |
425 | safeline.register(safeline.TYPE_QUERY, query, process)
426 | ```
427 |
428 |
--------------------------------------------------------------------------------
/series_10/api_doc/serializer.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/v1/user/cert:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|username | 字符串 | 必填 | False | 最大长度: 32; | \n\n",
3 | "/api/v1/user/unlock:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|username | 字符串 | 必填 | False | 最大长度: 32; | \n\n",
4 | "/api/v1/user:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; | \n|username | 字符串 | 必填 | False | 最大长度: 32; | \n|password | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|login_method | 指定选项 | 必填 | False | 选项是: ['cert', 'password'] | \n|permissions | 指定选项 | 必填 | False | 选项是: ['user_mgmt', 'waf_mgmt', 'log_mgmt'] | \n|ip_policy | 列表 | 必填 | False | - 详见下方表格 | \n|ip_policy_mode | 指定选项 | 必填 | False | 选项是: ['allow', 'deny'] | \n\nip_policy\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | 最大长度: 64; | \n\n",
5 | "/api/v1/user:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|username | 字符串 | 必填 | False | 最大长度: 32; | \n\n",
6 | "/api/v1/user:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; | \n|username | 字符串 | 必填 | False | 最大长度: 32; | \n|password | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|login_method | 指定选项 | 必填 | False | 选项是: ['cert', 'password'] | \n|permissions | 指定选项 | 必填 | False | 选项是: ['user_mgmt', 'waf_mgmt', 'log_mgmt'] | \n|ip_policy | 列表 | 必填 | False | - 详见下方表格 | \n|ip_policy_mode | 指定选项 | 必填 | False | 选项是: ['allow', 'deny'] | \n\nip_policy\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | 最大长度: 64; | \n\n",
7 | "/api/v1/login:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|username | 字符串 | 必填 | False | 最大长度: 32; | \n|password | 字符串 | 必填 | False | 最大长度: 32; | \n\n",
8 | "/api/v1/profile/session_timeout:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|session_timeout | 整型数字 | 非必填/172800 | False | 最小值: 1; | \n\n",
9 | "/api/v1/acl_rule:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|expire_time | 浮点数字 | 非必填/无默认值 | True | 绝对过期时间,时间戳; | \n|expire_after | 整型数字 | 非必填/无默认值 | True | 多少秒之后过期,和时间戳只传一个即可; 最小值: 1; | \n|domain | 字符串 | 非必填/无默认值 | True | - | \n|urlpath | 字符串 | 非必填/无默认值 | True | - | \n|urlpath_match_method | 指定选项 | 非必填/'str' | False | 选项是: ['str', 'prefix'] | \n|action | 指定选项 | 必填 | False | 选项是: ['forbid', 'limit_rate'] | \n|limit_rate_period | 整型数字 | 非必填/无默认值 | True | 最小值: 1; | \n|limit_rate_limit | 整型数字 | 非必填/无默认值 | True | 最小值: 1; | \n|comment | 字符串 | 非必填/无默认值 | False | 最大长度: 128; 最小长度: 0; | \n|cidr_list | 列表 | 非必填/[] | False | - 详见下方表格 | \n|on_duplicate | 指定选项 | 非必填/'error' | False | 报错或跳过; 选项是: ['skip', 'error'] | \n|status | 指定选项 | 非必填/'enabled' | False | 选项是: ['enabled', 'disabled'] | \n|forbid_type | 指定选项 | 非必填/'cidr' | False | 选项是: ['cidr', 'session'] | \n|session_list | 列表 | 非必填/[] | False | - 详见下方表格 | \n\ncidr_list\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | IPv4的 IP 或者子网形式字符串; | \n\n\nsession_list\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | - | \n\n",
10 | "/api/v1/acl_rule/delete:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|ids | 列表 | 必填 | False | - 详见下方表格 | \n\nids\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 整型数字 | 必填 | False | - | \n\n",
11 | "/api/v1/acl_rule/disable:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|ids | 列表 | 必填 | False | - 详见下方表格 | \n\nids\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 整型数字 | 必填 | False | - | \n\n",
12 | "/api/v1/acl_rule:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|expire_time | 浮点数字 | 非必填/无默认值 | True | 绝对过期时间,时间戳; | \n|expire_after | 整型数字 | 非必填/无默认值 | True | 多少秒之后过期,和时间戳只传一个即可; 最小值: 1; | \n|domain | 字符串 | 非必填/无默认值 | True | - | \n|urlpath | 字符串 | 非必填/无默认值 | True | - | \n|urlpath_match_method | 指定选项 | 非必填/'str' | False | 选项是: ['str', 'prefix'] | \n|action | 指定选项 | 必填 | False | 选项是: ['forbid', 'limit_rate'] | \n|limit_rate_period | 整型数字 | 非必填/无默认值 | True | 最小值: 1; | \n|limit_rate_limit | 整型数字 | 非必填/无默认值 | True | 最小值: 1; | \n|comment | 字符串 | 非必填/无默认值 | False | 最大长度: 128; 最小长度: 0; | \n|cidr_list | 列表 | 非必填/[] | False | - 详见下方表格 | \n|on_duplicate | 指定选项 | 非必填/'error' | False | 报错或跳过; 选项是: ['skip', 'error'] | \n|status | 指定选项 | 非必填/'enabled' | False | 选项是: ['enabled', 'disabled'] | \n|forbid_type | 指定选项 | 非必填/'cidr' | False | 选项是: ['cidr', 'session'] | \n|session_list | 列表 | 非必填/[] | False | - 详见下方表格 | \n|id | 整型数字 | 必填 | False | - | \n\ncidr_list\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | IPv4的 IP 或者子网形式字符串; | \n\n\nsession_list\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | - | \n\n",
13 | "/api/v1/acl/analyze:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_before | 浮点数字 | 非必填/无默认值 | False | - | \n|time_after | 浮点数字 | 非必填/无默认值 | False | - | \n|group_by | 字符串 | 非必填/无默认值 | False | - | \n|order_by | 字符串 | 非必填/无默认值 | False | - | \n|ip | 字符串 | 非必填/无默认值 | False | - | \n|domain | 字符串 | 非必填/无默认值 | False | - | \n|urlpath | 字符串 | 非必填/无默认值 | False | - | \n|offset | 整型数字 | 非必填/0 | False | - | \n|count | 整型数字 | 非必填/10 | False | - | \n\n",
14 | "/api/v1/acl/white_list:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|cidr | 字符串 | 必填 | False | IPv4的 IP 或者子网形式字符串; | \n|comment | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n\n",
15 | "/api/v1/acl/white_list:DELETE": "数据格式\n\n> 只需要传递 ID\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n\n",
16 | "/api/v1/acl/white_list:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|cidr | 字符串 | 必填 | False | IPv4的 IP 或者子网形式字符串; | \n|comment | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|id | 整型数字 | 必填 | False | - | \n\n",
17 | "/api/v1/acl/log/delete:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|cidr | 字符串 | 非必填/无默认值 | False | - | \n|domain | 字符串 | 非必填/无默认值 | False | - | \n|urlpath | 字符串 | 非必填/无默认值 | False | - | \n|rule_id | 整型数字 | 非必填/无默认值 | False | - | \n\n",
18 | "/api/v1/acl/log/download:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|cidr | 字符串 | 非必填/无默认值 | False | - | \n|domain | 字符串 | 非必填/无默认值 | False | - | \n|urlpath | 字符串 | 非必填/无默认值 | False | - | \n|rule_id | 整型数字 | 非必填/无默认值 | False | - | \n\n",
19 | "/api/v1/acl/ip_comment:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|ip | 字符串 | 必填 | False | - | \n|comment | 字符串 | 必填 | True | 最大长度: 128; 最小长度: 0; | \n\n",
20 | "/api/v1/conf/ha:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|interface_name | 字符串 | 必填 | False | 最大长度: 128; | \n|mode | 指定选项 | 必填 | False | 选项是: ['master', 'backup'] | \n|status | 布尔 | 必填 | False | - | \n|nopreempt | 布尔 | 必填 | False | - | \n\n",
21 | "/api/v1/conf/waf_state/log_ip_source:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|log_ip_source | 列表 | 必填 | False | - 详见下方表格 | \n\nlog_ip_source\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | 最大长度: 64; | \n\n",
22 | "/api/v1/stat/node/delete:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|node | 字符串 | 必填 | False | - | \n\n",
23 | "/api/v3/snserver/heartbeat:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|timestamp | 浮点数字 | 必填 | False | - | \n|time_from | 浮点数字 | 必填 | False | - | \n|node | 字符串 | 必填 | False | 最大长度: 128; | \n|cpu | 浮点数字 | 必填 | False | - | \n|mem | 浮点数字 | 必填 | False | - | \n|req_num_total | 整型数字 | 必填 | False | - | \n|req_num_blocked | 整型数字 | 必填 | False | - | \n|req_num_accepted | 整型数字 | 必填 | False | - | \n|req_num_handled | 整型数字 | 必填 | False | - | \n|req_num_dropped | 整型数字 | 必填 | False | - | \n|req_average_time | 浮点数字 | 必填 | False | - | \n|ioread | 浮点数字 | 必填 | False | - | \n|iowrite | 浮点数字 | 必填 | False | - | \n|req_ngx_time | 整型数字 | 必填 | False | - | \n|req_ngx_count | 整型数字 | 必填 | False | - | \n|detector_version | 整型数字 | 必填 | False | - | \n|workers | 列表 | 必填 | False | - 详见下方表格 | \n|policy_version | 字典 | - | - | 详见下面表格 | \n\nworkers\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|index | 整型数字 | 必填 | False | - | \n|cpu | 浮点数字 | 必填 | False | - | \n|ioread | 浮点数字 | 必填 | False | - | \n|iowrite | 浮点数字 | 必填 | False | - | \n\n\npolicy_version\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|waf_rule_version | 整型数字 | 必填 | False | - | \n|cc_rule_version | 整型数字 | 必填 | False | - | \n\n",
24 | "/api/v1/conf/syslog:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|ip | 字符串 | 必填 | False | - | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|format | 字典 | - | - | 详见下面表格 | \n\nformat\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|type | 指定选项 | 必填 | False | 选项是: ['json', 'custom'] | \n|data | 列表 | 非必填/无默认值 | False | - 详见下方表格 | \n\ndata\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n\n",
25 | "/api/v1/conf/system_service:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|services | 指定选项 | 必填 | False | 选项是: ['web_management', 'icmp_echo', 'ssh', 'snmp'] | \n\n",
26 | "/api/v1/conf/system_service:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|type | 指定选项 | 必填 | False | 选项是: ['web_management', 'icmp_echo', 'ssh', 'snmp'] | \n|interface | 字符串 | 必填 | False | 最大长度: 128; | \n|enabled | 布尔 | 必填 | False | - | \n|port | 整型数字 | 非必填/0 | False | 最小值: 1; 最大值: 65535 | \n\n",
27 | "/api/v1/conf2/backend:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|hostname | 字符串 | 必填 | False | - | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n\n",
28 | "/api/v1/conf2/backend:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n|force | 布尔 | 非必填/False | False | - | \n\n",
29 | "/api/v1/conf2/site_backend:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|site_id | 整型数字 | 必填 | False | - | \n|backend_id | 整型数字 | 必填 | False | - | \n|weight | 整型数字 | 必填 | False | 最小值: 1; | \n|is_backup | 布尔 | 非必填/False | False | - | \n\n",
30 | "/api/v1/conf2/site_backend:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|site_id | 整型数字 | 必填 | False | - | \n|backend_id | 整型数字 | 必填 | False | - | \n\n",
31 | "/api/v1/conf2/site_backend:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|site_id | 整型数字 | 必填 | False | - | \n|backend_id | 整型数字 | 必填 | False | - | \n|weight | 整型数字 | 必填 | False | 最小值: 1; | \n|is_backup | 布尔 | 非必填/False | False | - | \n\n",
32 | "/api/v1/conf2/fast_config/s10:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|sites | 字典列表 | - | - | 详见下面表格 | \n\nsites\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|cert_id | 整型数字 | 非必填/无默认值 | False | - | \n|backends | 字典列表 | - | - | 详见下面表格 | \n\nbackends\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n\n",
33 | "/api/v1/conf2/fast_config/w10:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|mode | 指定选项 | 必填 | False | 选项是: ['reverse_proxy', 'transparent_proxy', 'route_proxy'] | \n|inet_mode | 指定选项 | 必填 | False | 选项是: ['dhcp', 'static'] | \n|interface | 字符串 | 必填 | False | - | \n|listeners | 字典列表 | - | - | 详见下面表格 | \n\nlisteners\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|sites | 字典列表 | - | - | 详见下面表格 | \n|hostname | 字符串 | 必填 | False | - | \n\nsites\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|cert_id | 整型数字 | 非必填/无默认值 | False | - | \n|backends | 字典列表 | - | - | 详见下面表格 | \n\nbackends\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n\n",
34 | "/api/v1/conf2/health_check_options:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n|is_enabled | 布尔 | 必填 | False | - | \n|check_http_expect_alive | 指定选项 | 必填 | False | 选项是: ['http_2xx', 'http_3xx', 'http_4xx', 'http_5xx'] | \n|check_type | 指定选项 | 非必填/'http' | False | 选项是: ['tcp', 'http'] | \n|interval | 整型数字 | 非必填/30000 | False | - | \n|fall | 整型数字 | 非必填/5 | False | - | \n|rise | 整型数字 | 非必填/2 | False | - | \n|timeout | 整型数字 | 非必填/1000 | False | - | \n|port | 整型数字 | 非必填/0 | False | - | \n|http_send | 字符串 | 非必填/'GET / HTTP/1.0\\r\\nUser-Agent: SafeLine Health Check\\r\\n\\r\\n' | False | - | \n\n",
35 | "/api/v1/conf2/listener:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|interface | 字符串 | 必填 | False | - | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|hostname | 字符串 | 必填 | False | - | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n\n",
36 | "/api/v1/conf2/listener:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|interface | 字符串 | 必填 | False | - | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|hostname | 字符串 | 必填 | False | - | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|id | 整型数字 | 必填 | False | - | \n|is_disabled | 布尔 | 非必填/False | False | - | \n\n",
37 | "/api/v1/conf2/policy/group:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 必填 | False | - | \n\n",
38 | "/api/v1/conf2/policy/group:DELETE": "数据格式\n\n> 只需要传递 ID\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n\n",
39 | "/api/v1/conf2/policy/group:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n|name | 字符串 | 非必填/无默认值 | False | - | \n|modules_switch | 指定选项 | 非必填/'enabled' | False | 选项是: ['enabled', 'disabled'] | \n|detect_response_switch | 指定选项 | 非必填/'enabled' | False | 选项是: ['enabled', 'disabled'] | \n|modules_state | 字典类型 | - | - | 字典类型 | \n|policies_state | 指定选项 | 非必填/'enabled' | False | 选项是: ['enabled', 'disabled'] | \n|misc_detect_config | 字典类型 | - | - | 字典类型 | \n|is_timeout_enabled | 布尔 | 必填 | False | - | \n|detection_timeout_threshold | 整型数字 | 必填 | False | 最小值: 0; 最大值: 1000000 | \n\nmodules_state\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled', 'dry_run'] | \n|high_risk_action | 指定选项 | 必填 | False | 选项是: ['deny', 'continue'] | \n|high_risk_enable_log | 指定选项 | 必填 | False | 选项是: [0, 1] | \n|medium_risk_action | 指定选项 | 必填 | False | 选项是: ['deny', 'continue'] | \n|medium_risk_enable_log | 指定选项 | 必填 | False | 选项是: [0, 1] | \n|low_risk_action | 指定选项 | 必填 | False | 选项是: ['deny', 'continue'] | \n|low_risk_enable_log | 指定选项 | 必填 | False | 选项是: [0, 1] | \n|detect_config | 字典类型 | - | - | 字典类型 | \n\ndetect_config\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n\n\nmisc_detect_config\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n\n",
40 | "/api/v1/conf2/policy/rule:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 字符串 | 必填 | False | - | \n\n",
41 | "/api/v1/conf2/policy/rule:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|pattern | 字符串 | 必填 | False | - | \n|state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled', 'dry_run'] | \n|action | 指定选项 | 必填 | False | 选项是: ['deny', 'allow'] | \n|comment | 字符串 | 必填 | False | 最大长度: 48; | \n|enable_log | 布尔 | 必填 | False | - | \n|expire_time | 整型数字 | 必填 | False | - | \n|attack_type | 指定选项 | 必填 | False | 选项是: ['none', 'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'] | \n|id | 字符串 | 必填 | False | - | \n\n",
42 | "/api/v1/conf2/policy/rule_priority:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|policy_group_id | 整型数字 | 必填 | False | - | \n|rule_id_list | 列表 | 必填 | False | - 详见下方表格 | \n\nrule_id_list\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | 最大长度: 48; | \n\n",
43 | "/api/v1/conf2/cert:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n|force | 布尔 | 非必填/False | False | - | \n\n",
44 | "/api/v1/conf2/site:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|listener_id | 整型数字 | 必填 | False | - | \n|cert_id | 整型数字 | 非必填/无默认值 | True | - | \n\n",
45 | "/api/v1/conf2/site:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 整型数字 | 必填 | False | - | \n|hostname | 字符串 | 必填 | False | IP 或域名或者泛域名; | \n|cert_id | 整型数字 | 非必填/无默认值 | True | - | \n|is_disabled | 布尔 | 非必填/False | False | - | \n|load_balance_policy | 指定选项 | 非必填/无默认值 | False | 选项是: ['round_bin', 'least_conn', 'ip_hash'] | \n|keepalive | 整型数字 | 非必填/无默认值 | False | 最小值: 0; | \n|forbidden_page_status_code | 整型数字 | 非必填/无默认值 | False | 最小值: 100; 最大值: 999 | \n|ssl_protocols | 列表 | 非必填/无默认值 | False | - 详见下方表格 | \n|ssl_ciphers | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|proxy_ssl_protocols | 列表 | 非必填/无默认值 | False | - 详见下方表格 | \n|proxy_ssl_ciphers | 字符串 | 非必填/无默认值 | False | 最小长度: 0; | \n|x_forwarded_for_option | 指定选项 | 非必填/无默认值 | False | 选项是: ['trust', 'cover', 'append'] | \n\nssl_protocols\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 指定选项 | 必填 | False | 选项是: ['SSLv2', 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2'] | \n\n\nproxy_ssl_protocols\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 指定选项 | 必填 | False | 选项是: ['SSLv2', 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2'] | \n\n",
46 | "/api/v1/conf2/site_proxy_header:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|site_id | 整型数字 | 必填 | False | - | \n|x_forwarded_port | 布尔 | 非必填/无默认值 | False | - | \n|x_forwarded_proto | 布尔 | 非必填/无默认值 | False | - | \n|x_real_ip | 布尔 | 非必填/无默认值 | False | - | \n\n",
47 | "/api/v1/conf2/site/url_prefix:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|session_method_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['re', 'cookie', 'url_query', 'body_form', 'header', 'safeline_session'] | \n|session_method_param | 字符串 | 非必填/无默认值 | False | - | \n|session_method_pattern | 字符串 | 非必填/无默认值 | False | - | \n|url_prefix | 字符串 | 必填 | False | 最大长度: 128; | \n|policy_group | 整型数字 | 必填 | False | - | \n|site_id | 整型数字 | 必填 | False | - | \n\n",
48 | "/api/v1/conf2/site/url_prefix:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|session_method_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['re', 'cookie', 'url_query', 'body_form', 'header', 'safeline_session'] | \n|session_method_param | 字符串 | 非必填/无默认值 | False | - | \n|session_method_pattern | 字符串 | 非必填/无默认值 | False | - | \n|url_prefix | 字符串 | 必填 | False | 最大长度: 128; | \n|policy_group | 整型数字 | 必填 | False | - | \n|id | 整型数字 | 必填 | False | - | \n\n",
49 | "/api/v1/management/curl:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|url | 字符串 | 必填 | False | - | \n\n",
50 | "/api/v1/management/detect_replay:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|target | 字符串 | 必填 | False | - | \n|port | 整型数字 | 必填 | False | 最小值: 1; 最大值: 65535 | \n|protocol | 指定选项 | 必填 | False | 选项是: ['http', 'https'] | \n|content | 字符串 | 必填 | False | - | \n\n",
51 | "/api/v1/management/nslookup:POST": "数据格式\n\n> dns 不填就使用系统的dns\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | 最大长度: 128; | \n|dns | 字符串 | 非必填/'' | False | 最小长度: 0; | \n\n",
52 | "/api/v1/management/ntp_config:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|enabled | 布尔 | 必填 | False | - | \n|servers | 列表 | 非必填/ | False | - 详见下方表格 | \n\nservers\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | - | \n\n",
53 | "/api/v1/management/ping:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | 最大长度: 128; | \n\n",
54 | "/api/v1/management/snmp_config:PUT": "数据格式\n\n> v1 / v2c 协议只需要 community,\n v3 协议只不需要 community\n disable-default-check\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|version | 指定选项 | 必填 | False | 选项是: ['v1v2c', 'v3'] | \n|community | 字符串 | 非必填/无默认值 | False | 最大长度: 24; | \n|username | 字符串 | 非必填/无默认值 | False | 最大长度: 24; | \n|hash_method | 指定选项 | 非必填/无默认值 | False | 选项是: ['MD5', 'SHA'] | \n|password | 字符串 | 非必填/无默认值 | False | 最大长度: 24; 最小长度: 8; | \n|encryption_method | 指定选项 | 非必填/无默认值 | False | 选项是: ['AES', 'DES'] | \n\n",
55 | "/api/v1/management/safeline_config/export:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|smtp_config | 布尔 | 非必填/False | False | - | \n|ip_source | 布尔 | 非必填/False | False | - | \n|waf_resolver | 布尔 | 非必填/False | False | - | \n|cloud_analytics | 布尔 | 非必填/False | False | - | \n|global_waf_state | 布尔 | 非必填/False | False | - | \n|log_ip_source | 布尔 | 非必填/False | False | - | \n|sys_log_config | 布尔 | 非必填/False | False | - | \n|alarm_config | 布尔 | 非必填/False | False | - | \n|certs | 布尔 | 非必填/False | False | - | \n|backends | 布尔 | 非必填/False | False | - | \n|policy | 布尔 | 非必填/False | False | - | \n|acl_rule_template | 布尔 | 非必填/False | False | - | \n|acl_rule | 布尔 | 非必填/False | False | - | \n|acl_cidr_white_list | 布尔 | 非必填/False | False | - | \n\n",
56 | "/api/v1/management/sync_time:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time | 整型数字 | 非必填/0 | False | 最小值: 0; | \n|timezone | 字符串 | 必填 | False | - | \n\n",
57 | "/api/v1/management/traceroute:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|hostname | 字符串 | 必填 | False | 最大长度: 128; | \n\n",
58 | "/api/v1/mario/plugin:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 必填 | False | 最大长度: 64; | \n|comment | 字符串 | 非必填/'' | False | 最小长度: 0; | \n|state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled'] | \n|code | 字符串 | 必填 | False | lua 脚本; | \n\n",
59 | "/api/v1/mario/plugin:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | UUID | 必填 | False | UUID4 | \n\n",
60 | "/api/v1/mario/plugin:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|name | 字符串 | 必填 | False | 最大长度: 64; | \n|comment | 字符串 | 非必填/'' | False | 最小长度: 0; | \n|state | 指定选项 | 必填 | False | 选项是: ['enabled', 'disabled'] | \n|code | 字符串 | 必填 | False | lua 脚本; | \n|id | UUID | 必填 | False | UUID4 | \n\n",
61 | "/api/v1/mario/plugin/log:DELETE": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|plugin_id | 字符串 | 必填 | False | - | \n|tag | 字符串 | 非必填/无默认值 | False | - | \n\n",
62 | "/api/v1/mario/plugin/log:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|plugin_id | 字符串 | 必填 | False | - | \n|tag | 字符串 | 非必填/无默认值 | False | - | \n\n",
63 | "/api/v1/mario/plugin/log/download:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|plugin_id | 字符串 | 必填 | False | - | \n|tag | 字符串 | 非必填/无默认值 | False | - | \n\n",
64 | "/api/v1/mario/plugin/state:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|timestamp | 浮点数字 | 必填 | False | - | \n|id | UUID | 必填 | False | UUID4 | \n|hash | 字符串 | 必填 | False | 最大长度: 32; | \n|state | 字符串 | 必填 | False | 最大长度: 32; | \n|total_execution | 整型数字 | 必填 | False | - | \n|total_error_execution | 整型数字 | 必填 | False | - | \n|execution_per_second | 整型数字 | 必填 | False | - | \n|error_execution_per_second | 整型数字 | 必填 | False | - | \n|average_execution_time | 浮点数字 | 必填 | False | - | \n\n",
65 | "/api/v1/mario/plugin/state/log:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|timestamp | 浮点数字 | 必填 | False | - | \n|id | UUID | 必填 | False | UUID4 | \n|tag | 字符串 | 必填 | False | 最大长度: 32; | \n|msg | 字符串 | 必填 | False | - | \n\n",
66 | "/api/v1/stat/risk_level:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|domain | 字符串 | 非必填/无默认值 | False | - | \n\n",
67 | "/api/v1/stat/attack_number_trend:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n|domain | 字符串 | 非必填/无默认值 | False | - | \n\n",
68 | "/api/v1/log/archive/period:PUT": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|date | 指定选项 | 必填 | False | 选项是: ['month', 'week', 'day'] | \n|length | 整型数字 | 必填 | False | 最小值: 1; | \n\n",
69 | "/api/v1/log/delete:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|domain | 字符串 | 非必填/无默认值 | False | - | \n|time_after | 浮点数字 | 非必填/无默认值 | False | - | \n|time_before | 浮点数字 | 非必填/无默认值 | False | - | \n|risk_level | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'low', 'medium', 'high'] | \n|count | 整型数字 | 非必填/无默认值 | False | - | \n|offset | 整型数字 | 非必填/无默认值 | False | - | \n|src_ip | 字符串 | 非必填/无默认值 | False | - | \n|attack_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'] | \n|action | 指定选项 | 非必填/无默认值 | False | 选项是: ['allow', 'deny'] | \n|event_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; | \n|rule_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; | \n\n",
70 | "/api/v1/log:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|domain | 字符串 | 非必填/无默认值 | False | - | \n|time_after | 浮点数字 | 非必填/无默认值 | False | - | \n|time_before | 浮点数字 | 非必填/无默认值 | False | - | \n|risk_level | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'low', 'medium', 'high'] | \n|count | 整型数字 | 非必填/无默认值 | False | - | \n|offset | 整型数字 | 非必填/无默认值 | False | - | \n|src_ip | 字符串 | 非必填/无默认值 | False | - | \n|attack_type | 指定选项 | 非必填/无默认值 | False | 选项是: ['none', 'sql_injection', 'xss', 'csrf', 'ssrf', 'dos', 'backdoor', 'deserialization', 'code_execution', 'code_injection', 'command_injection', 'file_upload', 'file_inclusion', 'redirect', 'weak_permission', 'info_leak', 'unauthorized_access', 'unsafe_config', 'xxe', 'xpath_injection', 'ldap_injection', 'directory_traversal', 'scanner', 'permission_bypass', 'acl_bypass', 'file_write', 'file_download', 'file_deletion', 'logic_error', 'crlf_injection', 'ssti', 'click_hijacking', 'buffer_overflow', 'integer_overflow', 'format_string', 'race_condition', 'timeout', 'unknown'] | \n|action | 指定选项 | 非必填/无默认值 | False | 选项是: ['allow', 'deny'] | \n|event_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; | \n|rule_id | 字符串 | 非必填/无默认值 | False | 最大长度: 36; | \n\n",
71 | "/api/v1/stat/req_number_trend:GET": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|time_after | 浮点数字 | 非必填/无默认值 | False | 开始时间; 最小值: 0; | \n|time_before | 浮点数字 | 非必填/无默认值 | False | 结束时间; 最小值: 0; | \n\n",
72 | "/api/v1/node/config:POST": "数据格式\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|id | 字符串 | 必填 | False | - | \n|services_status | 字典 | - | - | 详见下面表格 | \n\nservices_status\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|tengine | 字典 | - | - | 详见下面表格 | \n\ntengine\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|version | 整型数字 | 必填 | False | - | \n|status | 字典 | - | - | 详见下面表格 | \n\nstatus\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|status | 字符串 | 必填 | False | - | \n|last_result | 字符串 | 必填 | False | 最小长度: 0; | \n|sites | 字典类型 | - | - | 字典类型 | \n\nsites\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字典 | - | - | 详见下面表格 | \n\n子字段\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|status | 字符串 | 必填 | False | - | \n|upstreams | 字典类型 | - | - | 字典类型 | \n\nupstreams\n\n|字段名|数据类型|是否必填/默认值|NULL|其他|\n|:--|:-:|:-:|:-:|:-:|\n|子字段 | 字符串 | 必填 | False | - | \n\n"
73 | }
--------------------------------------------------------------------------------
/series_20/api_doc/account.md:
--------------------------------------------------------------------------------
1 | # /api/LoginAPI
2 |
3 | ## POST
4 |
5 | ### [数据格式定义]
6 | 数据格式
7 |
8 | |字段名|数据类型|是否必填/默认值|NULL|其他|
9 | |:--|:-:|:-:|:-:|:-:|
10 | |tfa_token | 字符串 | 非必填/'' | False | 两步验证验证码; 最小长度: 0; |
11 | |username | 字符串 | 必填 | False | - |
12 | |password | 字符串 | 必填 | False | - |
13 |
14 | ?> 不需要登录
15 |
16 | ### - 密码登录
17 |
18 |
19 |
20 | 请求
21 |
22 | ```js
23 | {
24 | "username": "admin",
25 | "password": "admin@password"
26 | }
27 | ```
28 |
29 | 响应
30 |
31 | ```js
32 | {
33 | "err": null,
34 | "data": {
35 | "id": 15,
36 | "is_locked": false,
37 | "ip_policy": [],
38 | "last_login": "1544513489",
39 | "username": "admin",
40 | "comment": "",
41 | "tfa_enabled": false,
42 | "create_time": "1544513489",
43 | "last_update_time": "1544513489",
44 | "last_login_time": "1544513489",
45 | "last_change_password_time": "1544513489",
46 | "permissions": [
47 | "System Monitor",
48 | "System Management",
49 | "User Management",
50 | "Website And Security Policy Management",
51 | "Audit Log Management"
52 | ],
53 | "authentication_method": "password",
54 | "session_timeout_time": 172800
55 | },
56 | "msg": null
57 | }
58 | ```
59 |
60 | ### - 使用两步验证登录,验证通过
61 |
62 |
63 |
64 | 请求
65 |
66 | ```js
67 | {
68 | "username": "admin",
69 | "password": "admin@password",
70 | "tfa_token": "foobar"
71 | }
72 | ```
73 |
74 | 响应
75 |
76 | ```js
77 | {
78 | "err": null,
79 | "data": {
80 | "id": 18,
81 | "is_locked": false,
82 | "ip_policy": [],
83 | "last_login": "1544513491",
84 | "username": "admin",
85 | "comment": "",
86 | "tfa_enabled": true,
87 | "create_time": "1544513490",
88 | "last_update_time": "1544513490",
89 | "last_login_time": "1544513491",
90 | "last_change_password_time": "1544513490",
91 | "permissions": [
92 | "System Monitor",
93 | "System Management",
94 | "User Management",
95 | "Website And Security Policy Management",
96 | "Audit Log Management"
97 | ],
98 | "authentication_method": "password",
99 | "session_timeout_time": 172800
100 | },
101 | "msg": null
102 | }
103 | ```
104 |
105 | ### - 密码错误
106 |
107 |
108 |
109 | 请求
110 |
111 | ```js
112 | {
113 | "username": "admin",
114 | "password": "admin@passwordfoobar"
115 | }
116 | ```
117 |
118 | 响应
119 |
120 | ```js
121 | {
122 | "err": "error",
123 | "msg": "Incorrect username or password"
124 | }
125 | ```
126 |
127 | ### - 没有输入两步验证验证码
128 |
129 |
130 |
131 | 请求
132 |
133 | ```js
134 | {
135 | "username": "admin",
136 | "password": "admin@password"
137 | }
138 | ```
139 |
140 | 响应
141 |
142 | ```js
143 | {
144 | "err": "tfa-required",
145 | "msg": "Please input two factor auth token"
146 | }
147 | ```
148 |
149 | # /api/CertLoginAPI
150 |
151 | ## GET
152 |
153 | ?> 不需要登录
154 |
155 | ### - 检查是否安装了证书,证书是否有效
156 |
157 |
158 |
159 | 响应
160 |
161 | ```js
162 | {
163 | "err": null,
164 | "data": {
165 | "result": true
166 | },
167 | "msg": null
168 | }
169 | ```
170 |
171 | ## POST
172 |
173 | ### [数据格式定义]
174 | 数据格式
175 |
176 | |字段名|数据类型|是否必填/默认值|NULL|其他|
177 | |:--|:-:|:-:|:-:|:-:|
178 | |tfa_token | 字符串 | 非必填/'' | False | 两步验证验证码; 最小长度: 0; |
179 |
180 | ?> 不需要登录
181 |
182 | ### - 正常登录
183 |
184 |
185 |
186 | 请求
187 |
188 | ```js
189 | {}
190 | ```
191 |
192 | 响应
193 |
194 | ```js
195 | {
196 | "err": null,
197 | "data": {
198 | "id": 3,
199 | "is_locked": false,
200 | "ip_policy": [],
201 | "last_login": "1544513479",
202 | "username": "admin",
203 | "comment": "",
204 | "tfa_enabled": false,
205 | "create_time": "1544513479",
206 | "last_update_time": "1544513479",
207 | "last_login_time": "1544513479",
208 | "last_change_password_time": "1544513479",
209 | "permissions": [
210 | "System Monitor",
211 | "System Management",
212 | "User Management",
213 | "Website And Security Policy Management",
214 | "Audit Log Management"
215 | ],
216 | "authentication_method": "cert",
217 | "session_timeout_time": 172800
218 | },
219 | "msg": null
220 | }
221 | ```
222 |
223 | ### - 使用两步验证登录,验证通过
224 |
225 |
226 |
227 | 请求
228 |
229 | ```js
230 | {
231 | "tfa_token": "foobar"
232 | }
233 | ```
234 |
235 | 响应
236 |
237 | ```js
238 | {
239 | "err": null,
240 | "data": {
241 | "id": 4,
242 | "is_locked": false,
243 | "ip_policy": [],
244 | "last_login": "1544513480",
245 | "username": "admin",
246 | "comment": "",
247 | "tfa_enabled": true,
248 | "create_time": "1544513479",
249 | "last_update_time": "1544513479",
250 | "last_login_time": "1544513480",
251 | "last_change_password_time": "1544513479",
252 | "permissions": [
253 | "System Monitor",
254 | "System Management",
255 | "User Management",
256 | "Website And Security Policy Management",
257 | "Audit Log Management"
258 | ],
259 | "authentication_method": "cert",
260 | "session_timeout_time": 172800
261 | },
262 | "msg": null
263 | }
264 | ```
265 |
266 | # /api/ResetUserCredentialAPI
267 |
268 | ## PUT
269 |
270 | ### [数据格式定义]
271 | 数据格式
272 |
273 | > 只需要传递 ID
274 |
275 | |字段名|数据类型|是否必填/默认值|NULL|其他|
276 | |:--|:-:|:-:|:-:|:-:|
277 | |id | 整型数字 | 必填 | False | - |
278 |
279 | ### 权限
280 | - User Management
281 |
282 | ### - 管理员重置用户证书
283 |
284 |
285 |
286 | 请求
287 |
288 | ```js
289 | {
290 | "id": 23
291 | }
292 | ```
293 |
294 | 响应
295 |
296 | ```js
297 | {
298 | "err": null,
299 | "data": {
300 | "user": {
301 | "id": 23,
302 | "is_locked": false,
303 | "ip_policy": [],
304 | "last_login": null,
305 | "username": "test",
306 | "comment": "",
307 | "tfa_enabled": false,
308 | "create_time": "1544513493",
309 | "last_update_time": "1544513493",
310 | "last_login_time": null,
311 | "last_change_password_time": "1544513493",
312 | "permissions": [
313 | "System Monitor",
314 | "System Management",
315 | "User Management",
316 | "Website And Security Policy Management",
317 | "Audit Log Management"
318 | ],
319 | "authentication_method": "cert",
320 | "session_timeout_time": 172800
321 | },
322 | "extra": {
323 | "token": "2081824916eaa700b5c9006ffa3697fb:1gWcVy:Dqg8yXQNexzNVjVL0HfenYURwOI",
324 | "password": "m7ezSC"
325 | }
326 | },
327 | "msg": null
328 | }
329 | ```
330 |
331 | ### - 管理员重置用户密码
332 |
333 |
334 |
335 | 请求
336 |
337 | ```js
338 | {
339 | "id": 25
340 | }
341 | ```
342 |
343 | 响应
344 |
345 | ```js
346 | {
347 | "err": null,
348 | "data": {
349 | "user": {
350 | "id": 25,
351 | "is_locked": false,
352 | "ip_policy": [],
353 | "last_login": null,
354 | "username": "test",
355 | "comment": "",
356 | "tfa_enabled": false,
357 | "create_time": "1544513494",
358 | "last_update_time": "1544513494",
359 | "last_login_time": null,
360 | "last_change_password_time": "1544513494",
361 | "permissions": [
362 | "System Monitor",
363 | "System Management",
364 | "User Management",
365 | "Website And Security Policy Management",
366 | "Audit Log Management"
367 | ],
368 | "authentication_method": "password",
369 | "session_timeout_time": 172800
370 | },
371 | "extra": {
372 | "password": "v4DN0m"
373 | }
374 | },
375 | "msg": null
376 | }
377 | ```
378 |
379 | # /api/ChangeUserCredentialAPI
380 |
381 | ## PUT
382 |
383 | ### [数据格式定义]
384 | 数据格式
385 |
386 | |字段名|数据类型|是否必填/默认值|NULL|其他|
387 | |:--|:-:|:-:|:-:|:-:|
388 | |authentication_method | 指定选项 | 必填 | False | 选项是: ['ldap', 'password', 'cert'] |
389 | |old_password | 字符串 | 非必填/'' | False | 最小长度: 0; |
390 | |new_password | 字符串 | 非必填/'' | False | 最小长度: 0; |
391 | |tfa_token | 字符串 | 非必填/'' | False | 最小长度: 0; |
392 |
393 |
394 |
395 | ### - 用户重新生成证书
396 |
397 |
398 |
399 | 请求
400 |
401 | ```js
402 | {
403 | "authentication_method": "cert"
404 | }
405 | ```
406 |
407 | 响应
408 |
409 | ```js
410 | {
411 | "err": null,
412 | "data": {
413 | "user": {
414 | "id": 6,
415 | "is_locked": false,
416 | "ip_policy": [],
417 | "last_login": "1544513482",
418 | "username": "admin",
419 | "comment": "",
420 | "tfa_enabled": false,
421 | "create_time": "1544513480",
422 | "last_update_time": "1544513480",
423 | "last_login_time": "1544513482",
424 | "last_change_password_time": "1544513480",
425 | "permissions": [
426 | "System Monitor",
427 | "System Management",
428 | "User Management",
429 | "Website And Security Policy Management",
430 | "Audit Log Management"
431 | ],
432 | "authentication_method": "cert",
433 | "session_timeout_time": 172800
434 | },
435 | "extra": {
436 | "token": "a8aee7711c0c811d230c1ed52cc9e658:1gWcVm:hPP-lCq5AhY6xj0tf22xCoAZv9I",
437 | "password": "EHhhF8"
438 | }
439 | },
440 | "msg": null
441 | }
442 | ```
443 |
444 | ### - 证书登录切换到密码登录
445 |
446 |
447 |
448 | 请求
449 |
450 | ```js
451 | {
452 | "authentication_method": "password",
453 | "new_password": "somepassword@password"
454 | }
455 | ```
456 |
457 | 响应
458 |
459 | ```js
460 | {
461 | "err": null,
462 | "data": {
463 | "user": {
464 | "id": 7,
465 | "is_locked": false,
466 | "ip_policy": [],
467 | "last_login": "1544513483",
468 | "username": "admin",
469 | "comment": "",
470 | "tfa_enabled": false,
471 | "create_time": "1544513482",
472 | "last_update_time": "1544513482",
473 | "last_login_time": "1544513483",
474 | "last_change_password_time": "1544513484",
475 | "permissions": [
476 | "System Monitor",
477 | "System Management",
478 | "User Management",
479 | "Website And Security Policy Management",
480 | "Audit Log Management"
481 | ],
482 | "authentication_method": "password",
483 | "session_timeout_time": 172800
484 | },
485 | "extra": {}
486 | },
487 | "msg": null
488 | }
489 | ```
490 |
491 | ### - 密码登录切换到证书登录
492 |
493 |
494 |
495 | 请求
496 |
497 | ```js
498 | {
499 | "authentication_method": "cert",
500 | "old_password": "testadmin@password"
501 | }
502 | ```
503 |
504 | 响应
505 |
506 | ```js
507 | {
508 | "err": null,
509 | "data": {
510 | "user": {
511 | "id": 8,
512 | "is_locked": false,
513 | "ip_policy": [],
514 | "last_login": "1544513484",
515 | "username": "admin",
516 | "comment": "",
517 | "tfa_enabled": false,
518 | "create_time": "1544513484",
519 | "last_update_time": "1544513484",
520 | "last_login_time": null,
521 | "last_change_password_time": "1544513484",
522 | "permissions": [
523 | "System Monitor",
524 | "System Management",
525 | "User Management",
526 | "Website And Security Policy Management",
527 | "Audit Log Management"
528 | ],
529 | "authentication_method": "cert",
530 | "session_timeout_time": 172800
531 | },
532 | "extra": {
533 | "token": "2ccd35345fbcba53d23e227dd8b90975:1gWcVp:4Cl9dl3sqW-km38RI_jcZ0ziP4I",
534 | "password": "IqsveW"
535 | }
536 | },
537 | "msg": null
538 | }
539 | ```
540 |
541 | ### - 用户修改密码
542 |
543 |
544 |
545 | 请求
546 |
547 | ```js
548 | {
549 | "authentication_method": "password",
550 | "old_password": "testadmin@password",
551 | "new_password": "somerandom@password"
552 | }
553 | ```
554 |
555 | 响应
556 |
557 | ```js
558 | {
559 | "err": null,
560 | "data": {
561 | "user": {
562 | "id": 9,
563 | "is_locked": false,
564 | "ip_policy": [],
565 | "last_login": "1544513485",
566 | "username": "admin",
567 | "comment": "",
568 | "tfa_enabled": false,
569 | "create_time": "1544513485",
570 | "last_update_time": "1544513485",
571 | "last_login_time": null,
572 | "last_change_password_time": "1544513486",
573 | "permissions": [
574 | "System Monitor",
575 | "System Management",
576 | "User Management",
577 | "Website And Security Policy Management",
578 | "Audit Log Management"
579 | ],
580 | "authentication_method": "password",
581 | "session_timeout_time": 172800
582 | },
583 | "extra": {}
584 | },
585 | "msg": null
586 | }
587 | ```
588 |
589 | # /api/UserAPITokenAPI
590 |
591 | ## GET
592 |
593 |
594 |
595 | ### - 获取所有的 api token 和描述
596 |
597 |
598 |
599 | ?> 本 API 支持翻页参数
600 |
601 | 响应
602 |
603 | ```js
604 | {
605 | "err": null,
606 | "data": [
607 | {
608 | "id": 4,
609 | "value": "TlYBuIwm********************************",
610 | "ip_policy": [
611 | "0.0.0.0/0",
612 | "::/0"
613 | ],
614 | "name": "foobar",
615 | "create_time": "1544513503",
616 | "is_internal": false,
617 | "permissions": [
618 | "User Management"
619 | ],
620 | "expire_time": "1544513603"
621 | }
622 | ],
623 | "msg": null
624 | }
625 | ```
626 |
627 | ## POST
628 |
629 | ### [数据格式定义]
630 | 数据格式
631 |
632 | |字段名|数据类型|是否必填/默认值|NULL|其他|
633 | |:--|:-:|:-:|:-:|:-:|
634 | |name | 字符串 | 必填 | False | - |
635 | |expire_time | 浮点数字 | 非必填/None | True | 为 null 时, 表示永不过期; |
636 | |permissions | 指定选项 | 必填 | False | 选项是: ['System Monitor', 'System Management', 'User Management', 'Website And Security Policy Management', 'Audit Log Management'] |
637 | |ip_policy | 列表 | 必填 | False | [ - 详见下方表格](#nfshkhytihjvuqxtekzhqwrhqsvmnfvz) |
638 |
639 | ip_policy
640 |
641 | |字段名|数据类型|是否必填/默认值|NULL|其他|
642 | |:--|:-:|:-:|:-:|:-:|
643 | |子字段 | 字符串 | 必填 | False | IP 范围; |
644 |
645 |
646 |
647 | ### - 创建 api token
648 |
649 |
650 |
651 | 请求
652 |
653 | ```js
654 | {
655 | "name": "foobar",
656 | "expire_time": 1544513602.0145867,
657 | "permissions": [
658 | "User Management"
659 | ],
660 | "ip_policy": [
661 | "0.0.0.0/0",
662 | "::/0"
663 | ]
664 | }
665 | ```
666 |
667 | 响应
668 |
669 | ```js
670 | {
671 | "err": null,
672 | "data": {
673 | "id": 1,
674 | "value": "rQMxtSfheOlswkE8BBxRhFpP2J7nHCLjCIAq1crv",
675 | "ip_policy": [
676 | "0.0.0.0/0",
677 | "::/0"
678 | ],
679 | "name": "foobar",
680 | "create_time": "1544513502",
681 | "is_internal": false,
682 | "permissions": [
683 | "User Management"
684 | ],
685 | "expire_time": "1544513602"
686 | },
687 | "msg": null
688 | }
689 | ```
690 |
691 | ## PUT
692 |
693 | ### [数据格式定义]
694 | 数据格式
695 |
696 | |字段名|数据类型|是否必填/默认值|NULL|其他|
697 | |:--|:-:|:-:|:-:|:-:|
698 | |id | 整型数字 | 必填 | False | - |
699 | |permissions | 指定选项 | 必填 | False | 选项是: ['System Monitor', 'System Management', 'User Management', 'Website And Security Policy Management', 'Audit Log Management'] |
700 | |ip_policy | 列表 | 必填 | False | [ - 详见下方表格](#acvnlzcwhptxgopdwmlxmulkpovsfsvp) |
701 |
702 | ip_policy
703 |
704 | |字段名|数据类型|是否必填/默认值|NULL|其他|
705 | |:--|:-:|:-:|:-:|:-:|
706 | |子字段 | 字符串 | 必填 | False | IP 范围; |
707 |
708 |
709 |
710 | ### - 编辑 api token 的 permission 和 ip_policy
711 |
712 |
713 |
714 | 请求
715 |
716 | ```js
717 | {
718 | "id": 3,
719 | "permissions": [
720 | "User Management",
721 | "System Management"
722 | ],
723 | "ip_policy": [
724 | "0.0.0.0/0",
725 | "::/0",
726 | "10.0.0.0/24"
727 | ]
728 | }
729 | ```
730 |
731 | 响应
732 |
733 | ```js
734 | {
735 | "err": null,
736 | "data": {
737 | "id": 3,
738 | "value": "KqKNX0CA********************************",
739 | "ip_policy": [
740 | "0.0.0.0/0",
741 | "::/0",
742 | "10.0.0.0/24"
743 | ],
744 | "name": "foobar",
745 | "create_time": "1544513502",
746 | "is_internal": false,
747 | "permissions": [
748 | "System Management",
749 | "User Management"
750 | ],
751 | "expire_time": "1544513602"
752 | },
753 | "msg": null
754 | }
755 | ```
756 |
757 | ## DELETE
758 |
759 | ### [数据格式定义]
760 | 数据格式
761 |
762 | > 只需要传递 ID
763 |
764 | |字段名|数据类型|是否必填/默认值|NULL|其他|
765 | |:--|:-:|:-:|:-:|:-:|
766 | |id | 整型数字 | 必填 | False | - |
767 |
768 |
769 |
770 | ### - 删除 api token
771 |
772 |
773 |
774 | 请求
775 |
776 | ```js
777 | {
778 | "id": 2
779 | }
780 | ```
781 |
782 | 响应
783 |
784 | ```js
785 | {
786 | "err": null,
787 | "data": null,
788 | "msg": null
789 | }
790 | ```
791 |
792 | # /api/UserTFAConfigAPI
793 |
794 | ## GET
795 |
796 |
797 |
798 | ### - 获取验证码链接,前端需要生成二维码,然后使用 app 扫描添加
799 |
800 |
801 |
802 | 响应
803 |
804 | ```js
805 | {
806 | "err": null,
807 | "data": {
808 | "uri": "otpauth://totp/SafeLine:admin%40SafeLine?secret=ZXMDABN2HIAQK32G&issuer=SafeLine"
809 | },
810 | "msg": null
811 | }
812 | ```
813 |
814 | ## POST
815 |
816 | ### [数据格式定义]
817 | 数据格式
818 |
819 | |字段名|数据类型|是否必填/默认值|NULL|其他|
820 | |:--|:-:|:-:|:-:|:-:|
821 | |tfa_token | 字符串 | 非必填/'' | False | 两步验证验证码; 最小长度: 0; |
822 | |action | 指定选项 | 必填 | False | 选项是: ['disable', 'enable'] |
823 |
824 |
825 |
826 | ### - 禁用 tfa,需要输入一个 app 上生成的验证码
827 |
828 |
829 |
830 | 请求
831 |
832 | ```js
833 | {}
834 | ```
835 |
836 | 响应
837 |
838 | ```js
839 | {
840 | "err": null,
841 | "data": null,
842 | "msg": null
843 | }
844 | ```
845 |
846 | ### - 启用 tfa,需要输入一个 app 上生成的验证码
847 |
848 |
849 |
850 | 请求
851 |
852 | ```js
853 | {}
854 | ```
855 |
856 | 响应
857 |
858 | ```js
859 | {
860 | "err": null,
861 | "data": null,
862 | "msg": null
863 | }
864 | ```
865 |
866 | # /api/UserAPI
867 |
868 | ## GET
869 |
870 | ### [数据格式定义]
871 | 数据格式
872 |
873 | |字段名|数据类型|是否必填/默认值|NULL|其他|
874 | |:--|:-:|:-:|:-:|:-:|
875 | |id__in | 列表 | 非必填/ | False | [ - 详见下方表格](#vardiqgkkrqfynqvaiztxzamrkfhynih) |
876 | |username | 列表 | 必填 | False | [ - 详见下方表格](#ykwppezutgglekopzilqzpqyrsurvadn) |
877 |
878 | id__in
879 |
880 | |字段名|数据类型|是否必填/默认值|NULL|其他|
881 | |:--|:-:|:-:|:-:|:-:|
882 | |子字段 | 整型数字 | 必填 | False | - |
883 |
884 |
885 | username
886 |
887 | |字段名|数据类型|是否必填/默认值|NULL|其他|
888 | |:--|:-:|:-:|:-:|:-:|
889 | |子字段 | 字符串 | 必填 | False | - |
890 |
891 | ### 权限
892 | - User Management
893 |
894 | ### - 根据 id 获取用户信息
895 |
896 |
897 |
898 | 请求
899 |
900 | ```js
901 | {
902 | "id": 41
903 | }
904 | ```
905 |
906 | 响应
907 |
908 | ```js
909 | {
910 | "err": null,
911 | "data": {
912 | "id": 41,
913 | "is_locked": false,
914 | "ip_policy": [],
915 | "last_login": "1544513500",
916 | "username": "admin",
917 | "comment": "",
918 | "tfa_enabled": false,
919 | "create_time": "1544513500",
920 | "last_update_time": "1544513500",
921 | "last_login_time": null,
922 | "last_change_password_time": "1544513500",
923 | "permissions": [
924 | "System Monitor",
925 | "System Management",
926 | "User Management",
927 | "Website And Security Policy Management",
928 | "Audit Log Management"
929 | ],
930 | "authentication_method": "password",
931 | "session_timeout_time": 172800
932 | },
933 | "msg": null
934 | }
935 | ```
936 |
937 | ### - 根据 username 获取用户信息
938 |
939 |
940 |
941 | ?> 本 API 支持翻页参数
942 |
943 | 请求
944 |
945 | ```js
946 | {
947 | "username": "admin"
948 | }
949 | ```
950 |
951 | 响应
952 |
953 | ```js
954 | {
955 | "err": null,
956 | "data": [
957 | {
958 | "id": 42,
959 | "is_locked": false,
960 | "ip_policy": [],
961 | "last_login": "1544513500",
962 | "username": "admin",
963 | "comment": "",
964 | "tfa_enabled": false,
965 | "create_time": "1544513500",
966 | "last_update_time": "1544513500",
967 | "last_login_time": null,
968 | "last_change_password_time": "1544513500",
969 | "permissions": [
970 | "System Monitor",
971 | "System Management",
972 | "User Management",
973 | "Website And Security Policy Management",
974 | "Audit Log Management"
975 | ],
976 | "authentication_method": "password",
977 | "session_timeout_time": 172800
978 | }
979 | ],
980 | "msg": null
981 | }
982 | ```
983 |
984 | ### - 获取用户列表
985 |
986 |
987 |
988 | ?> 本 API 支持翻页参数
989 |
990 | 响应
991 |
992 | ```js
993 | {
994 | "err": null,
995 | "data": [
996 | {
997 | "id": 43,
998 | "is_locked": false,
999 | "ip_policy": [],
1000 | "last_login": "1544513501",
1001 | "username": "admin",
1002 | "comment": "",
1003 | "tfa_enabled": false,
1004 | "create_time": "1544513500",
1005 | "last_update_time": "1544513500",
1006 | "last_login_time": null,
1007 | "last_change_password_time": "1544513500",
1008 | "permissions": [
1009 | "System Monitor",
1010 | "System Management",
1011 | "User Management",
1012 | "Website And Security Policy Management",
1013 | "Audit Log Management"
1014 | ],
1015 | "authentication_method": "password",
1016 | "session_timeout_time": 172800
1017 | }
1018 | ],
1019 | "msg": null
1020 | }
1021 | ```
1022 |
1023 | ## POST
1024 |
1025 | ### [数据格式定义]
1026 | 数据格式
1027 |
1028 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1029 | |:--|:-:|:-:|:-:|:-:|
1030 | |username | 字符串 | 必填 | False | 最大长度: 16; |
1031 | |comment | 字符串 | 必填 | False | 最小长度: 0; |
1032 | |authentication_method | 指定选项 | 必填 | False | 选项是: ['ldap', 'password', 'cert'] |
1033 | |permissions | 指定选项 | 必填 | False | 选项是: ['System Monitor', 'System Management', 'User Management', 'Website And Security Policy Management', 'Audit Log Management'] |
1034 | |ip_policy | 列表 | 非必填/ | False | [ - 详见下方表格](#vfxpzftwbemywuozrruxdlhqolvxomia) |
1035 |
1036 | ip_policy
1037 |
1038 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1039 | |:--|:-:|:-:|:-:|:-:|
1040 | |子字段 | 字符串 | 必填 | False | IP 范围; |
1041 |
1042 | ### 权限
1043 | - User Management
1044 |
1045 | ### - 创建证书认证的用户
1046 |
1047 | extra 中 password 是证书的密码,token 是证书下载用的,见证书下载 api
1048 |
1049 | 请求
1050 |
1051 | ```js
1052 | {
1053 | "username": "foobar",
1054 | "comment": "test",
1055 | "authentication_method": "cert",
1056 | "permissions": [
1057 | "System Monitor",
1058 | "System Management",
1059 | "User Management",
1060 | "Website And Security Policy Management",
1061 | "Audit Log Management"
1062 | ],
1063 | "ip_policy": []
1064 | }
1065 | ```
1066 |
1067 | 响应
1068 |
1069 | ```js
1070 | {
1071 | "err": null,
1072 | "data": {
1073 | "user": {
1074 | "id": 28,
1075 | "is_locked": false,
1076 | "ip_policy": [],
1077 | "last_login": null,
1078 | "username": "foobar",
1079 | "comment": "test",
1080 | "tfa_enabled": false,
1081 | "create_time": "1544513495",
1082 | "last_update_time": "1544513495",
1083 | "last_login_time": null,
1084 | "last_change_password_time": null,
1085 | "permissions": [
1086 | "System Management",
1087 | "User Management",
1088 | "Website And Security Policy Management",
1089 | "System Monitor",
1090 | "Audit Log Management"
1091 | ],
1092 | "authentication_method": "cert",
1093 | "session_timeout_time": 172800
1094 | },
1095 | "extra": {
1096 | "token": "3857990760d7b0399ddd8efdb4e64cf8:1gWcW0:PDTaeMnNXjzPo7sErKMtpdtvRnE",
1097 | "password": "H6Gc1X"
1098 | }
1099 | },
1100 | "msg": null
1101 | }
1102 | ```
1103 |
1104 | ### - 创建 ldap 认证的用户
1105 |
1106 |
1107 |
1108 | 请求
1109 |
1110 | ```js
1111 | {
1112 | "username": "foobar",
1113 | "comment": "test",
1114 | "authentication_method": "ldap",
1115 | "permissions": [
1116 | "System Monitor",
1117 | "System Management",
1118 | "User Management",
1119 | "Website And Security Policy Management",
1120 | "Audit Log Management"
1121 | ],
1122 | "ip_policy": []
1123 | }
1124 | ```
1125 |
1126 | 响应
1127 |
1128 | ```js
1129 | {
1130 | "err": null,
1131 | "data": {
1132 | "user": {
1133 | "id": 30,
1134 | "is_locked": false,
1135 | "ip_policy": [],
1136 | "last_login": null,
1137 | "username": "foobar",
1138 | "comment": "test",
1139 | "tfa_enabled": false,
1140 | "create_time": "1544513496",
1141 | "last_update_time": "1544513496",
1142 | "last_login_time": null,
1143 | "last_change_password_time": null,
1144 | "permissions": [
1145 | "System Management",
1146 | "User Management",
1147 | "Website And Security Policy Management",
1148 | "System Monitor",
1149 | "Audit Log Management"
1150 | ],
1151 | "authentication_method": "ldap",
1152 | "session_timeout_time": 172800
1153 | },
1154 | "extra": null
1155 | },
1156 | "msg": null
1157 | }
1158 | ```
1159 |
1160 | ### - 创建密码认证的用户
1161 |
1162 | 密码是随机生成的
1163 |
1164 | 请求
1165 |
1166 | ```js
1167 | {
1168 | "username": "foobar",
1169 | "comment": "test",
1170 | "authentication_method": "password",
1171 | "permissions": [
1172 | "System Monitor",
1173 | "System Management",
1174 | "User Management",
1175 | "Website And Security Policy Management",
1176 | "Audit Log Management"
1177 | ],
1178 | "ip_policy": []
1179 | }
1180 | ```
1181 |
1182 | 响应
1183 |
1184 | ```js
1185 | {
1186 | "err": null,
1187 | "data": {
1188 | "user": {
1189 | "id": 32,
1190 | "is_locked": false,
1191 | "ip_policy": [],
1192 | "last_login": null,
1193 | "username": "foobar",
1194 | "comment": "test",
1195 | "tfa_enabled": false,
1196 | "create_time": "1544513497",
1197 | "last_update_time": "1544513497",
1198 | "last_login_time": null,
1199 | "last_change_password_time": "1544513497",
1200 | "permissions": [
1201 | "System Management",
1202 | "User Management",
1203 | "Website And Security Policy Management",
1204 | "System Monitor",
1205 | "Audit Log Management"
1206 | ],
1207 | "authentication_method": "password",
1208 | "session_timeout_time": 172800
1209 | },
1210 | "extra": {
1211 | "password": "eRFfGO"
1212 | }
1213 | },
1214 | "msg": null
1215 | }
1216 | ```
1217 |
1218 | ## PUT
1219 |
1220 | ### [数据格式定义]
1221 | 数据格式
1222 |
1223 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1224 | |:--|:-:|:-:|:-:|:-:|
1225 | |id | 整型数字 | 必填 | False | - |
1226 | |comment | 字符串 | 必填 | False | 最小长度: 0; |
1227 | |authentication_method | 指定选项 | 必填 | False | 选项是: ['ldap', 'password', 'cert'] |
1228 | |permissions | 指定选项 | 必填 | False | 选项是: ['System Monitor', 'System Management', 'User Management', 'Website And Security Policy Management', 'Audit Log Management'] |
1229 | |ip_policy | 列表 | 必填 | False | [ - 详见下方表格](#ywomadrftelbefzwypfqmnxaoghbvzkp) |
1230 |
1231 | ip_policy
1232 |
1233 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1234 | |:--|:-:|:-:|:-:|:-:|
1235 | |子字段 | 字符串 | 必填 | False | IP 范围; |
1236 |
1237 | ### 权限
1238 | - User Management
1239 |
1240 | ### - 编辑用户登录方式
1241 |
1242 |
1243 |
1244 | 请求
1245 |
1246 | ```js
1247 | {
1248 | "id": 36,
1249 | "is_locked": false,
1250 | "ip_policy": [],
1251 | "last_login": null,
1252 | "username": "foobar",
1253 | "comment": "test",
1254 | "tfa_enabled": false,
1255 | "create_time": "1544513498",
1256 | "last_update_time": "1544513498",
1257 | "last_login_time": null,
1258 | "last_change_password_time": "1544513498",
1259 | "permissions": [
1260 | "System Management",
1261 | "User Management",
1262 | "Website And Security Policy Management",
1263 | "System Monitor",
1264 | "Audit Log Management"
1265 | ],
1266 | "authentication_method": "cert",
1267 | "session_timeout_time": 172800
1268 | }
1269 | ```
1270 |
1271 | 响应
1272 |
1273 | ```js
1274 | {
1275 | "err": null,
1276 | "data": {
1277 | "user": {
1278 | "id": 36,
1279 | "is_locked": false,
1280 | "ip_policy": [],
1281 | "last_login": null,
1282 | "username": "foobar",
1283 | "comment": "test",
1284 | "tfa_enabled": false,
1285 | "create_time": "1544513498",
1286 | "last_update_time": "1544513498",
1287 | "last_login_time": null,
1288 | "last_change_password_time": "1544513498",
1289 | "permissions": [
1290 | "System Management",
1291 | "User Management",
1292 | "Website And Security Policy Management",
1293 | "System Monitor",
1294 | "Audit Log Management"
1295 | ],
1296 | "authentication_method": "cert",
1297 | "session_timeout_time": 172800
1298 | },
1299 | "extra": {
1300 | "token": "b161c788e116639277ee86fd5106d1c3:1gWcW2:xFbNZMmIp54S87_C7fjA9EmCDgk",
1301 | "password": "fZmHt3"
1302 | }
1303 | },
1304 | "msg": null
1305 | }
1306 | ```
1307 |
1308 | ### - 编辑用户 IP 策略
1309 |
1310 |
1311 |
1312 | 请求
1313 |
1314 | ```js
1315 | {
1316 | "id": 38,
1317 | "is_locked": false,
1318 | "ip_policy": [
1319 | "0.0.0.0/0",
1320 | "::/0"
1321 | ],
1322 | "last_login": null,
1323 | "username": "foobar",
1324 | "comment": "test",
1325 | "tfa_enabled": false,
1326 | "create_time": "1544513499",
1327 | "last_update_time": "1544513499",
1328 | "last_login_time": null,
1329 | "last_change_password_time": "1544513499",
1330 | "permissions": [
1331 | "System Management",
1332 | "User Management",
1333 | "Website And Security Policy Management",
1334 | "System Monitor",
1335 | "Audit Log Management"
1336 | ],
1337 | "authentication_method": "password",
1338 | "session_timeout_time": 172800
1339 | }
1340 | ```
1341 |
1342 | 响应
1343 |
1344 | ```js
1345 | {
1346 | "err": null,
1347 | "data": {
1348 | "user": {
1349 | "id": 38,
1350 | "is_locked": false,
1351 | "ip_policy": [
1352 | "0.0.0.0/0",
1353 | "::/0"
1354 | ],
1355 | "last_login": null,
1356 | "username": "foobar",
1357 | "comment": "test",
1358 | "tfa_enabled": false,
1359 | "create_time": "1544513499",
1360 | "last_update_time": "1544513499",
1361 | "last_login_time": null,
1362 | "last_change_password_time": "1544513499",
1363 | "permissions": [
1364 | "System Management",
1365 | "User Management",
1366 | "Website And Security Policy Management",
1367 | "System Monitor",
1368 | "Audit Log Management"
1369 | ],
1370 | "authentication_method": "password",
1371 | "session_timeout_time": 172800
1372 | },
1373 | "extra": {}
1374 | },
1375 | "msg": null
1376 | }
1377 | ```
1378 |
1379 | ### - 编辑用户权限
1380 |
1381 |
1382 |
1383 | 请求
1384 |
1385 | ```js
1386 | {
1387 | "id": 40,
1388 | "is_locked": false,
1389 | "ip_policy": [],
1390 | "last_login": null,
1391 | "username": "foobar",
1392 | "comment": "test",
1393 | "tfa_enabled": false,
1394 | "create_time": "1544513499",
1395 | "last_update_time": "1544513499",
1396 | "last_login_time": null,
1397 | "last_change_password_time": "1544513499",
1398 | "permissions": [
1399 | "System Management",
1400 | "User Management",
1401 | "Website And Security Policy Management",
1402 | "Audit Log Management"
1403 | ],
1404 | "authentication_method": "password",
1405 | "session_timeout_time": 172800
1406 | }
1407 | ```
1408 |
1409 | 响应
1410 |
1411 | ```js
1412 | {
1413 | "err": null,
1414 | "data": {
1415 | "user": {
1416 | "id": 40,
1417 | "is_locked": false,
1418 | "ip_policy": [],
1419 | "last_login": null,
1420 | "username": "foobar",
1421 | "comment": "test",
1422 | "tfa_enabled": false,
1423 | "create_time": "1544513499",
1424 | "last_update_time": "1544513499",
1425 | "last_login_time": null,
1426 | "last_change_password_time": "1544513499",
1427 | "permissions": [
1428 | "Website And Security Policy Management",
1429 | "System Management",
1430 | "User Management",
1431 | "Audit Log Management"
1432 | ],
1433 | "authentication_method": "password",
1434 | "session_timeout_time": 172800
1435 | },
1436 | "extra": {}
1437 | },
1438 | "msg": null
1439 | }
1440 | ```
1441 |
1442 | # /api/DownloadUserCertAPI
1443 |
1444 | ## GET
1445 |
1446 | ### [数据格式定义]
1447 | 数据格式
1448 |
1449 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1450 | |:--|:-:|:-:|:-:|:-:|
1451 | |token | 字符串 | 必填 | False | - |
1452 |
1453 | ?> 不需要登录
1454 |
1455 | ### - 下载证书认证的用户的证书
1456 |
1457 |
1458 |
1459 | 请求
1460 |
1461 | ```js
1462 | {
1463 | "token": "e15966530aaafdcee1f91a6065c0c3c0:1gWcW2:Ap4j2DLdtcVbYrjnFQKAVxQ1rC4"
1464 | }
1465 | ```
1466 |
1467 | 响应
1468 |
1469 | ```js
1470 | {
1471 | "err": null,
1472 | "data": null,
1473 | "msg": null
1474 | }
1475 | ```
1476 |
1477 | # /api/UsernameExistsCheckAPI
1478 |
1479 | ## GET
1480 |
1481 | ### [数据格式定义]
1482 | 数据格式
1483 |
1484 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1485 | |:--|:-:|:-:|:-:|:-:|
1486 | |username | 字符串 | 必填 | False | 最大长度: 16; |
1487 |
1488 | ### 权限
1489 | - User Management
1490 |
1491 | ### - 查询一个用户名是否被占用
1492 |
1493 |
1494 |
1495 | 请求
1496 |
1497 | ```js
1498 | {
1499 | "username": "foobar"
1500 | }
1501 | ```
1502 |
1503 | 响应
1504 |
1505 | ```js
1506 | {
1507 | "err": null,
1508 | "data": {
1509 | "result": false
1510 | },
1511 | "msg": null
1512 | }
1513 | ```
1514 |
1515 | # /api/UnlockUserAPI
1516 |
1517 | ## PUT
1518 |
1519 | ### [数据格式定义]
1520 | 数据格式
1521 |
1522 | > 只需要传递 ID
1523 |
1524 | |字段名|数据类型|是否必填/默认值|NULL|其他|
1525 | |:--|:-:|:-:|:-:|:-:|
1526 | |id | 整型数字 | 必填 | False | - |
1527 |
1528 | ### 权限
1529 | - User Management
1530 |
1531 | ### - 解锁用户
1532 |
1533 |
1534 |
1535 | 请求
1536 |
1537 | ```js
1538 | {
1539 | "id": 26
1540 | }
1541 | ```
1542 |
1543 | 响应
1544 |
1545 | ```js
1546 | {
1547 | "err": null,
1548 | "data": null,
1549 | "msg": null
1550 | }
1551 | ```
1552 |
1553 |
--------------------------------------------------------------------------------