czogueri/Homelab-Network-Security-
GitHub: czogueri/Homelab-Network-Security-
一份完整记录家庭网络多层安全体系构建过程的技术文档,涵盖 pfSense、Suricata 与 CrowdSec 的部署、调优及安全审计实操。
Stars: 0 | Forks: 0
# 家庭网络安全实验室:pfSense + Suricata + CrowdSec
这是一个针对家庭网络分层安全设置的完整构建文档,
运行在一个小型的家庭实验室上。这个项目最初只是为了“修复我的游戏 NAT”问题,
最终却演变成了一个完整的防御体系:一个经过妥善调优的防火墙、一个入侵
检测系统,以及一个分布式的、由社区驱动的拦截引擎——所有这些
都被设计为可以在普通硬件上运行。
这篇文章既是个人记录,也可作为作品集的一部分。
它侧重于每个决策背后的*原因*,而不仅仅是*操作方法*。
## 目录
1. [环境配置](#the-environment)
2. [架构概述](#architecture-overview)
3. [第 1 部分 — pfSense 防火墙与 NAT](#part-1--pfsense-firewall--nat)
4. [第 2 部分 — 公网 IP 安全审计](#part-2--security-auditing-the-public-ip)
5. [第 3 部分 — Suricata IDS](#part-3--suricata-ids)
6. [第 4 部分 — CrowdSec 分布式部署](#part-4--crowdsec-distributed-deployment)
7. [设计决策与权衡](#design-decisions--trade-offs)
8. [日常监控](#day-to-day-monitoring)
9. [经验总结](#what-i-learned)
## 环境配置
该网络运行在几台家庭实验室硬件上,每台设备都有明确的角色:
| 设备 | 角色 |
|---|---|
| 运行 Proxmox 的迷你 PC (4 GB RAM) | Hypervisor 宿主机 |
| pfSense (Proxmox 上的虚拟机) | 防火墙 / 路由器 / IDS |
| Raspberry Pi (Docker) | Pi-hole (全网 DNS + 广告拦截) |
| Linux 虚拟机 (Docker) | Nginx Proxy Manager + 自托管服务 |
互联网连接是通过 **PPPoE** 接入的光纤。防火墙暴露了少数几个
有意的入站服务(反向代理的 Web 应用、游戏、远程桌面
工具,以及一个入站的 WireGuard VPN),其他所有端口默认关闭。
在这次构建中,一个反复出现的主题是**在严格的内存限制下工作** —
宿主机的运行已接近其 RAM 上限,这直接影响了后续的几个架构
选择。
## 架构概述
```
Internet (PPPoE)
|
v
+--------------------------+
| pfSense |
| - Firewall / NAT |
| - Suricata (IDS) |
| - CrowdSec bouncer | <-- enforces blocks
+--------------------------+
|
LAN (192.168.x.0/24)
| | |
v v v
+----------+ +----------+ +--------------+
| Pi-hole | | NPM | | Other LAN |
| (DNS) | | + Crowd- | | devices |
| | | Sec | | |
| | | engine | | |
+----------+ +----------+ +--------------+
^
|
decisions pulled by
the pfSense bouncer
```
核心架构理念:**将“大脑”与“手脚”分离。**
- **CrowdSec 引擎**(大脑)运行在存有重要日志的机器上
— 即 Nginx Proxy Manager 服务器。它分析流量并决定拦截哪些对象。
- **CrowdSec Bouncer**(手脚)运行在 pfSense 上。它只在防火墙边缘执行
拦截决策。
这种分离将繁重的处理任务从内存受限的防火墙上卸载了下来。
## 第 1 部分 — pfSense 防火墙与 NAT
### 最初的问题
整个项目源于一个典型的症状:某款游戏提示遇到
**严格 NAT 类型**,这会导致无法进行匹配和语音聊天。同样的状况
在另一款游戏中也出现了,这是第一个有用的线索 — **如果多个
不相关的游戏都遇到严格 NAT,那问题就不在游戏本身,而在防火墙。**
### 为什么 pfSense + PPPoE 需要特殊处理
在 PPPoE 连接上,pfSense 的 WAN 接口是 **PPPoE 接口**,而不是
物理网卡。这很重要,因为 NAT 和防火墙规则必须
应用到正确的接口上,这是一个很容易出错的地方。
### 实际修复方法:静态端口出站 NAT
导致严格 NAT 的真正原因是**源端口随机化**。默认情况下,
pfSense 会重写出站源端口,这会干扰游戏赖以判断
NAT 类型的机制。
修复方法是将出站 NAT 切换为**混合模式**,并为
游戏设备添加一条启用**静态端口**的规则。这样可以在出站连接中保留原始源
端口。
**出站 NAT 规则(概念示例):**
```
Firewall > NAT > Outbound (Mode: Hybrid)
Interface: WAN
Source: /32
Translation: WAN address
Static Port: ENABLED
```
结合正确转发游戏所需的端口(并为动态需求启用 UPnP),
这彻底解决了所有受影响游戏中的严格 NAT 问题。
**经验教训:**针对单一游戏症状的修复,实际上揭示了普遍的防火墙
配置错误。永远要寻找共性根本原因。
## 第 2 部分 — 公网 IP 安全审计
既然开始暴露入站服务,随之而来的自然问题是:
**“当互联网访问我的 IP 时,它实际上能看到什么?”**
### 扫描自己的公网 IP
我使用网络内 Kali 虚拟机上的 `nmap`,扫描了自己的公网 IP,以
审计暴露的攻击面:
```
# 最常见端口的 Basic scan
nmap -sS -Pn --top-ports 1000
# 对返回 open 的内容进行 Service/version detection
nmap -sV -Pn -p
```
### 扫描结果(以及我如何修复)
扫描发现了两样我**不**打算暴露的内容:
**1. 53 端口 (DNS) — 开放**
`nmap -sV` 识别出该服务为 **Unbound**,即 pfSense 的 DNS 解析器。一个
开放的、可被公开访问的 DNS 解析器是一个真正的问题 — 它可能
在 DDoS 攻击中被滥用作放大器。
一个值得注意的排查细节:Unbound 绑定到了**所有接口**,
这意味着它会直接在 WAN IP 上响应。因为流量的目的地是
*防火墙本身*,所以普通的 WAN 拦截规则是不够的 — 发往
防火墙自身服务的流量,其穿过滤权机制与直通流量是不同的。
正确的修复方法是在**应用层**,使用 Unbound 的访问列表来
拒绝来自局域网外部的查询:
```
Services > DNS Resolver > Access Lists
Allow : 192.168.x.0/24 (LAN)
Refuse : 0.0.0.0/0 (everything else)
```
从外部视角进行了验证 — 现在对公网 IP 的 DNS 查询会
超时,而内部解析仍然完美运行。
**2. 443 端口 (HTTPS) — 开放**
事实证明这是 **Nginx Proxy Manager** 在提供合法的
反向代理服务(通过 DuckDNS 动态 DNS)。这是有意为之且
预期之中的 — 但正是审计过程让你能够自信地分辨“有意为之”和
“意外暴露”。
**经验教训:***nmap 中的“开放”并不自动意味着“易受攻击”* — 一个
有响应但拒绝执行操作的服务(比如对外部访问者返回 REFUSED 的 Unbound)是
受到有效保护的。理解这其中的差异才是运维的核心。
## 第 3 部分 — Suricata IDS
在收紧了边界之后,下一层是**可见性** — 了解何时
有东西正在探测或攻击网络。
### 设置
**Suricata** 被安装在 pfSense 上(通过内置的 Package Manager),部署在
**WAN 接口**上,运行于 **IDS(仅告警)模式**。起初特意*没有*使用 IPS/拦截
模式 — 在让系统自动丢弃任何流量之前,你需要先观察并进行调优,
否则可能会拦截掉你自己的合法服务。
规则使用了免费的 **ET Open** 规则集,重点关注对暴露
主机最为重要的类别:扫描、漏洞利用和恶意软件。
### 阅读告警:信号与噪声
使用 IDS 时最有价值的技能是**甄别** — 大多数告警都是噪声,
你必须学会区分它们。以下是来自此次部署的真实案例:
**噪声(已抑制):**
- `SURICATA QUIC failed decrypt` — Suricata 无法解密的正常加密
Google/YouTube 流量。无害。
- `STREAM excessive retransmissions` / `SYN/ACK ignored TFO` — 正常的 TCP 怪癖
和延迟连接。不是安全事件。
**低价值但保留(被拦截的扫描):**
- `ET SCAN Suspicious inbound to MySQL/MSSQL/PostgreSQL` — 不断
在互联网上扫描暴露数据库的僵尸网络。我的防火墙早就丢弃了这些;
告警只是确认了这一点。这是上网的“背景辐射”。
**真正值得关注的:**
- `ET EXPLOIT Apache HTTP Server Path Traversal (CVE-2021-41773 / 42013)` —
针对我*确实*暴露的 80 端口的一次真实**漏洞利用尝试**。
### 调查真实的漏洞利用尝试
Apache 漏洞利用告警是一个很好的案例研究,教你如何*不恐慌*并且*去验证*:
- 该漏洞利用专门针对 **Apache HTTP Server 2.4.49/2.4.50**。
- 我暴露的服务是 **Nginx Proxy Manager**,它**不受**该
CVE 影响。
- 攻击者只是在盲目地对每个开放了 80 端口的 IP 蛮干扫射这个 exploit —
并不是专门针对我。
结论:**方向和上下文很重要。**针对你并未运行的服务的
入站漏洞利用就是噪声。真正能让我立刻放下手头一切去处理的告警是
从我的内部网络连接到已知恶意主机的*出站*连接 — 那将
意味着设备已经被入侵了。
### 理解严重程度
Suricata 会为每个告警分配一个优先级。针对我实际暴露端口的优先级 `1` 是我的“立即查看”级别。优先级 `2`/`3` 的扫描和协议噪声
则属于“扫一眼就过”的级别。
## 第 4 部分 — CrowdSec 分布式部署
Suricata 告诉你发生了什么。**CrowdSec 则负责采取行动** — 而且
至关重要的是,它接入了一个共享威胁情报的全球社区。
### 为什么选择 CrowdSec,以及它的两大优势
1. **它监视着真正的攻击面。**通过解析 Nginx Proxy Manager 的
访问日志,CrowdSec 能够看到针对我实际暴露的服务发起的
真实应用层攻击。
2. **社区黑名单。**当一个 IP 攻击了全球范围内的*任何* CrowdSec 用户,它就会
被添加到一个共享列表中,每个人都可以预先拦截它。你可以在
已知攻击者*到达你这里之前*就拦截他们。
### 最为关键的架构决策
CrowdSec 由两个部分组成,可以跨机器分离部署:
- **Security Engine** — 消耗大量内存的大脑(解析日志、维护数据库、
拉取社区情报、做出决策)。
- **Firewall Bouncer** — 一个轻量级的执行器,只负责拦截被告知要拦截的 IP。
因为防火墙运行在内存受限的迷你 PC 上,将完整的
引擎放在那里是不可行的。干净的解决方案 — 这本身也是*良好的
架构设计* — 是这样的:
- **引擎 → 部署在 NPM 服务器上**(重要日志本来就在这里,因此不需要
跨机器传输日志)。
- **Bouncer → 部署在 pfSense 上**(以 CrowdSec 的 "Small" 模式运行:仅有 bouncer,没有本地
引擎,没有日志处理器 — 占用最小的资源)。
这就是“将大脑与手脚分离”理念的实际应用。
### 引擎设置(Docker,在 NPM 机器上)
引擎作为 Docker 容器与 NPM 一起运行。简化后的 compose 配置如下:
```
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
environment:
COLLECTIONS: "crowdsecurity/nginx-proxy-manager crowdsecurity/http-cve crowdsecurity/base-http-scenarios"
GID: "1000"
volumes:
- ./config:/etc/crowdsec
- ./data:/var/lib/crowdsec/data
- /path/to/npm/data/logs:/var/log/npm:ro # NPM logs, read-only
ports:
- "8080:8080" # LAPI, for the pfSense bouncer
restart: unless-stopped
```
**日志获取** (`acquis.yaml`) — 特意只针对实时的访问
日志,而不是嘈杂的证书续订日志或轮转归档:
```
filenames:
- /var/log/npm/proxy-host-*_access.log
- /var/log/npm/fallback_http_access.log
labels:
type: nginx
```
构建过程中的一个有用调试提示:CrowdSec 是从文件的**末尾**
开始读取日志的(仅读取新条目),因此在有新
流量到达之前,获取指标看起来都是空的。这是预期行为,不是故障。挂载日志的文件
**权限**是另一个需要注意的地方 — 容器需要对
宿主机的日志文件具有读取权限。
### Bouncer 设置(pfSense,"Small" 模式)
pfSense 的 CrowdSec 软件包由社区维护,并通过 shell 脚本安装
(它不在官方的 pfSense 软件包源中)。配置为 **Small**:
- 本地 API:**禁用**
- 日志处理器:**禁用**
- Remediation 组件 (bouncer):**启用**
- 远程 LAPI:指向 NPM 机器上的引擎 (`http://:8080/`)
有两个凭证用于将 bouncer 连接到引擎:
- 一个 **bouncer API 密钥** (`cscli bouncers add ...`) — 用于验证执行器。
- 一个 **机器登录名/密码** (`cscli machines add ...`) — 将 pfSense 注册为
远程 API 的一个代理。
### 验证其是否生效
bouncer 连接正常,并按计划拉取数据:
```
$ cscli bouncers list
Name Valid Last API pull Type
pfsense-firewall ✔️ 2026-..T18:40:40Z crowdsec-firewall-bouncer
```
而这才是最重要的证明 — 直接读取 pfSense 上的**实际 pf 表**,而不仅仅是看 GUI:
```
$ pfctl -t crowdsec_blacklists -T show | wc -l
14534
```
**约 14,500 个已知的恶意 IP** 被加载到防火墙中并在边缘被丢弃,
这些 IP 来源于社区并自动刷新。pfSense 插件管理着
两个自动生成的别名(IPv4 的 `crowdsec_blacklists` 和
IPv6 的 `crowdsec6_blacklists`) — 它们由 bouncer 管理,永远
不应手动编辑。
## 设计决策与权衡
| 决策 | 原因 |
|---|---|
| 引擎部署在 NPM 机器上,而非 pfSense | 将繁重的处理任务从内存受限的防火墙上卸载;日志本身就在本地 |
| Bouncer 采用 "Small" 模式 | 在防火墙上占用最小的资源 — 仅执行拦截 |
| Suricata 采用 IDS (仅告警) 式 | 在冒险自动拦截合法流量之前,先进行观察和调优 |
| 通过访问列表而非防火墙规则修复 DNS 暴露 | 发往防火墙自身服务的流量需要应用层修复 |
| 静态端口出站 NAT | 针对严格 NAT 的精准修复,而非盲目的 DMZ 暴露 |
| 保留扫描告警,抑制协议噪声 | 保留有意义的信号,减少杂乱信息 |
## 日常监控
**在引擎上 (NPM 机器):**
```
cscli metrics # overview: parsing activity, decision counts
cscli decisions list # who is currently blocked, and why
cscli alerts list # what attacks have been detected
cscli bouncers list # confirm the pfSense bouncer is still syncing
```
**在 pfSense 上:**
```
pfctl -t crowdsec_blacklists -T show | wc -l # confirm blocklist is loaded
```
如果需要可视化仪表板,可以将引擎注册到免费的 **CrowdSec
Console** (`app.crowdsec.net`) 中,它提供基于 Web 的指标、地图和涵盖整个部署的告警历史记录。
## 经验总结
- **症状往往掩盖了真实原因。**一个“游戏”问题其实是防火墙 NAT
配置错误;修复方法与游戏本身毫无关系。
- **从外向内进行审计。**扫描你自己的公网 IP 是找出意外暴露
的最快方法。
- **“开放” ≠ “易受攻击”。**上下文 — 即究竟是什么在监听,以及它是否会为陌生访客执行操作 — 才是决定一切的关键。
- **IDS 的效果取决于你的甄别能力。**真正的技能在于学会区分噪声与
信号,而不仅仅是开启它。
- **架构胜过暴力破解。**解决资源限制的最好方法是更聪明的
设计(将引擎与 bouncer 分离),而不是添加更强大的硬件。
- **分层带来复合防御。**防火墙 + IDS + 社区驱动的拦截各自覆盖
不同的盲区。结合在一起,它们比其中任何一个都要强大得多。
## 技术栈摘要
**防火墙/路由器:**pfSense (基于 Proxmox)
**IDS:**Suricata (ET Open 规则集,IDS 模式)
**IPS/拦截:**CrowdSec (分布式:引擎 + 防火墙 bouncer)
**DNS/广告拦截:**Pi-hole
**反向代理:**Nginx Proxy Manager
**侦察/测试:**nmap, Wireshark (Kali 虚拟机)
*本文档中已省略或泛化了所有的密钥、凭证、公网 IP 及内部细节。*
标签:Metaprompt, pfSense, Proxmox, 家庭实验室, 网络安全, 请求拦截, 运维部署, 防火墙, 隐私保护