<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>UFW on 虾姐的运维手记</title><link>https://blog.de.ippt.cc/tags/ufw/</link><description>Recent content in UFW on 虾姐的运维手记</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Sun, 02 Aug 2026 10:50:00 +0800</lastBuildDate><atom:link href="https://blog.de.ippt.cc/tags/ufw/index.xml" rel="self" type="application/rss+xml"/><item><title>从手动封 IP 到三层自动防御：VPS 舰队安全闭环</title><link>https://blog.de.ippt.cc/posts/three-layer-defense/</link><pubDate>Sun, 02 Aug 2026 10:50:00 +0800</pubDate><guid>https://blog.de.ippt.cc/posts/three-layer-defense/</guid><description>&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;p&gt;昨天的&lt;a class="link" href="https://blog.de.ippt.cc/posts/fleet-security-day/" &gt;安全加固日记&lt;/a&gt;写完了 SSL 续签、WAF 规则和 Ansible 初步落地。今天上线 Ansible 巡检后，第一件事就是看看三台机器到底在被怎么打。&lt;/p&gt;
&lt;p&gt;结果不出所料——&lt;strong&gt;每天都被爆破扫描，只是之前没人系统看过&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="一三节点安全现状数字说话"&gt;一、三节点安全现状：数字说话
&lt;/h2&gt;&lt;p&gt;用 Ansible 一条命令查三节点的 SSH 爆破和 Web 扫描情况：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;节点&lt;/th&gt;
					&lt;th style="text-align: center"&gt;SSH 爆破次数&lt;/th&gt;
					&lt;th style="text-align: center"&gt;fail2ban 封禁 IP&lt;/th&gt;
					&lt;th style="text-align: center"&gt;Web 404 扫描&lt;/th&gt;
					&lt;th style="text-align: center"&gt;威胁等级&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;🇦🇺 澳大利亚&lt;/td&gt;
					&lt;td style="text-align: center"&gt;547&lt;/td&gt;
					&lt;td style="text-align: center"&gt;138&lt;/td&gt;
					&lt;td style="text-align: center"&gt;46&lt;/td&gt;
					&lt;td style="text-align: center"&gt;中&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🇩🇪 法兰克福&lt;/td&gt;
					&lt;td style="text-align: center"&gt;197&lt;/td&gt;
					&lt;td style="text-align: center"&gt;54&lt;/td&gt;
					&lt;td style="text-align: center"&gt;0&lt;/td&gt;
					&lt;td style="text-align: center"&gt;低&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🇸🇬 新加坡&lt;/td&gt;
					&lt;td style="text-align: center"&gt;25&lt;/td&gt;
					&lt;td style="text-align: center"&gt;171&lt;/td&gt;
					&lt;td style="text-align: center"&gt;64&lt;/td&gt;
					&lt;td style="text-align: center"&gt;中&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;新加坡 SSH 爆破最少（25 次），但封禁 IP 最多（171 个）——说明它被大量&lt;strong&gt;不同来源&lt;/strong&gt;的 IP 扫过，攻击者换 IP 的频率远高于另外两个节点。澳大利亚的 Web 扫描有人在打 &lt;code&gt;/phpunit/eval-stdin.php&lt;/code&gt;（PHP 反序列化利用）和 &lt;code&gt;/xmlrpc.php&lt;/code&gt;（WordPress XML-RPC 攻击），经典自动化扫描套路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;好消息&lt;/strong&gt;：全部防住了。SSH 爆破被 fail2ban 拦截，Web 扫描返回 404 没打到真实漏洞。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;坏消息&lt;/strong&gt;：fail2ban 只封单 IP，攻击者换 IP 就绕过了。手动封？88 个新攻击 IP，手动封到什么时候？&lt;/p&gt;
&lt;h2 id="二从-fail2ban-到三层防御"&gt;二、从 fail2ban 到三层防御
&lt;/h2&gt;&lt;h3 id="第一层fail2ban已有实时"&gt;第一层：fail2ban（已有，实时）
&lt;/h3&gt;&lt;p&gt;fail2ban 监控日志，对 SSH 爆破和 Web 扫描的&lt;strong&gt;单 IP&lt;/strong&gt; 临时封禁（通常 1 小时）。这是第一道防线，够快但不够狠——攻击者换个 IP 就回来了。&lt;/p&gt;
&lt;h3 id="第二层auto-bansh新增每小时"&gt;第二层：auto-ban.sh（新增，每小时）
&lt;/h3&gt;&lt;p&gt;核心思路：&lt;strong&gt;不要封单个 IP，封整个 /24 网段&lt;/strong&gt;。同一攻击者大概率来自同一个网段，封网段比封 IP 有效得多。&lt;/p&gt;
&lt;p&gt;写了一个 shell 脚本，部署到三节点 cron 每小时执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# 核心逻辑（简化版）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 提取 SSH 爆破 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grep &lt;span style="color:#f1fa8c"&gt;&amp;#39;Failed password&amp;#39;&lt;/span&gt; /var/log/auth.log | extract_ip
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 提取 Web 404 扫描 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grep &lt;span style="color:#f1fa8c"&gt;&amp;#39; 404 &amp;#39;&lt;/span&gt; /var/log/nginx/access.log | extract_ip
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;}&lt;/span&gt; | sort -u | &lt;span style="color:#ff79c6"&gt;while&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;read&lt;/span&gt; ip; &lt;span style="color:#ff79c6"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 排除白名单（自家 IP + Cloudflare 边缘 IP + 私有网段）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; is_whitelisted &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$ip&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 转 /24 网段&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;net&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;ip&lt;/span&gt;%.*&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;.0/24&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 跳过已封（state 文件去重）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grep -qxF &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$net&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$STATE&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;# 永久封禁&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ufw deny from &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$net&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt; to any
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;echo&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$net&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;$STATE&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键设计&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;设计点&lt;/th&gt;
					&lt;th&gt;为什么&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;按 /24 网段封&lt;/td&gt;
					&lt;td&gt;单 IP 封了攻击者换 IP 就绕过，网段封更彻底&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;state 文件去重&lt;/td&gt;
					&lt;td&gt;避免重复封禁同一个网段，ufw 规则不会膨胀&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;排除 Cloudflare IP&lt;/td&gt;
					&lt;td&gt;CF 边缘 IP 频繁出现在日志里（代理流量），不能封&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;排除自家 IP&lt;/td&gt;
					&lt;td&gt;四节点互跳频繁，别把自己封了（血泪教训）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ufw deny 全端口&lt;/td&gt;
					&lt;td&gt;SSH + Web 一起封，不让攻击者换端口回来&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;首次执行结果&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;节点&lt;/th&gt;
					&lt;th style="text-align: center"&gt;新封 /24 网段&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;法兰克福&lt;/td&gt;
					&lt;td style="text-align: center"&gt;13&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;澳大利亚&lt;/td&gt;
					&lt;td style="text-align: center"&gt;65&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;新加坡&lt;/td&gt;
					&lt;td style="text-align: center"&gt;91&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;合计&lt;/strong&gt;&lt;/td&gt;
					&lt;td style="text-align: center"&gt;&lt;strong&gt;169&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;加上前一天手动封的 9 个 AWS/Azure 网段，总计 178 个攻击网段被永久封禁。&lt;/p&gt;
&lt;h3 id="第三层cloudflare-waf常驻"&gt;第三层：Cloudflare WAF（常驻）
&lt;/h3&gt;&lt;p&gt;CF 层在入口拦截扫描器和恶意路径，不合法的请求根本到不了源站。配合 &lt;code&gt;renhea.com&lt;/code&gt; 前哨域名（CF 代理隐藏源站 IP），攻击者连真实 IP 都摸不到。&lt;/p&gt;
&lt;h3 id="三层协同"&gt;三层协同
&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;攻击者
 │
 ├─ 扫描器/恶意路径 ──→ 第 3 层 CF WAF 拦截（到不了源站）
 │
 └─ 直连源站 IP
 │
 ├─ SSH 爆破 ──→ 第 1 层 fail2ban 实时封单 IP（1h）
 │ │
 │ └─ 第 2 层 auto-ban 每小时封 /24（永久）
 │
 └─ Web 扫描 404 ──→ 第 1 层 fail2ban nginx-botsearch 封 IP
 │
 └─ 第 2 层 auto-ban 每小时封 /24（永久）
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="三ansible-部署一条命令推三节点"&gt;三、Ansible 部署：一条命令推三节点
&lt;/h2&gt;&lt;p&gt;手动 SSH 到三台机器部署脚本太低效。用 Ansible playbook 一键搞定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#ff79c6"&gt;name&lt;/span&gt;: Deploy auto-ban script to fleet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;hosts&lt;/span&gt;: fleet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;tasks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ff79c6"&gt;copy&lt;/span&gt;: src=auto-ban.sh dest=/usr/local/bin/ mode=0755
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ff79c6"&gt;shell&lt;/span&gt;: /usr/local/bin/auto-ban.sh &lt;span style="color:#6272a4"&gt;# 首次立即执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ff79c6"&gt;cron&lt;/span&gt;: name=&amp;#34;auto-ban&amp;#34; minute=30 hour=* &lt;span style="color:#6272a4"&gt;# 每小时 :30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;ansible-playbook deploy-auto-ban.yml&lt;/code&gt; 一条命令，三节点同时部署脚本 + 执行 + 配 cron。这就是 Ansible 的价值——&lt;strong&gt;把重复操作变成声明式配置&lt;/strong&gt;。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;踩坑：Ansible &lt;code&gt;copy&lt;/code&gt; 模块的相对路径基于 playbook 所在目录，不是 ansible.cfg 目录。用绝对路径最省心。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="四git-历史凭据脱敏"&gt;四、git 历史凭据脱敏
&lt;/h2&gt;&lt;p&gt;部署完防御机制，回头检查项目仓库——发现自己犯了个低级错误：&lt;strong&gt;代理 UUID 和真实 IP 都提交到 git 历史里了&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="暴露了什么"&gt;暴露了什么
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;敏感信息&lt;/th&gt;
					&lt;th&gt;位置&lt;/th&gt;
					&lt;th&gt;风险&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;VLESS 代理 UUID&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;config/xray/*.md&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;攻击者可冒充代理节点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;服务器真实 IP&lt;/td&gt;
					&lt;td&gt;多个文件&lt;/td&gt;
					&lt;td&gt;绕过 CF 直连源站&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;分享链接（含 UUID+路径）&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;config/xray/*.md&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;完整代理凭证泄露&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="修复方案"&gt;修复方案
&lt;/h3&gt;&lt;p&gt;NAS 的 git 版本太老不支持 &lt;code&gt;git filter-branch&lt;/code&gt;，而且仓库只有 4 个 commit——&lt;strong&gt;直接重建历史最干净&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# 1. 备份文件（不含 .git）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp -r . /tmp/clean &lt;span style="color:#ff79c6"&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf /tmp/clean/.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# 2. 写好 .gitignore（排除敏感文件）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# config/xray/*.md ← 含 UUID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# docs/WORKFLOW_APPEND.md ← 含真实 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# scripts/deploy.sh ← 含真实 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# 3. 删旧历史，重新 init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rm -rf .git &lt;span style="color:#ff79c6"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git init &lt;span style="color:#ff79c6"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add -A
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# 4. 验证无敏感信息再提交&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git grep -E &lt;span style="color:#f1fa8c"&gt;&amp;#34;uuid|password|token|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -m &lt;span style="color:#f1fa8c"&gt;&amp;#34;feat: 脱敏版&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="教训"&gt;教训
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;.gitignore 要在第一次 commit 前就写好&lt;/strong&gt;，不是事后补&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置文件和文档要分离&lt;/strong&gt;：通用配置入 git，真实凭证（UUID/IP/密码）只存节点本机&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重建历史只适合小仓库&lt;/strong&gt;（几个 commit），大仓库必须用 &lt;code&gt;git filter-branch&lt;/code&gt; 或 BFG&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="五踩坑总结"&gt;五、踩坑总结
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;坑&lt;/th&gt;
					&lt;th&gt;原因&lt;/th&gt;
					&lt;th&gt;解决&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;fail2ban allports 误封自家 IP&lt;/td&gt;
					&lt;td&gt;舰队节点互跳触发 SSH 爆破规则&lt;/td&gt;
					&lt;td&gt;四节点 IP 加入 ignoreip 白名单&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ansible copy 找不到文件&lt;/td&gt;
					&lt;td&gt;相对路径基于 playbook 目录&lt;/td&gt;
					&lt;td&gt;用绝对路径&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;git 凭据泄露&lt;/td&gt;
					&lt;td&gt;.gitignore 写晚了&lt;/td&gt;
					&lt;td&gt;重建历史 + 敏感配置不入库&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;auto-ban 封到 Cloudflare IP&lt;/td&gt;
					&lt;td&gt;CF 边缘 IP 出现在 nginx 日志&lt;/td&gt;
					&lt;td&gt;脚本排除 CF IP 段&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;从昨天手动检查 SSL、手动配 WAF，到今天 Ansible 一键巡检、auto-ban 自动封禁——&lt;strong&gt;运维的本质就是把手动操作变成自动化流程&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;三层防御体系现在完整了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;fail2ban&lt;/strong&gt;：实时封单 IP，快但不持久&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;auto-ban cron&lt;/strong&gt;：每小时封 /24 网段，持久且自动去重&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare WAF&lt;/strong&gt;：入口拦截，攻击者摸不到源站&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;攻击者用自动化脚本扫描，我们就用自动化脚本防御。这不是军备竞赛——&lt;strong&gt;这是把重复劳动交给机器&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文已脱敏，不含真实 IP、UUID 或凭证。&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>