[简体中文] / English


AIO Ep22. PVE 主机配置 port mirroring 及 Suricata IDS 防火墙

 

背景

我的 HomeLab PVE 主机上一直有着十分混乱的各类 workload, 从简单的 SMB/syncthing 文件共享, 到各类爬虫/数据库, 还有我用于开发的 Windows/Linux 虚拟机.

尽管我已在 PVE 控制台配置了应有的内网防火墙规则, 我仍能在 Netdata 监控中看到不明且难以追溯的互联网流量. 我用 tcpdump 调查过一些, 但比较费时费力.

所以打算部署一个类似企业级硬件防火墙的流量监控分析功能, 可以监听所有内网的流量, 并进行 DPI (Deep Packet Inspection) 理解其中的应用协议, 记录对应事件, 做成 Dashboard 供检查.

IDS (Intrusion Detection System) 是这种网络分析软件的常见附加功能, 可以自动按规则报告可疑的流量, 能发现一些配置错误或恶意入侵的痕迹.


一开始我尝试了一些真正起夜企业级的产品, 例如 FortiGateSophos 的免费试用虚拟机. 但试用下来效果都不太满意, 它们的配置很复杂晦涩不提, 对事件的记录和查看也不够清晰.

最后我选择了开源的 Suricata 以及前端 Evebox, 虽然 Evebox UI 简陋了一些, 但基本能满足我的需求, 并且 CPU 和内存占用量很低.

安装

LXC 容器与端口镜像 (port mirroring)

在 PVE 创建一个 Debian 12 的 LXC 容器, 比如就叫他 suricata. 先添加一个 vmbr0 网桥上的正常网卡 eth0, 分配一个 IP 地址, 用于正常访问该容器.

在 PVE Dashboard 中 本机 - System - Network 菜单中, 创建一个 Liunx bridge vmbr1, 只用于桥接流量, 不用指定 IP 地址和网关, 暂时也不用绑定接口.

在 PVE 主机运行以下脚本, 将所有虚拟机/容器/主机上的流量, 镜像一份到 vmbr1 网桥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
apply_tc_rules() {
local port_name=$1

echo "Applying tc rules to $port_name"
tc qdisc del dev $port_name root
tc qdisc del dev $port_name ingress

tc qdisc add dev $port_name ingress
tc filter add dev $port_name parent ffff: protocol all u32 match u8 0 0 action mirred egress mirror dev vmbr1
tc qdisc add dev $port_name root handle 1: prio
tc filter add dev $port_name parent 1: protocol all u32 match u8 0 0 action mirred egress mirror dev vmbr1
}

apply_tc_to_fwpr_ports() {
local bridge_port="vmbr0"
apply_tc_rules $bridge_port
for port in /sys/class/net/$bridge_port/brif/fwpr*; do
if [ -d "$port" ]; then
port_name=$(basename "$port")
if [[ $port_name == fwpr* ]]; then
apply_tc_rules $port_name
fi
fi
done
}

apply_tc_to_fwpr_ports

这里的 tc (traffic control) 指令的语法未免有些诡异, tc filter add dev vmbr0 parent ffff: protocol all u32 match u8 0 0 action mirred egress mirror dev vmbr1 很少见参数这么长的神秘指令. 由于我自己也不太懂, 我就不详细展开了, 感兴趣的读者可以自行研究一下 tc 的用法. 这是我搜到的 Linux 下镜像端口最简单的方法了.

回到正题, 这里脚本里 apply_tc_rules 会将某个 port 的出入双向的流量复制一份, 发送到 vmbr1 接口, 而 apply_tc_to_fwpr_ports 则会对 vmbr0 及其上桥接的容器/虚拟机接口应用 apply_tc_rules.

脚本执行后, vmbr0 上的所有流量就被发到了 vmbr1. (不过如果有新 VM/容器, 需要对新接口也执行)

给刚刚的 suricata 容器再添加一个 vmbr1 上的网卡 eth1, 同样不必配置 IP. 这样在容器中, 我们就能在 eth1 上监听到主机的所有流量了. 可以用 tcpdump 来验证.

Suricata

如同开头所说, Suricata 是一个高性能且开源的网络分析和威胁检测软件. 我们将其运行在 LXC 容器中, 监听 eth1 上的流量.

官方的 Debian 源缺少维护, 先根据官方文档进行编译安装: https://docs.suricata.io/en/latest/install.html, 记得 make install-conf 生成默认配置文件.

配置 systemd 配置文件 /etc/systemd/system/suricata.service:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=Suricata Intrusion Detection Service
After=syslog.target network-online.target

[Service]
# Environment file to pick up $OPTIONS.
#EnvironmentFile=-/etc/default/suricata
ExecStartPre=/bin/rm -f /usr/local/var/run/suricata.pid
ExecStart=/usr/local/bin/suricata -c /usr/local/etc/suricata/suricata.yaml --af-packet --pidfile /usr/local/var/run/suricata.pid $OPTIONS
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

按需修改 suricata 配置文件 /usr/local/etc/suricata/suricata.yaml:

1
2
3
# (配置很长, 请参考注释按需修改, 这里只列出必须修改的部分)
af-packet:
- interface: eth1 # 修改为需要监听的网卡

启动前使用 suricata-update 下载 Suricata 官方默认的一套检测规则.

完成后进行 systemctl daemon-reloadsystem enable --now suricata 启动 Suricata 服务, 如一切顺利, 此时 Suricata 应该已经启动了:

1
2
3
4
5
6
# systemctl status suricata
● suricata.service - Suricata Intrusion Detection Service
Loaded: loaded (/etc/systemd/system/suricata.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-07-14 12:59:51 UTC; 1s ago
Process: 2792 ExecStartPre=/bin/rm -f /usr/local/var/run/suricata.pid exited, status=0/SUCCESS)
Main PID: 2793 (Suricata-Main)

默认配置下, Suricata 会将事件写入 /usr/local/var/log/suricata/ 下的 eve.json 中, 这会是一个不断扩张的 JSON 文件, 需要用户自己配置 logrotate.

我的配置 /etc/logrotate.d/suricata 供参考:

1
2
3
4
5
6
7
8
9
10
11
12
/usr/local/var/log/suricata/*.log /usr/local/var/log/suricata/*.json
{
daily
rotate 1
missingok
nocompress
create 640 root root
sharedscripts
postrotate
/bin/kill -HUP `cat /usr/local/var/run/suricata.pid 2>/dev/null` 2>/dev/null || true
endscript
}

配置好后可以 logrotate -f /etc/logrotate.d/suricata 手动强制触发 rotate 验证

Suricata 的安装暂时就 OK 了, 其还有很多高级功能, 见其官方文档: https://docs.suricata.io/en/latest/what-is-suricata.html

EveBox

EveBox 是一个可以消费并处理 Suricata 事件的前端.

从官方下载二进制并放到 /usr/local/bin/evebox.

配置 systemd 服务 /etc/systemd/system/evebox.service 启动 EveBox 消费 Suricata 事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Unit]
Description=EveBox Server
After=network-online.target suricata.service
Wants=network-online.target
Requires=suricata.service
Documentation=https://evebox.org/docs/

[Service]
Type=simple
ExecStart=/usr/local/bin/evebox server -c /etc/evebox/config.yaml
Restart=on-failure
RestartSec=5s
LimitNOFILE=65535
User=root
Group=root

[Install]
WantedBy=multi-user.target

编写 /etc/evebox/config.yaml 配置文件 (完整的配置参考样例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# EveBox Server configuration file.
http:
host: 0.0.0.0
tls:
enabled: true

authentication:
required: false

database:
# Database type: elasticsearch, sqlite.
type: sqlite
retention:
# Only keep events for the past 7 days.
days: 7

# Maximum database size.
size: "15 GB"

# The server can process a log file, eliminating the need for a
# separate agent process if on the same machine.
input:
# Toggle to disable the input without commenting it out.
enabled: true

# Suricata EVE file patterns to look for and read.
paths:
- "/usr/local/var/log/suricata/eve.json"
- "/usr/local/var/log/suricata/eve.*.json"

同理使用 systemd 启动服务, 就可以直接从网页访问了. Suricata 产出的 JSON 数据会被 EveBox 转存在 /var/lib/evebox/config.sqlite 中, 并按最大 7 天和最大 15 GB 的规则自动轮转.

监听到的事件列表监听到的事件列表

一个监控大盘一个监控大盘

一个被记录到的 TLS 流量, 识别到的不同流量有不同字段一个被记录到的 TLS 流量, 识别到的不同流量有不同字段

本文采用 CC BY-NC-SA 4.0 许可协议发布.

作者: lyc8503, 文章链接: https://blog.lyc8503.net/post/22-network-ids/
如果本文给你带来了帮助或让你觉得有趣, 可以考虑赞助我¬_¬