Files
blog/content/posts/fleet-security-day.md
T

92 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "舰队安全加固日记:从攻击溯源到 Ansible 自动化"
description: "三节点 VPS 舰队的一天:SSL 续签排查、WAF 加固、Ansible 落地、域名迁移。全程踩坑记录。"
date: 2026-08-01T16:00:00+08:00
draft: false
tags:
- 运维
- 安全
- Ansible
- Cloudflare
---
## 背景
维护一个由三台海外 VPS 组成的"小舰队"(分别承担代理、博客、文件服务、监控等角色),配合本地 NAS 作为管理中心。今天做了一次全面安全巡检与加固,记录几个有价值的踩坑点。
## 一、SSL 自动续签的隐形坑
**现象**:三节点都配置了 acme.sh 自动续签 cron,看似无忧。
**真相**:cron 在跑 ≠ 续签链路完整。逐节点检查发现:
| 问题 | 影响 |
|------|------|
| 部署路径为空(`Le_RealFullChainPath` 没配置) | 续签后证书不会部署到 Nginx 引用位置 |
| ReloadCmd 为空 | 新证书部署了但不 reload,一直用旧的 |
**最严重的情况**:某节点 4 个证书全部"部署路径 + reload 命令"双缺——key 会换新、证书不换,一旦 reload 就是 key/cert 不匹配,TLS 全挂。
**经验**
1. acme.sh 的字段名是 `Le_RealFullChainPath`(不是 `Le_RealCertPath`
2. 宝塔的 Nginx 不是 systemd 管理,reload 要用 `/etc/init.d/nginx reload`
3. 部署前必须核对 Nginx 实际引用的证书路径,不同节点布局可能完全不同
## 二、fail2ban 误伤自家节点
**现象**:NAS 访问某节点突然超时,SSH 连不上,网站也打不开。
**根因**fail2ban 的 `banaction_allports`(全端口封禁)把**自家 NAS 的出口 IP** 封了——原因是 NAS 通过堡垒机跳转时的认证失败触发了封禁规则。
**后果**SSH22)、HTTPS443)全被 nftables 切断。
**经验**
1. **舰队节点之间互相探测/跳转频繁,防爆破规则很容易误伤自己人**
2. 解决方案:把全部节点 IP 加入 fail2ban 的 `ignoreip` 白名单(注意要放在 `[DEFAULT]` 段,不是某个 jail 段末尾)
3. SSH 防爆破不要用 allports 封禁,只封 22 端口即可
## 三、Ansible 落地:控制端放 NAS
**选型**Ansible(无 agent、SSH 直连、YAML 声明式),控制端放在本地 NAS。
**认证方案**:生成 ed25519 密钥对,公钥分发到三节点 authorized_keys。注意:
1. **某节点 sshd 配置了 `PubkeyAuthentication no`**——公钥装了也连不上,需要改回 yes 并重启
2. **网络回程问题**:NAS 直连某节点时"发送通、接收断"(命令执行成功但输出回不来)——SSH 长连接(如插件)没问题,但 Ansible 短连接卡死
3. **解决方案**:用中间节点做 SSH 跳板(ProxyCommand 方式),绕开直连的线路问题
**注意**Ansible 有个在野 RCECVE-2026-11332ansible-galaxy role install 参数注入)——安装后立即升级到修复版(≥2.21.1),且只安装官方/高星 role。
## 四、Cloudflare WAF 加固
**发现**:托管 WAF 和 DDoS 规则集在列表里能看到,但实际**没有部署到域名**(entrypoint 不存在)——攻击根本没有防线。
**动作**
1. 用新版 WAF API(rulesets)部署自定义规则:拦截扫描器 UA、恶意路径(/.env、/.git、/phpmyadmin 等)
2. 根据源 IP 统计,把非国内的高频攻击源(云主机僵尸网络)按 /24 网段批量封禁
3. 三层防护:CF 边缘规则 + 源站防火墙 + 原有 fail2ban
**验证**:模拟攻击 `/.env` 请求 → 403 拦截成功 ✅
**局限**:CF 规则只对走 CF 代理(proxied)的域名生效,直连源站的域名需要靠源站防火墙兜底。
## 五、域名迁移:新域名做"前哨"
**思路**:把公开服务入口迁移到独立域名,走 CF 代理隐藏真实源站 IP。原域名收缩为内部服务,降低攻击面。
**要点**
- 新域名通过 CF DNS API 快速切换指向
- acme.sh + DNS Challenge 签发证书(无需开放 80 端口)
- 聚合导航页部署,一站式入口
- 自动续期链路完整配置(部署路径 + reload)
## 小结
一天的巡检发现:**"看起来在自动运行"的服务最危险**——acme.sh cron 在跑但续签链路缺一半、WAF 规则集存在但没部署、fail2ban 在封禁但误伤自己人。
自动化是双刃剑:配置了就要验证完整闭环,否则"自动"只是虚假安全感。
---
*(本文基于真实运维经验撰写,出于安全考虑隐去具体域名、IP、凭证信息)*