起因
这份脚本一开始没想得太多。最早只是想把 Nginx 的编译参数固定下来,免得每次重装都重新找模块、重新拼参数。后来机器装多了,升级和回退也做过几次,才发现最容易出错的不是编译本身,而是后面的几步。
比如模块到底有没有带全,旧版本怎么切回去,配置应该跟着哪个目录走,WAF 那一套 Lua 文件是不是还得装完以后再补。这些事情第一次做时都不算大,隔一阵子再回来,反而最容易忘。
所以后来我把脚本整体改过一遍,把后面维护时总会碰到的事情尽量并到同一个流程里。
相关代码在这里:
- 安装说明:https://github.com/kroyoo/cust0m12a6le/blob/main/nginx/README.md
- 安装脚本:https://github.com/kroyoo/cust0m12a6le/blob/main/nginx/install_nginx.sh
- WAF 目录:https://github.com/kroyoo/cust0m12a6le/tree/main/waf
- WAF 说明:https://github.com/kroyoo/cust0m12a6le/blob/main/waf/README.md
现在这脚本大概是什么样子
它直接走源码安装,不走 apt、yum 这条路。Nginx 本体之外,OpenSSL、PCRE2、LuaJIT、libmaxminddb 这些依赖会一起处理,Lua 和 GeoIP2 相关模块也一并编进去。像我平时会用到的 ngx_devel_kit、lua-nginx-module、stream-lua-nginx-module、ngx_http_geoip2_module、ngx_http_substitutions_filter_module,现在都已经放进脚本里了。
我比较在意的是目录结构。默认路径下,大致是这样:
/usr/local/nginx/releases # 每次编译出来的新版本
/usr/local/nginx/current # 当前正在使用的版本
/usr/local/nginx-data # 运行时配置、WAF、Lua 文件、日志目录等
这样分开以后,后面的动作就简单很多。新版本先编出来,配置先拿新二进制验一遍,确认没问题了再把 current 切过去;真要回退,也只是把链接拨回上一版。运行时的配置、WAF 和日志都还在原来的地方,不用重新从旧目录里一点点抠出来。
这也是我后来一直没把这套装法换掉的原因。过一段时间再回来,版本在什么地方,当前跑的是哪一个,运行时那份东西又放在哪,还是一眼能看明白。
WAF 为什么一起放进安装流程
以前我的做法是先把 Nginx 装好,再慢慢把 WAF 补上。第一次做通常没什么问题,次数一多就开始漏东西。有时忘了在 nginx.conf 里加 include waf/waf.conf;,有时漏了 lua_package_path,还有一次目录都放对了,结果运行时读的不是我以为的那份 config.lua。
这些都不是大问题,但每次都得回头查。后来我就不再把 WAF 留成安装后的手工步骤,直接把它并到脚本里。
脚本现在既可以直接用本地仓库里的 waf/ 目录,也可以从远端仓库拉。策略上也留了几种选择:可以要求 WAF 没准备好就直接停下,也可以先给警告继续装,还可以完全跳过。这么做不是为了把参数做得很花,而是因为不同机器的要求确实不一样。
运行时 WAF 默认会放在:
/usr/local/nginx-data/conf/waf
目录拷过去还不够,后面几处容易漏的地方也得一起补上:nginx.conf 里的 include、lua_package_path / lua_package_cpath、运行时要用的 Lua 文件,以及最后那次配置校验。这样装完以后,WAF 基本就在正确的位置上了,不用再回头补最后几步。
平时我真正会改哪里
真到日常维护时,我动得最多的还是 config.lua。规则文件当然也会改,但大多数时候,先看的还是这个文件。
原因很简单。平时碰到的事情,无非就是这几类:日志要不要多打一层,CC 阈值要不要调,拦截页文案要不要改,挑战模式是不是还按原来的方式走。这些设置大多都还集中在 config.lua 里,所以过一阵子再回来,我通常也是先从这里开始看。
challenge 这一块我会特别留意。页面能不能弹出来倒不难,真正麻烦的是后面那段状态怎么收。原始回跳地址会先记在共享字典里,不会因为走了一趟验证就丢掉;旧的 cookie 也不能拿来绕过新锁。这个地方如果没想清楚,前端看起来再正常,后面也还是会出问题。
如果要开 turnstile,我一般不是先去看页面,而是先查后端几样东西是不是齐了:config.lua 里的 site_key 和 secret_key 有没有填,waf.conf 里的 lua_shared_dict waf_challenge 20m; 还在不在,机器能不能访问 Turnstile 校验接口,运行时有没有 lua-resty-http。这些条件不通,页面能打开也没用,最后还是会卡在验证上。
我自己通常怎么跑
第一次上机时,我一般先做一次 dry-run,把路径和动作看一遍:
bash nginx/install_nginx.sh --dry-run
确认没问题后,再真正编译和发布。通常我会先保留服务不动,自己手工验一遍:
bash nginx/install_nginx.sh --waf-source online --waf-policy optional
编完以后,先拿活动二进制验证运行时配置:
/usr/local/nginx/current/sbin/nginx -t -p /usr/local/nginx-data/ -c conf/nginx.conf
确认通过,再决定要不要 reload:
systemctl reload nginx
如果只是想更新 WAF 规则或逻辑,不想把整套 Nginx 连带依赖再编一遍,脚本也留了单独入口:
bash nginx/install_nginx.sh --update-waf-only --waf-policy required
这个模式只处理运行时 WAF,同步完会重新校验配置并 reload。规则改动比较勤、但 Nginx 本体不需要动的时候,用起来会轻松很多。
还有两个地方我自己会额外留心。
一个是 --sync-waf 默认没开。脚本不会每次安装都强行覆盖运行时 conf/waf,这很正常,因为线上规则经常已经按机器情况改过。如果就是想拿仓库里的版本完整覆盖运行时 WAF,再显式加 --sync-waf 会更稳。
另一个是 Turnstile 真正容易卡住的地方,很多时候不在前端页面,而是在后端这几处:resty.http 有没有,LUA_PATH / LUA_CPATH 有没有进服务环境,服务器能不能访问 Turnstile 校验地址。这几处不通,前面看着都对,最后还是会卡在验证上。
最后
我把这份脚本一直留着,主要是因为后面再看它时不会费劲。版本在哪,当前跑的是哪一个,配置放在哪,WAF 又放在哪,都还是清楚的。
机器少的时候,这些事看着不起眼;机器一多,或者中间隔了很久再回来,这种清楚就很重要了。
Comments