<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CrowdSec | 个人主页</title><link>https://page.syscall.cn/tags/crowdsec/</link><atom:link href="https://page.syscall.cn/tags/crowdsec/index.xml" rel="self" type="application/rss+xml"/><description>CrowdSec</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh-Hans</language><lastBuildDate>Fri, 18 Sep 2026 00:00:00 +0000</lastBuildDate><image><url>https://page.syscall.cn/media/icon_hu7729264130191091259.png</url><title>CrowdSec</title><link>https://page.syscall.cn/tags/crowdsec/</link></image><item><title>🛡 家庭服务器的安全加固：从端口映射到 CrowdSec 联动封禁</title><link>https://page.syscall.cn/post/homelab-security-hardening/</link><pubDate>Fri, 18 Sep 2026 00:00:00 +0000</pubDate><guid>https://page.syscall.cn/post/homelab-security-hardening/</guid><description>&lt;blockquote>
&lt;p>自托管服务的第一个念头是&amp;quot;怎么把它跑起来&amp;quot;，第二个念头应该是&amp;quot;怎么别让它被人玩坏&amp;quot;。这篇是第二个念头的完整记录。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一先收敛暴露面">一、先收敛暴露面&lt;/h2>
&lt;p>在装任何防护软件之前，先做减法。我的检查清单：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 哪些端口真的在监听&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ss -tlnp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 容器端口是否误暴露到公网&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps --format &lt;span class="s1">&amp;#39;{{.Names}}\t{{.Ports}}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三条原则：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>容器端口只监听内网&lt;/strong>。宁可 &lt;code>127.0.0.1:2283&lt;/code> 也不要 &lt;code>0.0.0.0:2283&lt;/code>。&lt;/li>
&lt;li>&lt;strong>对外只保留必要的入口&lt;/strong>。一个 nginx 做统一入口，比十个服务各自开端口好管一百倍。&lt;/li>
&lt;li>&lt;strong>默认端口不是安全边界&lt;/strong>，但也别把管理面板挂在 80 上 —— SSH、面板、数据库都不该出现在公网扫描的常见命中列表里。&lt;/li>
&lt;/ol>
&lt;h2 id="二crowdsec用社区情报做本地防护">二、CrowdSec：用社区情报做本地防护&lt;/h2>
&lt;p>CrowdSec 的架构比 fail2ban 更值得用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">日志解析 → 场景判定 → 决策（decision）→ LAPI 存储 → bouncer 执行封禁
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我用的部署是&lt;strong>两级&lt;/strong>的：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>应用层节点&lt;/strong>：跑 LAPI（决策中心）+ 日志解析器，:8080 提供决策 API&lt;/li>
&lt;li>&lt;strong>边缘节点&lt;/strong>：跑 bouncer，从远程 LAPI 拉决策，在 nginx 层拒绝请求&lt;/li>
&lt;/ul>
&lt;p>这样的好处是&lt;strong>决策与执行解耦&lt;/strong>：多个入口（nginx / 防火墙 / 其他主机）可以共享同一份黑名单。&lt;/p>
&lt;p>nginx bouncer 的最小配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># http{} 内
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">8888&lt;/span> &lt;span class="s">ssl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># bouncer 通过 auth_request 或 lua 拦截
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1"># 命中黑名单 → 403
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="三坑一限流区定义位置">三、坑一：限流区定义位置&lt;/h2>
&lt;p>给防护页面加限流防刷，我第一次这么写：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">8888&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ❌ 这里不行
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kn">limit_req_zone&lt;/span> &lt;span class="nv">$binary_remote_addr&lt;/span> &lt;span class="s">zone=api:10m&lt;/span> &lt;span class="s">rate=10r/s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>nginx 直接起不来。原因：&lt;strong>&lt;code>limit_req_zone&lt;/code> 是 &lt;code>http{}&lt;/code> 级别的指令&lt;/strong>，必须写在 &lt;code>http{}&lt;/code> 内、&lt;code>server{}&lt;/code> 之外。&lt;/p>
&lt;p>正确写法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">http&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">limit_req_zone&lt;/span> &lt;span class="nv">$binary_remote_addr&lt;/span> &lt;span class="s">zone=api:10m&lt;/span> &lt;span class="s">rate=30r/s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">8888&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="s">/api/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">limit_req&lt;/span> &lt;span class="s">zone=api&lt;/span> &lt;span class="s">burst=20&lt;/span> &lt;span class="s">nodelay&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">limit_req_status&lt;/span> &lt;span class="mi">429&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>burst&lt;/code> 是允许的突发额度，&lt;code>nodelay&lt;/code> 表示突发部分立即放行（不排队）。防护类页面上线时这两个参数很关键 —— 小主机上排队比拒绝更危险。&lt;/p>
&lt;h2 id="四坑二防护机制把自己封了">四、坑二：防护机制把自己封了&lt;/h2>
&lt;p>这是最值得记录的一个坑，因为它属于&amp;quot;机制反噬&amp;quot;：&lt;/p>
&lt;blockquote>
&lt;p>我做压测时，防护组件判定&lt;strong>来源 IP 有攻击行为&lt;/strong>，于是把发起压测的 IP 封禁了。而这个 IP 恰好是我自己 SSH 用的出口 IP —— 结果表现为 &lt;strong>SSH 假死&lt;/strong>，看起来像网络断了，实际是被自己的防护墙拒绝。&lt;/p>
&lt;/blockquote>
&lt;p>排查过程很有代表性：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 首先怀疑网络&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ping &amp;lt;host&amp;gt; &lt;span class="c1"># 通？&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh &amp;lt;host&amp;gt; &lt;span class="c1"># 挂？&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 从另一个网络位置尝试&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># → 能连上 → 说明服务活着，是&amp;#34;某个来源&amp;#34;被拒&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 直接查决策表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cscli decisions list
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 找到自己的 IP，删除&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cscli decisions delete --ip &amp;lt;your-ip&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>结论与对策：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>压测/扫描&lt;strong>永远从第三方网络位置发起&lt;/strong>，不要用管理出口 IP&lt;/li>
&lt;li>给管理 IP 加&lt;strong>白名单&lt;/strong>（trusted IPs），比事后救火靠谱&lt;/li>
&lt;li>记住 &lt;code>cscli decisions delete --ip &amp;lt;x&amp;gt;&lt;/code> 这条救命命令&lt;/li>
&lt;li>反过来这证明防护是&lt;strong>真的在工作&lt;/strong> —— 一个从不误封的 IDS 通常也没在工作&lt;/li>
&lt;/ul>
&lt;h2 id="五坑三光猫映射只能增量改">五、坑三：光猫映射只能增量改&lt;/h2>
&lt;p>运营商光猫的端口映射接口有个危险特性：&lt;strong>某些操作是整体替换而非单条修改&lt;/strong>。我踩过一次映射表被覆盖，服务全断。&lt;/p>
&lt;p>现在我的流程固定为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1. 先导出/记录当前完整映射表
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 只对目标条目做 add-only 操作（新增，绝不批量提交）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 立即读回校验：新增条目在？旧条目还在？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 任何异常 → 用步骤 1 的记录回滚
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样适用于任何&amp;quot;配置整体提交&amp;quot;的管理面板 —— &lt;strong>先备份，再增量，后校验。&lt;/strong>&lt;/p>
&lt;h2 id="六坑四cgi-的-fastcgi_params-陷阱">六、坑四：CGI 的 &lt;code>fastcgi_params&lt;/code> 陷阱&lt;/h2>
&lt;p>我想用 nginx + fcgiwrap 提供一个输出网卡状态的探针。配置写完，所有请求 404：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">location&lt;/span> &lt;span class="s">/eth0&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">include&lt;/span> &lt;span class="s">fastcgi_params&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1"># ❌ 元凶
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kn">fastcgi_pass&lt;/span> &lt;span class="s">unix:/run/fcgiwrap.socket&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原因：&lt;code>fastcgi_params&lt;/code> 里有一行&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它会&lt;strong>覆盖&lt;/strong>你设定的 &lt;code>SCRIPT_FILENAME&lt;/code>，指向一个不存在的路径，于是 CGI 找不到脚本 → 404。&lt;/p>
&lt;p>正确做法是&lt;strong>手工列出需要的参数&lt;/strong>，不要 include 这个文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">location&lt;/span> &lt;span class="s">/eth0&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_pass&lt;/span> &lt;span class="s">unix:/run/fcgiwrap.socket&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_param&lt;/span> &lt;span class="s">SCRIPT_FILENAME&lt;/span> &lt;span class="s">/path/to/script.sh&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_param&lt;/span> &lt;span class="s">QUERY_STRING&lt;/span> &lt;span class="nv">$query_string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_param&lt;/span> &lt;span class="s">REQUEST_METHOD&lt;/span> &lt;span class="nv">$request_method&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_param&lt;/span> &lt;span class="s">CONTENT_TYPE&lt;/span> &lt;span class="nv">$content_type&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">fastcgi_param&lt;/span> &lt;span class="s">SERVER_PROTOCOL&lt;/span> &lt;span class="nv">$server_protocol&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="七坑五域名到期--安全事件">七、坑五：域名到期 = 安全事件&lt;/h2>
&lt;p>最后这个坑最容易被忽视，但后果最严重：&lt;/p>
&lt;p>站点配置里留着一个旧域名的引用。那个域名&lt;strong>我没有续费&lt;/strong>。结果：&lt;/p>
&lt;ol>
&lt;li>域名掉落后被&lt;strong>抢注&lt;/strong>&lt;/li>
&lt;li>抢注者配置了&lt;strong>通配解析&lt;/strong>，所有子域指向一个诈骗页面&lt;/li>
&lt;li>我的站点 canonical / sitemap / RSS &lt;strong>全部仍然指向旧域名&lt;/strong>&lt;/li>
&lt;li>搜索引擎和社交分享卡片全部导流到诈骗站&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>技术上站点是正常的，但对外身份被劫持了。&lt;/strong>&lt;/p>
&lt;p>对策清单：&lt;/p>
&lt;ul>
&lt;li>定期检查所有在用的域名到期时间（whois / RDAP）&lt;/li>
&lt;li>域名一旦不打算再用，&lt;strong>立刻从所有配置中清除&lt;/strong>，包括 canonical、sitemap、RSS、&lt;code>server_name&lt;/code>、&lt;code>/etc/hosts&lt;/code>&lt;/li>
&lt;li>迁移域名时&lt;strong>检查 baseURL 这类全局变量&lt;/strong> —— 只改 DNS 和证书是不够的&lt;/li>
&lt;li>搜索引擎收录是滞后的，修复后要等重新抓取&lt;/li>
&lt;/ul>
&lt;h2 id="八小结">八、小结&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层次&lt;/th>
&lt;th>措施&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>暴露面&lt;/td>
&lt;td>端口收敛 + 容器端口内网化 + 统一入口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>检测&lt;/td>
&lt;td>CrowdSec 日志解析 + 场景判定&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>阻断&lt;/td>
&lt;td>bouncer 在边缘执行，决策集中管理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>抗压&lt;/td>
&lt;td>&lt;code>limit_req&lt;/code> + 热缓存读写分离&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>变更安全&lt;/td>
&lt;td>备份 → 增量 → 校验 → 可回滚&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>身份安全&lt;/td>
&lt;td>域名到期监控 + 全局变量一致性检查&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>安全加固不是装一个软件，而是让每一次变更都可回滚、每一份对外身份都可追溯。&lt;/strong>&lt;/p></description></item></channel></rss>