第1章 OpenrResty
1.1 OpenResty简介
Web防火墙在安全领域出现的频率很高,安全人员基于各种技术创建WAF系统用来实现Web防火墙功能,基于Java、Go语言、OpenResty等多种形式的实现方案,而基于OpenrResty的WAF系统实现,根据业务规模需求规模不同,系统实现的代码工程规模各异,功能复杂,可以实现一个交互体验良好的检测管理系统,功能简单,实现黑白名单亦可,高投入产出比高,伸缩性很强。
OpenResty的发呢源于Nginx,Nginx是一个性能强悍的Web服务器,最开始基于对Nginx服务的扩展,大多数情况都用C语言来完成。C语言属于一种“中级”计算机语言,因可通过指针控制对计算机底层的操作处理,让C语言同比其他高级语言,学习曲线更陡峭,更容易在生产编码中出错,维护起来相对Python、Lua这种业务型的,抽象程度高的计算机语言来说,更复杂,同比实现同样功能的代码完成行数与时间更多更长一些。
OpenResty的作者在Nginx的基础上引入的Lua语言,通过Nginx Lua Module这个模块,让Nginx可以通过Lua语言对Nginx进行功能扩展,并且同时实现了大量的Nginx扩展模块对其支持,完备的Nginx的工具与功能生态,在这些基础之上作者发布了一个新的Nginx发行版本:OpenResty。
OpenResty为Nginx提供了强大的Lua业务描述语言工具,提供了一些可调用的API功能,Lua相比C语言,语言抽象程度更高,短短的几行话,就可以实现一个原来需要Nginx C扩展模板实现的功能,省去了编译C语言所需要的时间,直接用Lua语言动态实现,在调试阶段甚至可以不用重启Nginx服务,可修改Lua源码,变更功能代码简单方便。
OpenResty在国内社区的发展的脉络,简单回顾的话,主要以OpenResty发展为主线,周边衍生出多种的社会产品,包括第三方模块、Web框架、网关产品等产品。
进入Lua的Web世界,有几个关键型开源项目要提及,一位是OpenResty作者(很帅),还有一位是MoonScript的作者,同时是也OpenResty Web框架的作者Leafo老师,Luarocks的维护者,Luarocks提供了大量的Lua语言基础库,整合了大量Lua实现的库。
MoonScript语言之于Lua来说, 就像CoffeScript与JavaScript的关系,MoonScipt提供了更高抽象程度的语言工具,MoonScirpt最后会被翻译成Lua语言,相对一个复杂的Lua语言实现的功能,可以通过 MoonScript更简洁的实同,MoonScript采用的类似Python语言的缩进方式。
文件app.moon,内容,如下 :
apis = require "lapis" class extends lapis.Application "/": => "Welcome to Lapis #{require "lapis.version"}!"
文件app.lua,内容,如下:
local lapis = require("lapis") do local _class_0 local _parent_0 = lapis.Application local _base_0 = { ["/"] = function(self) return "Welcome to Lapis " .. tostring(require("lapis.version")) .. "!" end } _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, ...) return _class_0.__parent.__init(self, ...) end, __base = _base_0, __name = nil, __parent = _parent_0 }, { __index = function(cls, name) local val = rawget(_base_0, name) if val == nil then local parent = rawget(cls, "__parent") if parent then return parent[name] end else return val end end, __call = function(cls, ...) local _self_0 = setmetatable({}, _base_0) cls.__init(_self_0, ...) return _self_0 end }) _base_0.__class = _class_0 if _parent_0.__inherited then _parent_0.__inherited(_parent_0, _class_0) end return _class_0 end
app.moon的源代码需要MoonScript编译器程序moonc,对源文件进行翻译,MoonScript语言直接支持的简单的OO面向对象编程特性,这也是原生Lua语言不具备的功能,但可以通过Lua原表功能进行模拟,OpenResty+lua的模式可以进行OpenResty的控制逻辑编写,同样也可以通过简洁的MoonScript语言进行OpenResty服务器编程,需要增加的一个步骤就是需要对MoonScript脚本编写的程序翻译成Lua脚本,这样OpenResty即可识别执行。
MoonScript有配套的Web框架:Lapis,Lapis作为一个有OpenResty 的Web语言框架集合了很多OpenResty功能模块,可以让用户通过MoonScript便捷的使用这些库功能,Lapis提供了命令行脚手架程序,可以快速的OpenResty的整套脚手架代码,上面的app.moon就是Lapis命令行程序自动生成,Lapis让OpenResty的Web编程更简单,可以让用户快速的入门OpenResty Lua编程,将注意力快速带入到业务逻辑编写,下面是Lapis自动生成的nginx.conf配置文件,如下:
env LAPIS_ENVIRONMENT=development; worker_processes 1; error_log stderr notice; daemon off; pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; server { listen 8080; lua_code_cache off; location / { default_type text/html; content_by_lua_block { require("lapis").serve("app") } } location /static/ { alias static/; } location /favicon.ico { alias static/favicon.ico; } } }
在2015开始就有国内的作者开始编写国产化的Lua框架,比如:Lor、Vanila同时代用的比较多Lua Web框架,Lor框架在某些业务的生产系统上也工作了很多年,并且后来还基于Lor框架,产生国产网关产品Orange。
因为Lapis作为Web框架,对应OpenResty的7个工作阶段中的content_by_lua阶段,在网上可以经常看到的一张图,如下:
基于OpenResty的WAF系统,与传统OpenResty Web的区别是,WAF代码在access_by_lua阶段被执行,当发现用户访问的异常之后,执行请求的拒绝或是跳转,不进行更具体的内容信息的渲染。
随着新的OpenResty新版本的发布,上面这张处理阶段也需要更新, OpenResty1.19.3版本发布中加入了exit_worker_by_*阶段,可以更方便的在worker退出的阶段执行任务。在某些版本中对特定数据处理部分的进行性能优化,提高了20%的性能,在ngx.balancer模块中增加了recreate_request() API,可在balancer阶段改写请求信息,目前这个版本提供给社区使用的,不推荐生产环境使用。
1.1.1 MoonScript与Lua
OpenResty服务即可用于实现网关应用,也可以实现WAF防护墙,有人可能会对MoonScript的性能产生怀疑,直接用Lua进行WAF模块的编写,而经过实践,用MoonScript翻译成的Lua代码执行的性能就差,也不是绝对的,所以,接着上一节的对MoonScript的内容,对MoonScript语言编程进行详细一些的介绍,如何可以通过MoonScript“操控”OpenResty,如下:
Coffescript是一种中间的脚本,可以把这种脚本翻译成JavaScript。而MoonScript,是可以翻译成lua语言的中间脚本。
1.安装MoonScript
sudo luarocks install moonscript
2.创建.moon源文件app.moon
lapis = require "lapis" class extends lapis.Application "/": => "Welcome to Lapis #{require "lapis.version"}!"
3.安装MoonScript语法高亮的插件。