如何在香港服务器上配置 Linux LSM(安全模块),满足金融与跨境支付业务的合规要求?

凌晨 01:20,我站在香港将军澳机房的冷通道里,手背贴着 1U 机箱的前面板,温度显示 23℃。那盏熟悉的红色告警灯刚刚熄灭,旁边同事把工单夹回透明夹板:“合规审计清单最后一项——生产支付网关启用 LSM 强化——今天必须落地。”
两周前,审计给了我们一摞要求:最小权限、强制访问控制(MAC)、系统调用约束、审计可追溯、变更可回滚。落地在 Linux 上,就是把 LSM(Linux Security Modules) 这套能力(SELinux/AppArmor/Yama/seccomp 等)真正跑起来,并把证据留好。纸面上很简单,机房里一旦开错一个布尔量或者标签,凌晨的报警会让你记得一辈子。
这篇文,是我把那一夜以及之后一周收尾的过程,完整写给做金融和跨境支付的同行。不是玄学,也不是“开个开关就安全”,而是一步步、带数据、可复现的实操笔记。
免责声明:本文分享技术落地经验,不构成法律意见。金融合规应与法务/内控/审计共同评估。
1. 场景与硬件清单(HK 机房真实参数)
我们三类节点:
| 角色 | 型号与配置 | 操作系统 | 主要软件 |
|---|---|---|---|
| 支付网关 (GW01~GW04) | Dell R740(2×Xeon Silver 4210, 128GB RAM, 2×1.92TB NVMe PM983, 2×10GbE Intel X710) | CentOS 7.9(3.10.0-1160.el7.x86_64) | Nginx/OpenResty、LuaJIT、Keepalived、rsyslog、auditd、Docker 20.10(container-selinux) |
| 风控服务 (RK01~RK02) | Supermicro 1029U-TRT(同等配置) | CentOS 7.9 | Java 11、gRPC、Redis、Zookeeper |
| 结算与账务 (DB01~DB02) | Dell R740xd(改为 8×1.92TB NVMe, HBA 模式) | CentOS 7.9 | PostgreSQL 12、Patroni、pgBouncer、WAL-G |
为什么 CentOS 7? 历史原因 + 兼容性稳定,且我们有长期维护的基线脚本。LSM 选择以 SELinux(targeted 策略)+ Yama 为主;容器侧用 seccomp 与 SELinux MCS。
注:如果你是新部署,建议直接上 RHEL 9/AlmaLinux 9 或 Ubuntu 22.04+,获得更新的内核与 LSM stacking(多模块并存)与 Landlock/BPF LSM 能力。本文也在后文给出迁移路线。
2. 合规目标与控制矩阵(把“要求”翻译成“配置”)
| 合规/审计要求 | 技术控制 | 在本文中的实现 |
| 最小权限(资源访问隔离) | 强制访问控制(MAC) | SELinux Targeted 策略 + 自定义 TE 模块;容器卷使用 :Z/ :z 语义 |
| 变更可追溯 | 审计日志(Who/When/What) | auditd 规则 + rsyslog 远程写入 + 完整性校验(AIDE) |
| 入侵面收敛 | 系统调用过滤 | Docker 默认 seccomp profile(精简版)+ 对关键 Daemon 的 NoNewPrivileges |
| 调试能力控制 | ptrace 限制 | Yama kernel.yama.ptrace_scope=2,仅允许父子关系 |
| 口令/密钥保护 | 文件标签与布尔量 | semanage fcontext 标注密钥目录,仅特定域可读 |
| 数据面/控制面分离 | 网络与进程域限制 | Nginx、Postgres 各自独立域,最小布尔放行;手工允许特定端口绑定 |
| 可验证性 | 可重复交付与证据 | 基线 Ansible 角色、执行记录、策略包(.pp)与审计报表(CSV/JSON) |
3. 方案选型与变更策略
LSM 选型:CentOS/RHEL 系列首选 SELinux;Ubuntu 选 AppArmor(后文给替代步骤)。
上线策略:三阶段——Permissive → Enforcing(灰度) → 全量 Enforcing。
回滚:Grub 启动项保留 selinux=0 enforcing=0 备选;关键节点保留 policy-rollback.pp;策略包版本化。
窗口:00:30–02:30,DNS/流量按 25% → 50% → 100% 切换。
4. 上线前的“安全基线”
4.1 内核参数(/etc/sysctl.d/99-security.conf)
# 调试与信息泄露面
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.perf_event_paranoid = 3
kernel.unprivileged_bpf_disabled = 1
kernel.yama.ptrace_scope = 2
# 网络面
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
应用并持久化:
sysctl --system
4.2 审计与日志
yum install -y audit audit-libs audispd-plugins rsyslog
systemctl enable --now auditd rsyslog
/etc/audit/rules.d/lsm.rules:
## 关键目录与二进制完整性
-w /etc/selinux/ -p wa -k selinux
-w /etc/audit/ -p wa -k auditconf
-w /usr/sbin/semanage -p x -k selinux_tools
-w /usr/sbin/setsebool -p x -k selinux_tools
-w /usr/sbin/semodule -p x -k selinux_tools
## 身份与特权
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k sysctl
-a always,exit -F arch=b64 -S init_module -S finit_module -S delete_module -k module
-a always,exit -F arch=b64 -S mount -S umount2 -k mount
auditctl -R /etc/audit/rules.d/lsm.rules
augenrules --load
远程日志 /etc/rsyslog.d/50-remote.conf:
*.* @@10.66.8.10:514;RSYSLOG_SyslogProtocol23Format
记得在合规里保留日志链路图与样例记录。
5. SELinux 部署:从探路到落地
5.1 当前状态与模式
getenforce
sestatus
cat /etc/selinux/config | grep -E 'SELINUX|TYPE'
期望:SELINUX=permissive 起步,策略 SELINUXTYPE=targeted。
切换到 Permissive(若原先是 disabled):
sed -ri 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
touch /.autorelabel
reboot
.autorelabel:首次启用必做,否则大量 denied 来自错误标签。
5.2 标注关键路径(fcontext)
支付密钥与证书:
semanage fcontext -a -t cert_t "/var/secure/keys(/.*)?"
restorecon -Rv /var/secure/keys
Nginx 网关静态与 Lua 脚本:
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/gw(/.*)?"
restorecon -Rv /var/www/gw
PostgreSQL 数据:
semanage fcontext -a -t postgresql_db_t "/pgdata(/.*)?"
restorecon -Rv /pgdata
5.3 服务布尔量(Booleans)
getsebool -a | grep httpd
setsebool -P httpd_can_network_connect on
setsebool -P httpd_can_network_connect_db on
setsebool -P nis_enabled off
常见布尔量清单:
| 服务 | 布尔量 | 说明 |
| Nginx/OpenResty | httpd_can_network_connect |
允许对外发起连接(上游、Redis 等) |
| Nginx → DB | httpd_can_network_connect_db |
连接数据库所需 |
| 反向代理 WebSocket | httpd_can_network_relay |
WebSocket/转发场景 |
| Docker/容器 | container_use_cephfs 等 |
视存储插件而定 |
原则:只开必要的开关,开前先在 Permissive 收集 AVC,再精准放行。
5.4 从审计生成策略(audit2allow → .te/.pp)
触发业务流量,收集 AVC:
ausearch -m avc -ts recent > /root/avc.log
sealert -a /var/log/audit/audit.log | tee /root/sealert.txt
生成最小策略:
cat /root/avc.log | audit2allow -M gw_custom
semodule -i gw_custom.pp
典型自定义策略(gw_custom.te 示例节选):
module gw_custom 1.0;
require {
type httpd_t; type redis_port_t; class tcp_socket name_connect;
}
# 仅允许 httpd_t 连接 Redis 端口
allow httpd_t redis_port_t:tcp_socket name_connect;
小技巧:把“端口语义化”,而不是放开全部 name_connect。
5.5 灰度到 Enforcing
灰度:仅切一台 GW 到 Enforcing,由 LVS/Keepalived 调度 25% 流量,观察 30 分钟。
setenforce 1
平稳后覆盖到全部 GW,最后到风控与数据库节点。
6. 容器场景:SELinux + seccomp + 卷标注
6.1 Docker 与 SELinux
确保装有 container-selinux:
yum install -y container-selinux
挂载卷使用 :Z(私有标签)或 :z(共享标签):
docker run --name gw \
-v /var/www/gw:/usr/local/openresty/nginx/html:Z \
-p 443:443 -d openresty/openresty:alpine
6.2 Seccomp 精简配置(支付容器)
在 /etc/docker/seccomp-gw.json:
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{ "names": ["accept4", "epoll_wait", "read", "write", "recvfrom", "sendto"], "action": "SCMP_ACT_ALLOW" },
{ "names": ["clone", "unshare", "kexec_load"], "action": "SCMP_ACT_ERRNO" }
]
}
运行:
docker run --security-opt seccomp=/etc/docker/seccomp-gw.json ...
6.3 systemd 限权(非容器进程)
/etc/systemd/system/gw.service 节选:
[Service]
ExecStart=/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
7. AppArmor 替代(Ubuntu 节点速用)
若边缘接入在 Ubuntu:
apt install apparmor apparmor-utils auditd
aa-status
aa-enforce /etc/apparmor.d/usr.sbin.nginx
自定义 profile(节选):
/usr/sbin/nginx {
# 网络只允许 443 出口
network inet stream,
deny network raw,
/var/www/gw/** r,
/var/cache/nginx/** rw,
}
AppArmor 的语义对目录/文件粒度更直接,策略学习曲线更平。
8. 性能与压测数据(开启前后对比)
| 场景 | QPS | P95 延迟 | CPU 利用 | 备注 |
| GW(SELinux Permissive) | 28k | 12.4 ms | 61% | 基线 |
| GW(SELinux Enforcing) | 27.5k | 12.9 ms | 62% | -1.8% QPS,影响可接受 |
| GW(Enforcing+seccomp) | 27.3k | 13.1 ms | 62% | 再下降 0.7% |
| DB(Enforcing) | 9.1k | 18.7 ms | 58% | 无显著差异 |
观测:在我们业务模型下,SELinux 的额外开销可控;只要策略足够“窄”,长尾影响很小。
9. 典型坑点与现场排障手记
- “.autorelabel 忘记”:重启后疯狂 denied,大量 system_u:object_r:default_t。解决:touch /.autorelabel && reboot,等待 10–20 分钟。
- 布尔量开大:早期直接 httpd_can_network_connect on 但没限定端口,渗透测试提示可横向。解决:收集 AVC → 自定义 TE 仅放 redis_port_t/postgresql_port_t 等。
- 容器卷没加 :Z:容器读写 13。解决:卷统一 :Z;共享目录再用 :z。
- PostgreSQL 迁移:pg_wal 新盘忘了 restorecon,主从延迟。解决:新增 fcontext + restorecon -Rv。
- auditd 规则太多:CPU 抖动。解决:聚焦关键事件 + 远程汇聚;为高频事件加 -F success=0 过滤。
排障速查:
ausyscall --dump | head
ausearch -m avc -ts recent
semanage boolean -l | column -t | less
ps -eZ | grep nginx
ls -Z /var/www/gw
10. 可验证交付:把“做了”变成“证据”
- 策略包:gw_custom.pp、版本号、变更单号、生成时间、生成命令。
- 系统状态快照:sestatus、getenforce、关键布尔量导出(见下脚本)。
- 审计报表:ausearch 导出近 24h 关键 -k 的事件 CSV。
- 配置留痕:Ansible 角色(selinux, auditd, rsyslog)与运行日志。
导出脚本 /usr/local/bin/lsm-report.sh:
#!/usr/bin/env bash
set -euo pipefail
mkdir -p /var/reports/lsm/$(date +%F)
D=/var/reports/lsm/$(date +%F)
# SELinux 状态
sestatus > "$D/sestatus.txt"
getenforce > "$D/getenforce.txt"
# 布尔量
getsebool -a | sort > "$D/booleans.txt"
# 策略模块
semodule -l > "$D/semodule_list.txt"
# 审计关键事件
ausearch -k selinux -ts yesterday | aureport -x --summary > "$D/audit_exec_summary.txt"
ausearch -k selinux -ts yesterday > "$D/audit_selinux.log" || true
tar czf "$D/../lsm-$(hostname)-$(date +%F).tar.gz" -C "$D/.." "$(date +%F)"
echo "Report at $D/../lsm-$(hostname)-$(date +%F).tar.gz"
11. 升级与迁移路线(想要更“新”的 LSM)
RHEL/Alma 9:保留 SELinux 主体,获得 LSM stacking 更完整支持;容器侧可配合 cgroup v2 + 系统 seccomp;可探索 eBPF LSM 精细化钩子(更高阶)。
Ubuntu 22.04+:AppArmor 默认;可引入 Landlock 在应用侧做“最小文件访问”沙箱(开发/批处理很香)。
蓝绿迁移:新老机房/机架并行,先在新平台 Enforcing,压测达标后再迁。
12. SOP:一键到位的变更流程卡
- 变更评审(需求→控制矩阵)
- 备份与回滚(快照/策略/Grub)
- 基线(sysctl、auditd、rsyslog)
- SELinux Permissive 启用与 .autorelabel
- fcontext 标注关键路径
- 业务压测 + 收集 AVC
- audit2allow 生成最小策略 + 代码评审
- 单机 Enforcing 灰度 + 指标观察
- 全量 Enforcing + 报表归档
- 渗透测试/红队验证 + 复盘
结尾:关灯之前
02:18,我从冷通道走出来,机柜门轻轻合上。监控屏上,GW 的 95 线稳定,审计规则也不再刷屏。门外港岛的风从电梯口灌进来,手机里弹出一条消息:“策略评审通过,证据齐全,可以收口了。”
我知道这事远没完:合规不是“通过一次审计”,而是把 安全变成系统的默认形态。LSM 不会替你做选择,它只是把“选择”固定下来。下一次——也许是 RHEL 9 的迁移,也许是 Landlock 的试点——我还会在这条冷风里,把每一次“开关”的动作,写成你能复用的 SOP。
如果你也在香港的某个机房加班,希望这份实操手记,能让你少踩几个坑,多睡一会