装了 AdGuard Home 之后,查询日志让我第一次看清了家里那些“智能”设备有多能折腾:一个摄像头一天发几千个 DNS 请求,智能插座的固件好几年没更新过,扫地机器人和电视也在不停地往外连。这些设备和我的 NAS、电脑在同一个网段里,彼此之间畅通无阻,想想还是有点不放心。
于是这个月终于下决心划 VLAN,把它们隔离出去。
规划
| VLAN | 网段 | 用途 |
|---|---|---|
| 10 | 192.168.10.0/24 | 主网:电脑、手机、服务器 |
| 20 | 192.168.20.0/24 | IoT:插座、摄像头、扫地机、电视 |
访问规则:
- IoT 可以访问公网,可以访问 AdGuard Home 的 53 端口,除此之外不能访问任何内网地址;
- 主网可以主动访问 IoT(比如在电脑上打开摄像头的管理页面),IoT 只能回应,不能主动发起连接。
需要的硬件有三样:能做 VLAN 的路由器、支持 802.1Q 的网管交换机、能把不同 SSID 绑定到不同 VLAN 的无线 AP。我家的路由器是一台装了 Debian 的双网口小主机,enp2s0 接光猫,enp1s0 接交换机,本来就用 nftables 做 NAT、用 dnsmasq 发 DHCP,改起来比较自由。
为了少动已有的配置,主网在交换机上虽然是 VLAN 10,但在接路由器、AP 和 Proxmox 的这几个口上,它都以不带标签(untagged)的形式出现,VLAN 20 则带着标签(tagged)走同一根线。接有线 IoT 设备(电视)的那个口,设成 VLAN 20 不带标签。
路由器:新增 VLAN 接口和 DHCP
# /etc/network/interfaces 里新增
auto enp1s0.20
iface enp1s0.20 inet static
address 192.168.20.1/24
# /etc/dnsmasq.d/iot.conf
interface=enp1s0.20
dhcp-range=set:iot,192.168.20.100,192.168.20.250,255.255.255.0,12h
dhcp-option=tag:iot,option:router,192.168.20.1
dhcp-option=tag:iot,option:dns-server,192.168.10.53
Debian 的 ifupdown 会把 enp1s0.20 这种带点的接口名自动识别成 VLAN 子接口。IoT 设备的 DNS 直接指向 AdGuard Home,这样在查询日志里还能按设备区分。
路由器:防火墙规则
核心就是 forward 链里的几条规则。下面是和 IoT 相关的部分,NAT 和其他规则没变,省略:
define LAN = "enp1s0"
define IOT = "enp1s0.20"
define WAN = "enp2s0"
table inet filter {
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
ct state invalid drop
# 主网出去随意,也可以主动访问 IoT
iifname $LAN accept
# IoT 只放行到 AdGuard Home 的 DNS
iifname $IOT ip daddr 192.168.10.53 meta l4proto { tcp, udp } th dport 53 accept
# IoT 访问任何私有地址一律丢弃
iifname $IOT ip daddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } drop
# 剩下的只能出公网
iifname $IOT oifname $WAN accept
}
}
规则从上往下匹配,established,related 那一条保证了主网发起的连接,IoT 设备能正常回包。input 链里也要限制 IoT 只能访问路由器的 DHCP(67/udp)和下面要用到的 mDNS(5353/udp),别让它们连得上路由器的 SSH。
Proxmox:网桥开启 VLAN 感知
Proxmox 那一侧,把 vmbr0 改成 VLAN 感知的网桥。以后哪台虚拟机或容器需要待在 IoT 网段,给网卡加个 tag 就行,不用再多插一根网线:
auto vmbr0
iface vmbr0 inet static
address 192.168.10.5/24
gateway 192.168.10.1
bridge-ports enp2s0
bridge-stp off
bridge-fd 0
bridge-vlan-aware yes
bridge-vids 2-4094
改完执行 ifreload -a。宿主机自己的地址还在不带标签的主网里,现有的虚拟机不受影响。需要放进 IoT 网段的,用 qm set <vmid> --net0 virtio,bridge=vmbr0,tag=20 就行;注意这样会重新生成 MAC 地址,想保留的话把原来的 MAC 写进参数里(virtio=原MAC,bridge=vmbr0,tag=20)。我打算以后把跑智能家居中控的那台也放进来。
踩过的坑
1. 把自己锁在了外面。 我先在交换机上改端口,一不小心把自己电脑那个口的 PVID 改成了 20,瞬间连不上交换机的管理页面,最后只能按复位键重来。教训:先配路由器,再配交换机,而且永远最后才动自己正在用的那个口。
2. 新 SSID 的兼容性。 给 IoT 新建的无线网络只开 2.4GHz,加密用 WPA2,名字用纯英文。不少 IoT 设备不支持 5GHz 和 WPA3,开着混合模式时有的设备连都连不上。
3. 所有设备都要重新配网。 智能插座、扫地机得在各自的 App 里重新配一遍网络,花了我一整个周末。
4. 投屏和设备发现失效了。 手机在主网,电视在 IoT 网段,投屏时找不到电视。原因是这类发现机制依赖 mDNS 组播,而组播不能跨网段。在路由器上开启 Avahi 的反射功能可以解决:
# /etc/avahi/avahi-daemon.conf
[server]
allow-interfaces=enp1s0,enp1s0.20
[reflector]
enable-reflector=yes
注意这只能解决 mDNS,依赖 SSDP 的 DLNA 投屏它管不了。我家主要用的那种投屏刚好走 mDNS,算是运气好。
5. 写死了 DNS 的设备。 有个设备不用 DHCP 下发的 DNS,而是写死了公共 DNS。在路由器上加一条 DNAT,把 IoT 网段发往任何地址 53 端口的请求都转到 AdGuard Home,就能管住它。
小结
划完 VLAN 之后,AdGuard Home 日志里的 IoT 设备都变成了 192.168.20.x,一眼就能和自己的设备区分开,还可以给整个网段单独设一套更严格的过滤规则。家里的网络第一次有了“内外之分”,花一个周末,值。