前情提要:
升级动机
在前文
在前文完成2年后,1Panel v2发布了,引入了多节点管理、更完善的 Docker 管理功能,以及支持 OpenResty 自行编译等特性。在1Panel v2的早期版本中,更新日志中包含了大量的bug修复,数量太多以至于没有动力从v1升级到v2,甚至在 2.0.5 版本中存在高危的无需权限 RCE 漏洞。此时v1处于lts版本,运行稳定且更新更慢减少打扰,所以保持了一段时间不升级到v2。随着 2.0.8 的发布,其 Release 日志中的 Bug 修复明显减少,Issues 中的反馈频率也有所降低,同时我也有1Panel的永久授权,不用白不用,最终在 2.0.9 发布前进行了升级。
在早期使用雷池 WAF 时,并不十分省心,在我文章的更新记录中可以看出,雷池 WAF 经常更新版本,不仅修改 Docker Compose 文件和 .env 配置,有时还会调整 Socket 文件位置,升级过程中耗费大量时间用于排查问题。准备升级1Panel时,我也顺带看了下雷池的更新日志,发现我之前使用的 latest-stream 版本已久未更新。在更新日志中发现了其有发布多个lts版本,因此决定将雷池升级到 LTS 版本。lts版本有多个,看了用户相关反馈,新的lts可能削了首页看板/防护日志等关键功能,不能追新版本,选择了一个优化了防护引擎且保留功能的 8.0.0-LTS 版本。
新版本关键变更
1Panel v2
官方亮点介绍:
https://github.com/1Panel-dev/1Panel/releases/tag/v2.0.0
新增多机管理能力
支持自定义仓库,离线环境也能安装应用
新增文件对传功能
网站管理功能全面重构
新增网站负载均衡功能
新增脚本库功能
容器页面重构
快照机制重构
以上我都不太需要,我认为有价值的点:
Docker 管理功能更全面
OpenResty支持自己编译
雷池 WAF 8.0.0-LTS
摘取官方我认为有用的更新点:
https://help.waf-ce.chaitin.cn/node/0197787e-b70e-756e-b9d5-69d0a707144a
引擎(7.4.0)
新增
大幅优化漏洞检测模块,支持更多漏洞
新增大量加强规则
优化
HTTP 协议解析逻辑
SQL 注入检测逻辑
Java 代码注入检测逻辑
PHP 代码注入检测逻辑
JSON 解析解码逻辑
斜杠反转义解码逻辑
修复
攻击检测部分接口偶现失效问题
部分 XFF 空值的场景中,检测异常的问题
重构
JSP 代码检测逻辑,支持检测更多绕过
Java 反序列化检测逻辑
防护应用支持配置 SSE 流式响应(8.0.0)
修复 SDK 旁路接入时 QPS 不显示的问题(8.0.0)
旁路接入架构
旁路接入实现步骤
安装1Panel
1Panel按官方文档安装或升级即可,无特殊操作。
安装完成后,在 1Panel 应用商店安装最新版本的 OpenResty,安装后确认网站能正常访问。
部署雷池
.env文件
新建/data/safeline
目录,创建.env
文件,并添加以下内容:
SAFELINE_DIR=/data/safeline
POSTGRES_PASSWORD=随机一个密码
MGT_PORT=9443
RELEASE=-lts
CHANNEL=-lts
REGION=
IMAGE_PREFIX=chaitin
# 如果从dockerhub拉不下来可以换下面这个
# IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com/chaitin-safeline
IMAGE_TAG=8.0.0-lts
SUBNET_PREFIX=192.168.199
ARCH_SUFFIX=
配置文件的格式说明如下:
SAFELINE_DIR: 雷池安装目录,例如
/data/safeline
,请根据实际情况调整POSTGRES_PASSWORD: 雷池数据库的密码,随机生成一个
MGT_PORT: 雷池控制台的端口
RELEASE: 更新通道,已配置为 lts 版本更新通道
IMAGE_PREFIX: 雷池镜像源的前缀,建议根据服务器地理位置选择合适的源
IMAGE_TAG: 要安装的雷池版本, 格式为
版本号-lts
SUBNET_PREFIX: 雷池内部网络的网段,我这里为了防止ip冲突用了一个C段IP,可按实际情况设置
Docker Compose文件
在/data/safeline
目录下创建docker-compose.yml
文件,以下是我修改的Docker Compose文件:
networks:
safeline-ce:
name: safeline-ce
driver: bridge
ipam:
driver: default
config:
- gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
subnet: ${SUBNET_PREFIX}.0/24
driver_opts:
com.docker.network.bridge.name: safeline-ce
services:
postgres:
container_name: safeline-pg
restart: always
image: postgres:15.2
volumes:
- ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
- /etc/localtime:/etc/localtime:ro
environment:
- POSTGRES_USER=safeline-ce
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.2
command: [postgres, -c, max_connections=600]
healthcheck:
test: pg_isready -U safeline-ce -d safeline-ce
mgt:
container_name: safeline-mgt
restart: always
image: ${IMAGE_PREFIX}/safeline-mgt${REGION}${ARCH_SUFFIX}:${IMAGE_TAG:?image tag required}
volumes:
- /etc/localtime:/etc/localtime:ro
- ${SAFELINE_DIR}/resources/mgt:/app/data
- ${SAFELINE_DIR}/logs/nginx:/app/log/nginx:z
- ${SAFELINE_DIR}/resources/sock:/app/sock
- /var/run:/app/run
ports:
- ${MGT_PORT:-9443}:1443
healthcheck:
test: curl -k -f https://localhost:1443/api/open/health
environment:
- MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
depends_on:
postgres:
condition: service_healthy
fvm:
condition: service_started
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.4
detect:
container_name: safeline-detector
restart: always
image: ${IMAGE_PREFIX}/safeline-detector${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
volumes:
- ${SAFELINE_DIR}/resources/detector:/resources/detector
- ${SAFELINE_DIR}/logs/detector:/logs/detector
- /etc/localtime:/etc/localtime:ro
environment:
- LOG_DIR=/logs/detector
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.5
tengine:
container_name: safeline-tengine
image: ${IMAGE_PREFIX}/safeline-tengine${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
restart: "no"
entrypoint:
- sh
- -c
- |
(sleep 600; echo "[INFO] 10 minutes passed. Stopping tengine..." >&2; kill -TERM 1) & \
exec entrypoint.sh nginx -g 'daemon off;'
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/resolv.conf:/etc/resolv.conf:ro
- ${SAFELINE_DIR}/resources/nginx:/etc/nginx
- ${SAFELINE_DIR}/resources/detector:/resources/detector
- ${SAFELINE_DIR}/resources/chaos:/resources/chaos
- ${SAFELINE_DIR}/logs/nginx:/var/log/nginx:z
- ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
- ${SAFELINE_DIR}/resources/sock:/app/sock
environment:
- TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
- TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
# deprecated
- SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
- CHAOS_ADDR=${SUBNET_PREFIX}.10
ulimits:
nofile: 131072
network_mode: host
luigi:
container_name: safeline-luigi
restart: always
image: ${IMAGE_PREFIX}/safeline-luigi${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
environment:
- MGT_IP=${SUBNET_PREFIX}.4
- LUIGI_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
volumes:
- /etc/localtime:/etc/localtime:ro
- ${SAFELINE_DIR}/resources/luigi:/app/data
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
depends_on:
detect:
condition: service_started
mgt:
condition: service_healthy
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.7
fvm:
container_name: safeline-fvm
restart: always
image: ${IMAGE_PREFIX}/safeline-fvm${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
volumes:
- /etc/localtime:/etc/localtime:ro
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.8
chaos:
container_name: safeline-chaos
restart: always
image: ${IMAGE_PREFIX}/safeline-chaos${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
environment:
- DB_ADDR=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
volumes:
- ${SAFELINE_DIR}/resources/sock:/app/sock
- ${SAFELINE_DIR}/resources/chaos:/app/chaos
networks:
safeline-ce:
ipv4_address: ${SUBNET_PREFIX}.10
基于官方原版Compose文件,我修改了如下内容:
完善depends_on配置,等待前置容器完全启动/正常工作后才启动下一个容器
safeline-tengine容器10分钟后自动退出,避免系统资源浪费(本部署方式无需tengine)
postgres数据库采用官方镜像
因使用lts版本,理论上以后docker compose文件不会有大变动了。
启动雷池
启动前确保如下2个文件已正确保存,.env文件可能会隐藏。
/data/safeline
├── docker-compose.yaml
└── .env
在/data/safeline
目录下使用以下命令启动服务:
docker compose up -d
启动后理论上文件夹下的内容:
/data/safeline
├── 1panel.env
├── docker-compose.yaml
├── .env
├── logs
└── resources
编译并配置openresty应用
t1k 是雷池提供的用于 OpenResty 旁路接入的 Lua SDK。基于 1Panel v2 可自行编译 OpenResty 的特性,可以在启动容器前就安装好SDK,相比以往在容器启动后安装,能显著提升启动速度。
打开网站-网站-设置-模块-创建,输入以下配置。
名称:随便起一个名。
参数:
--with-compat
,理论上无需参数,但 1Panel 此处必须填写一个,只能重申一次启动动态模块兼容性。脚本:
echo -e "\nluarocks install lua-resty-t1k\n" >> /tmp/default.sh
,目的是在安装 LuaRocks 后,通过 /tmp/default.sh 安装 lua-resty-t1k 。
确认后打开开关并点击构建,等待构建完成。
配置openresty旁路接入雷池
挂载套接字文件目录
点击应用商店-已安装-openresty-参数-高级设置-编辑compose文件,打开compose文件编辑框。
在volumes下增加一行 - /data/safeline/resources/detector:/resources/detector
保存并重建openresty,建议重新编译一次以确保生效。
站点配置
创建通用配置文件
在1Panel网站目录下创建common目录,网站目录可在应用-OpenResty-参数中查看,例如我的在 /opt/1panel/www/common
创建文件并添加相应内容:
t1k.conf /opt/1panel/www/common/t1k.conf
access_by_lua_block {
local t1k = require "resty.t1k"
local t = {
mode = "block", -- block or monitor or off, default off
host = "unix:/resources/detector/snserver.sock",
port = 8000,
connect_timeout = 1000,
send_timeout = 1000,
read_timeout = 1000,
req_body_size = 1024,
keepalive_size = 256,
keepalive_timeout = 60000,
remote_addr = "http_x_forwarded_for: 1",
}
local ok, err, _ = t1k.do_access(t, true)
if not ok then
ngx.log(ngx.ERR, err)
end
dofile("/usr/local/openresty/1pwaf/waf.lua")
}
header_filter_by_lua_block {
local t1k = require "resty.t1k"
t1k.do_header_filter()
}
t1k_static_stie_location_root.conf /opt/1panel/www/common/t1k.conf/t1k_static_stie_location_root.conf
location ~ / {
index index.html index.htm index.php default.php default.htm default.html;
include /www/common/t1k.conf;
}
此配置提供了 Lua 脚本处理模板:请求先通过雷池检测,再经过 1Panel 自身的检测(网站监控 + WAF),这样能让雷池和1Panel的WAF共同工作。
网站接入雷池
1Panel站点的反向代理配置文件中包含以下行以完成配置:
include /www/common/t1k.conf;
或在静态站点的配置文件中添加:
include /www/common/t1k_static_stie_location_root.conf;
问题与解决方案
Halo正常接口误报
自定义规则中订阅在线规则就行。
尾巴
升级这事儿,说到底就是个平衡。系统跑得稳是第一位的,但偶尔尝点新鲜也能让架构更顺手。1Panel 从 v1 换到 v2,雷池上 LTS,其实也就是在“别瞎折腾”和“别太保守”之间找个舒服的点。
折腾多了人会累,版本更新也追不完。挑准时机,动一次就够了,剩下的时间还是留给生活比较实在。