SafeLine 扩展插件系统(以下简称插件系统)使用 Lua 脚本语言(目前语法兼容 Lua 5.1)与 雷池WAF 深度集成,为有余力定制 WAF 的用户提供了一个毋须修改 WAF 本身即可扩展其功能的接口。
Lua 是一种轻量小巧的脚本语言,在 64 位 Linux 下包含标准库的 Lua 解释器及其相关库所占用的空间只有不到 1M 的大小。同时 Lua 成熟可靠,在解释型脚本语言中有着最好的性能表现。另一方面 Lua 也十分容易嵌入到其他语言实现的应用中,为其拓展可编程能力,例如 Redis、OpenResty 等。
雷池 WAF 的流量检测的运行逻辑大致如下,客户端与服务端之间的流量会在经过转发服务时被劫持然后发送到检测服务,检测服务在检测完成后会回复给转发服务一个 “是否拦截” 的结果,同时检测服务还会将这一次的检测信息与流量的一些信息发送至日志服务,日志服务会异步于检测过程对检测信息做加工,Lua 扩展插件也运行于此处。
为了兼顾转发检测的稳定性与功能的实用性,我们选择让用户在处理检测信息的过程中可以执行 Lua,这样既可以保证检测服务的性能不受 Lua 扩展插件的影响,又可以在提供足够接口的情况下完成各种统计任务、数据上报任务以及各种联动任务。即使如此我们依旧希望 Lua 扩展插件能有更好的处理性能,所以我们选择使用 Luajit 来获得更快的速度。
LuaJIT is a Just-In-Time Compiler for Lua programming language.
Just-in-time(JIT)技术通过在运行时记录并分析代码执行过程中的热点,对热点部分进行额外的编译,形成对应的机器码,在之后的执行过程中,相应的代码部分将直接使用机器码进行处理,以此达到效率提升的目的。雷池作为一个大流量处理设备,其 Lua 扩展插件也将不断重复对不同请求流量执行,热点位置相对稳定,能够很好的发挥 JIT 的作用。
我们首先来看一个简单的 Lua 扩展插件样例。
local safeline = require("safeline")
-- arg_risk 的可能取值:
-- safeline.RISK_NORMAL
-- safeline.RISK_LOW
-- safeline.RISK_MEDIUM
-- safeline.RISK_HIGH
local arg_risk = safeline.RISK_MEDIUM
local arg_range = 60 * 30
local arg_period = 5
local arg_threshold = 3
local query = string.format([[
SELECT src_ip
FROM detect_log
WHERE risk_level >= %d
GROUP BY SLIDE_WINDOW(NOW(), %d, %d), src_ip
HAVING COUNT(*) >= %d
]], arg_risk, arg_range, arg_period, arg_threshold)
function process(key, rows)
local total_num = 0
local err_num = 0
for row in rows do
err = safeline.action_ban(safeline.ACTION_SCOPE_ALL, { ip = row.src_ip }, 3600)
total_num = total_num + 1
if err ~= nil then
err_num = err_num + 1
end
end
if total_num ~= 0 then
safeline.log("信息", string.format("本次需要封禁 %d 个 IP,其中 %d 个封禁失败", total_num, err_num))
end
end
safeline.register(safeline.TYPE_QUERY, query, process)
这个插件实现了 “封禁在 30 分钟内发起 3 次或以上攻击的 IP,生效周期为 5 秒” 的能力。
Lua 扩展插件的开发过程,首先需要导入 safeline 模块,然后注册一个 trigger(触发器),并实现当触发器被触发时的处理函数。下面来简单介绍一下这些部分。
更多插件 Demo 与插件文档可参考:https://github.com/chaitin/safeline-open-platform
在编写插件时,首先需要导入 safeline 模块,safeline 模块包含了插件系统提供的所有接口。
local safeline = require "safeline" |
---|
出于安全性的考量,插件脚本中只允许使用有限的标准库函数,其中 IO 库中的所有函数均被禁用,OS 库中仅保留 time, date, clock, timediff
等函数。
每个插件至少需要调用一次注册函数以获得一个触发器(Trigger),调用注册函数时通常会指定一个触发条件及其回调函数,当满足条件时该回调函数会被调用。
注册函数签名如下:
safeline.register(type, ...) |
---|
第一个参数 type 代表触发器的类型,可能的取值及用途见下表。其后的变长参数的数目及类型随着 type 的不同而不同。
触发器类型 | 用途 |
---|---|
safeline.TYPE\_TICKER | 执行定时任务,每隔一段时间调用一次指定的回调函数 |
safeline.TYPE\_QUERY | 给定一个类 SQL 的查询语句,处理符合给定查询的请求信息会被传递给指定的回调函数 |
safeline.TYPE\_MATCH | 给定一系列匹配条件,满足该匹配条件的请求信息会被传递给指定的回调函数 |
Match 触发器需要用户给定一个描述请求特征(仅限于预定义的一些条件)的表 match 和一个回调函数 process,当通过 SafeLine 的请求满足 match 的描述时,回调函数 process 会在该请求的上下文中被调用。其注册方式如下:
local safeline = require "safeline" local match = { ip = "0.0.0.0/0", host = \[\[.\*\.chaitin\.cn:80\]\], url\_path = \[\[/.\*safeline\]\], target = safeline.MATCH\_TARGET\_ALL, } function process(ip, host, url\_path) -- 在此处理请求 end safeline.register(safeline.TYPE\_MATCH, match, process) |
---|
Ticker 触发器会根据用户指定的周期定时地调用回调函数,在集群部署模式下亦能保证该定时器的正确性。其注册方式如下:
local safeline = require "safeline" local duration = 10 function tick(dur) -- 在此处理定时任务 end safeline.register(safeline.TYPE\_TICKER, duration, tick) |
---|
duration 是一个整数,用来声明插件的触发间隔,其单位为秒。
ticker 是插件的回调函数,当插件触发时被调用,参数 dur 的值为插件的触发间隔,其单位为秒。函数无返回值。
Query 触发器允许用户使用 Plumber SQL 对请求进行流式的统计,每次统计结果产生时会将其传递给回调函数。其注册方式如下:
\-- 导入 safeline 包 safeline = require("safeline") \-- 指定 Plumber SQL query = \[\[SELECT ip, time FROM access\_log WHERE time > 0.01\]\] \-- 处理 Plumber SQL 产生的结果 function process(key, rows) for row in rows do safeline.log("INFO", "IP: " .. row.ip .. ", Time: " .. row.time) end end \-- 注册 Query 触发器 safeline.register(safeline.TYPE\_QUERY, query, process) |
---|
Plumber SQL 是一种类 SQL 的数据操纵语言(Data Manipulation Language),可以将 Safeline 的 访问日志、检测日志 作为数据源,在周期时间窗口或滑动窗口内对数据进行筛选、分组、聚合等操作。
err = safeline.action\_ban(scope, key, duration) |
---|
封禁符合 key 约束的请求,有效时间为 duration 秒。
duration 为整型,以秒为单位。
提交请求成功时函数返回 nil,失败时返回包含错误信息的字符串。
safeline.log(tag, msg) |
---|
产生一条插件日志,日志可以在 SafeLine 管理界面**「日志管理」→ 「扩展插件日志」中查看。**
tag
和 msg
均为字符串类型,tag
用于标记日志的类别,值为 "system" 的 tag 被插件系统内部保留使用,msg
是日志内容。tag
和 msg
均不得为空。
函数无返回值。
作为 Web 流量处理网关,SafeLine 扩展插件系统 在保证性能与稳定性的同时,提供了丰富的扩展接口,支持以更多自定义的方式来处理收到的 HTTP 请求,为用户提供更加自由的可编程能力,提供更加快捷的业务实现方式。