下一站 - Ihcblog!

远方的风景与脚下的路 | 子站点:ihc.im

0%

动手向——搭建安全、快速的家庭网络

设备

之前在淘宝 3000 块买了个 R720XD,体验不错。于是在这个机器上做了个虚拟路由,以单臂路由的形式接入路由器,提供科学的路由和 DNS 服务。

安全的局域网

一旦得到内网权限,对于普通的家庭网络,我们可以通过 ARP 劫持拿到流量并可以 MITM;对于部分 HTTPS 流量可以通过 sslstrip 等工具降级至 HTTP 继而拿到明文;利用 SNI 扩展和 DNS 流量也可以轻松地拿到用户的访问记录。如果内网部署的服务、或者路由器本身存在漏洞,则影响可以进一步扩大。

这里我们不考虑物理 Hack,那基本上需要重点关注的就是守好 WIFI 这道门。

无线网络的脆弱性

很久以前研究过 Wi-Fi 密码破解,那时候光驱还是电脑标配,刻了几张 Backtrack3 的 live cd,还蛮好使的,特别是对 WEP 加密的邻居们,这个加密简直不存在。如果是 WPA,拿到握手包就可以离线破解,只要资源足够跑出来也不是什么难事。所以如果想搭建安全的无线网络,WEP 和 WPA 都是不可靠的加密方式。

进了大学以后发现还有 EAP 这种认证方式,校园网或者是 eduroam 都是走这类认证。设计上有对认证服务器证书的验证,但是不像针对域名的证书一样,可以方便准确地确认域名所有权下发证书,SSID 是不存在绝对的拥有权的,所以这块的认证只能靠一些预共享的信息,比如连接时去人肉验证证书,或者是在连接时预先导入 CA 证书或信任 Server 证书。这种预共享信息的分发往往很难:在我设备(特别是 PC、Mac 这种无法扫码的设备)还没有网络可用的情况下,可能还真的只有键盘输入方便一些。

于是往往学校或公司的这类网络会提供给你用户名和密码,在连接时你会选择忽略证书(Windows 配这个很麻烦)或者信任单个证书。可能由于这种操作太过普遍,旧版本的安卓甚至直接忽略了证书(如果你没有明确指定一个预先导入的 CA 的话),这样做的问题就是单向认证容易被钓鱼。本科时尝试过用 hostapd 搭建伪造的 EAP 热点骗取用户 token,借用实验室的卡用 hashcat 跑出来过同学的 UIS 密码,虽然跑密码花了点时间,但是是可行的,和 WPA 的问题一样,拥有足够多资源就可以足够快。

要解决信任问题,那双向认证基本是不可避免的。

配置 EAP-TLS

这里环境是 ESXi + Debian + Docker,路由器 ASUS-AX88U。

配置并启动 freeradius:

1
2
3
4
5
6
7
8
9
10
version: '2.4'
services:
radius:
image: freeradius/freeradius-server
container_name: radius
volumes:
- "./freeradius:/etc/freeradius:ro"
restart: always
ports:
- "1812:1812/udp"

同目录需要从原镜像中拷贝出一份 /etc/freeradius ,并修改部分配置。

修改 certs/ca.cnf, certs/server.cnf, certs/client.cnf 后,生成一套证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
openssl dhparam -out dh 2048
openssl req -new -out server.csr -keyout server.key -config ./server.cnf
openssl req -new -x509 -keyout ca.key -out ca.pem -days 3650 -config ./ca.cnf
touch index.txt
echo '01' > serial
openssl ca -batch -keyfile ca.key -cert ca.pem -in server.csr -key whatever -out server.crt -config ./server.cnf
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -passin pass:whatever -passout pass:whatever
openssl pkcs12 -in server.p12 -out server.pem -passin pass:whatever -passout pass:whatever
openssl verify -CAfile ca.pem server.pem
openssl x509 -inform PEM -outform DER -in ca.pem -out ca.der

openssl req -new -out client.csr -keyout client.key -config ./client.cnf
openssl ca -batch -keyfile ca.key -cert ca.pem -in client.csr -key whatever -out client.crt -config ./client.cnf
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -passin pass:whatever -passout pass:whatever

之后在路由器上修改认证方式为 EAP,Radius 服务器配好 IP、Port 和 Secret 即可。

生成配置文件

对于 Windows,我们直接导入已生成的 CA 和 client.p12 ,然后在连接时选择 CA 证书和用户证书即可。但是对于 macOS 和 iOS 用户就很麻烦了。iOS 还可以手动信任并选择证书连接, macOS 连配置的地方都没有。

研究了一下发现对于苹果的设备需要下发一个 mobileconfig 文件,这个文件需要专门的工具来制作。

根据官方的一些资料,发现专业的 Server 是有这类管理工具可以制作的;还有一个上古软件 “iPhone 配置实用工具” 可以制作,但是实测 mac 版已不兼容跑不起来,Windows 版却还能正常工作;另外还有一个开源的软件可以做这件事:https://github.com/ProfileCreator/ProfileCreator ,但实际使用体验我感觉还不如上古版本的iPhone 配置实用工具的 Windows 版。

做出这个文件之后就可以导入到设备啦,airdrop 丢过去装一下就好。连接时直接单击即可,并且由于是通过描述文件安装,“忘记网络”的按钮也消失了,手残党的福音。

描述文件 CA 证书
描述文件 CA证书

后续还有很多事情要做,比如 IP-MAC 绑定等,在此不写了。

不可信网络

对于家庭网络,有访客要连接是常有的事情;家庭内的部分智能设备也需要 Wi-Fi 密码;部分个人设备也只需要连接外网。这些设备的共同之处是不需要连接内网,只需要外网访问权限。所以我们需要提供一个只有外网连接的网络。

一个方案是在路由器上再开一个 Wi-Fi,以及 DHCP 服务。

路由器固件是 Merlin,我感觉使用体验不如 OpenWRT。以普通用户的视角,在修改某些根本不需要重启网络的设置时,路由器往往也会重启,这明显是代码写的不太行;并且只能开 2.4G、5G 两个广播,加上内置的访客 Wi-Fi 功能是四个广播。以 root 用户的视角,当我想要修改内部文件时,是只读的,对外只提供了 JFFS 可写,可控制性很差。再开一套广播和 DHCP 的方案不太可行。

另一个方案是在某台设备上插张网卡自己做一个广播出来。折腾了半天发现我的那张网卡不支持 AP 模式,方案 GG。

最后采用的方案是再插一个路由器做广播,并在路由器的启动脚本里添加 iptables -I FORWARD -d 192.168.x.x/24 -j DROP 来丢弃通往内网的流量。

为什么不直接使用主路由器的访客模式?这个模式的确挺好用的,可以直接隔离内网以及访客间的互通;但是由于我在路由器 DHCP 配置里下发的网关和 DNS 路由是虚拟机 IP,因为是个内网 IP,这时会导致访客 Wi-Fi 不通。而绝大多数智能设备是不支持手动配置网关的,所以该方案不可行。

2022-06:更新了一下该方案,见下文。

科学的外部网络

啥叫科学的外部网络大家都明白。这里也是通过前文提到的 ESXi 虚拟机做的。

VPN in Docker

【免得被查水表,本部分已删除】

写一写 docker compose 配置就跑起来啦!

踩过的坑

在处理 VPN 与 Docker 的时候经常会踩到一个有关 MTU 的坑。Docker 默认的网桥 mtu 是 1500。

对于在宿主机开启 VPN,在容器内使用的用户,会因为宿主机出口是 1500,减去 VPN 的一些头之后只剩 1400 多,所以 1500 的包就 gg 了。这时一个简单的做法就是把这个网桥 MTU 改小一些。如果使用 docker-compose,则添加类似如下配置:

1
2
3
4
5
6
7
8
9
networks:
ihciah:
driver: bridge
ipam:
config:
- subnet: 192.168.230.1/24
gateway: 192.168.230.1
driver_opts:
com.docker.network.driver.mtu: 1400

对于我这种在容器内起 VPN 容器外使用的情况更蛋疼一些:VPN 容器的出入流量都是走这个,所以怎么限制都没用。这时可以强制 TCP 握手时协商较小的 MSS,如 1300,这样即便是加上 IP 和 TCP 的头(40)也不会超那个 1400 多的值:

1
iptables -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1300

分流

不能所有的流量都走 VPN,否则排位是要输的。

针对路由,这里就用了很老的那套策略路由的方法,做了两个 ipset:一个是走国内 A 线路的(连 Google 等已经做了专线出国的),一个是直连国外 B 线路的(没有在白名单内的)。然后分别打标记并走各自的路由表。为了维护方便,做了个小脚本可以把带有语义化的注释的 ipset 列表转换为可以直接导入 ipset 的文件。

针对 DNS,起了 unbound,针对名单中的域转发一下就 ok。

这套算是黑名单机制(政治正确一点应该说屏蔽名单机制?)的路由,还是会有漏网之鱼,见到只能手动搞一下。但是好处是不会错误地把国内 IP 路由出去,之前用 CHNList 做白名单路由踩过这个坑,还是很蛋疼的:打开淘宝有时就会提示你是否跳转国外版本。

比较尴尬的是,这套东西刚配起来没两周,那个直连国外的线路就 GG 了。

网络隔离

UPDATED AT 2022-06

前面对于不可信网络的处理是再插个路由器搞。这套用了很久之后出了两个问题:

  1. 光猫的电源适配器坏了,被封在家里没得换,只能把那个次级路由的电源挪用了
  2. 想在服务器上开 VM 给小伙伴使用
  3. 顺便省点电

由于上面的问题,我在想要么就直接用一个路由器搞一搞好了。

首先是 esxi 上要创建一个额外的虚拟交换机,之后为其添加一条特殊的上行链路。之后将那个特殊链路对应的网口插到路由器的特殊网口上。之后创建一个非信任的端口组,并将其虚拟交换机配置为刚刚创建的非信任虚拟交换机。

所以现在 esxi 上的网络配置是 Trusted VM Network 和 Management Network 走 vSwitchTrusted,Untrusted VM Network 走 vSwitchUntrusted,该 switch 绑定了 vmnic3。

我这里使用的是 AX88U,有 8 个网口。vmnic3 对应的网口插入了 LAN4,而 LAN4 对应的设备是 eth1。梅林默认会将非 eth0 的口全部桥接起来,这里我们需要将这类特殊网口解除桥接并拉入另一个独立的 bridge。

1
2
3
4
5
6
7
8
9
# Move eth1, wl0.2 and wl1.2 to untrusted
brctl delif br0 eth1
brctl delif br0 wl0.2
brctl delif br0 wl1.2
brctl addbr untrusted
brctl stp untrusted on
brctl addif untrusted eth1
brctl addif untrusted wl0.2
brctl addif untrusted wl1.2

之后为 untrusted 添加 ip 和相关 iptables 规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Set network for untrusted
ip addr add 192.168.1.1/24 dev untrusted
ip link set dev untrusted up allmulticast on

# Allow INPUT
iptables -I INPUT -i untrusted -m state --state NEW -j ACCEPT
iptables -I INPUT -i untrusted -p tcp --dport 80 -j DROP
iptables -I INPUT -i untrusted -p tcp --dport 22 -j DROP

# Allow FORWARD
iptables -I FORWARD -i untrusted -j DROP
iptables -I FORWARD -i untrusted -o untrusted -j ACCEPT
iptables -I FORWARD -i untrusted -o eth0 -j ACCEPT
iptables -I FORWARD -i untrusted -o ppp0 -j ACCEPT
iptables -I FORWARD -i untrusted -o br0 -m state --state RELATED,ESTABLISHED -j ACCEPT

# MASQUERADE for untrusted intranet
iptables -t nat -I POSTROUTING -s 192.168.1.0/24 -d 192.168.1.0/24 -o untrusted -j MASQUERADE

后续只需要为该子网提供 DHCP 即可,这个直接用现成的 dnsmasq。

效果是,2.4G/5G 访客网络 2 和归属于 Untrusted VM Network 的 VM 都会被分配独立网段的 ip,并且不能主动连接可信网络;而可信网络可以主动连接非信任网络。

完成配置见链接

欢迎关注我的其它发布渠道