和鸟1.6.8,我成功宣布了一个 IPv4/24
子网,我主要使用以下示例 IP 地址/子网:RFC 5735:
root@tunnel0:~# birdc show proto all vultr
BIRD 1.6.8 ready.
name proto table state since info
vultr BGP master up 2024-01-18 Established
我现在想在家里的本地服务器上使用其中203.0.113.32/27
的一部分,使用 WireGuard203.0.113.0/24
- 本地服务器是多个 KVM 客户机的主机(
libvirt
在 Debian 12 Bookworm 上)。 - 主机本身需要一个公共 IPv4 地址,客户机也一样。
- 如果可能的话,我想使用基于策略的路由而不是 NAT。
- 我确实使用了
nftables
,而不是,iptables
因为后者越来越不被重视。
我能够在两台服务器之间设置 WireGuard 连接,但我不知道如何配置公共 IPv4 子网,以便我可以使用它而不是私有子网:
- WireGuard 服务器:
[Interface] Address = 10.17.0.1/24 SaveConfig = true ListenPort = 51820 PrivateKey = ... [Peer] PublicKey = ... AllowedIPs = 10.17.0.0/24
- WireGuard 客户端:(=虚拟机的本地服务器/主机)
# Client [Interface] PrivateKey = ... Address = 10.17.0.128/24 # Server [Peer] PublicKey = ... AllowedIPs = 10.17.0.0/24 PersistentKeepalive = 25 Endpoint = 198.51.100.77:51820
- 鸟:
router id 198.51.100.77; protocol bgp vultr { # substitute with your AS or Vultr's private AS local as 123456; source address 198.51.100.77; import none; export all; graceful restart on; multihop 2; neighbor 169.254.169.254 as 64515; password "..."; } protocol static { route 203.0.113.0/24 via 198.51.100.77; } protocol device { scan time 5; }
答案1
在两台主机上,通过
Table = 2
在两个配置中设置 eg 来禁用 wg-quick 的自动策略路由。(我甚至会Table = off
禁用它的自动路由添加,并手动完成所有操作,但我认为在你的情况下保留它是可以的;它以后会很有用。)[Interface] Table = 2
当 WireGuard 隧道重新启动时,查看
ip rule
并ip route show table 2
查看路由策略。我期望不非默认规则(仅下面显示的三个)和表 2 中的单个“dev wg0”路由。Both# ip -4 rule 0: from all lookup local 32766: from all lookup main 32767: from all lookup default
Both# ip -4 route show table 2 10.17.0.0/24 dev wg0
更新隧道的 AllowedIPs 以匹配将通过它的流量。
在服务器端,dst=203.0.113.32/27 的数据包会发送给客户端,而 src=203.0.113.32/27 的数据包会从客户端接收,因此客户端的服务器端 [Peer] 条目必须在客户端的 AllowedIPs 中包含 203.0.113.32/27。
在客户端,来自任何src(即整个互联网)将从隧道接收数据包,并且数据包到任何dst 将被发送回隧道,因此客户端配置需要 AllowedIPs 允许 0.0.0.0/0 即所有内容。
我只需在两侧将 AllowedIPs 设置为 0.0.0.0/0,这样 WireGuard 就成为一个完全标准的隧道,可以承载任何它所说的 IP 流量。
[Peer] AllowedIPs = 0.0.0.0/0
重新启动隧道;用来
wg show
确保更改生效。Both# wg show peer: asdf allowed ips: 0.0.0.0/0
还可以再次使用
ip route show table 2
,检查 wg-quick 将 AllowedIPs 转换为系统路由时受到怎样的影响。Both# ip r sh table 2 default dev wg0
在服务器上,实际上通过 WireGuard wg0 接口路由地址。
# ip route add 203.0.113.32/27 dev wg0
坚持下去:
[Interface] PostUp = ip route add 203.0.113.32/27 dev wg0
因为它是三层隧道接口,所以不需要指定网关
via
;所有路由都可以是公正的dev wg0
(虽然添加via 10.17.0.128
是无害的,但也无用)。WireGuard 使用 AllowedIPs 来决定将数据包转发到哪个对等点;像 GRE 这样的纯点对点隧道只会将所有内容发送到“另一端”。但是,如果它是像 L2TP 或 ZeroTier 这样的二层隧道,则必须指定via 10.17.0.128
网关。在这种情况下,wg-quick 已经将路线添加到表 2,因此我们可以刚刚通过添加一个来
ip rule
代替上面的路线——事实上,这将是必要的在客户端 – 但在服务器上,为了便于理解,从一条简单的路线开始。从技术上讲,您可以使用 Bird 的“协议静态”来使路由永久化,而不是使用 wg-quick 的“PostUp”,但目前我建议使用后者,因为使用 Bird 时,您需要添加过滤器以防止通过 BGP 发送 /27 路由。(您的对等方无论如何都不会接受 /27 路由,但最好不要通告除 /24 聚合路由之外的任何内容。)
使用 tcpdump 验证数据包是否发往客户端。
ping 203.0.113.32
在 Vultr 服务器上运行。- 在 Vultr 服务器上运行
tcpdump -n -i wg0
,确保这些数据包通过隧道出去(即路由表正常) - 在 WG 客户端上运行相同的程序
tcpdump -n -i wg0
,以确保数据包到达在隧道的另一端(即 AllowedIPs 正常)。 - 此时还不会有任何 ping 回复 - 没关系;先让它在一个方向上工作。
还要验证数据包来自网络到达您的 VPS 并被转发给客户端。
- 在某些计算机上运行
ping 203.0.113.32
(-t
如果是 Windows,则使用选项)不是家庭服务器和 VPS。(如果您没有其他位置可以 ping,请使用公共“Looking Glass”站点 - 许多大型运营商允许您从他们的网络 ping/跟踪路由。) - 在Vultr服务器上运行
tcpdump -n -i eth0 "host 203.0.113.32"
,查看数据包是否到达服务器(即BGP宣告OK)。 - 在 Vultr 服务器上运行
tcpdump -n -i wg0
以确保这些数据包通过隧道转发出去。如果没有,请检查 iptables 规则。 - 再次在客户端上运行它以确保数据包到达。
在客户端上,您现在正在接收“发往”203.0.113.32/27 的数据包。无论出于何种目的,您都可以像客户端直接进行 BGP 公告一样处理该问题,并且就像隧道不存在一样(除了仍然需要策略路由来在两个上行链路之间进行选择)。
我假设您的 VM 主机有一个
vmbr0
所有 VM 都连接到的单桥。我无法提供任何有关 libvirt 网络的说明,因为我几乎只接触过 libvirt,但所需的只是配置 libvirt 网络 IP 地址 —— 其中不会有任何特定于 WireGuard 或 BGP 的内容。
为主机本身分配一个地址。将其作为 /32 添加到其他接口(“lo”是传统的,“dummy0”或“wg0”也可以使用),这样即使 vmbr0 由于某种原因关闭,它也能继续工作。
让我们使用 /27 的第一个地址:
Client# ip addr add 203.0.113.33/32 dev lo
(因为它是 /32,所以您甚至可以使用第 0 个地址,即
.32/32
;“保留”规则不适用于此处。同样,/32 没有广播,因此最后一个地址.63
也可用。)此时,如果您仍在
ping
运行该程序,则主机应该开始发送回复(尽管仍然通过错误的接口),因此再次使用 tcpdump 来验证是否存在 ICMP 回显请求和响应。Client# tcpdump -e -n -i any "net 203.0.113.32/27"
添加一条策略路由规则,根据源地址通过 wg0 引导流量。
Client# ip rule add from 203.0.113.32/27 lookup 2
通常情况下,您需要先将路由添加到表 2 中,但 wg-quick 已经完成了此操作(来自 AllowedIPs)。不过,您仍应验证它是否正确(如果不正确,请更正):
Client# ip -4 r ls table 2 default dev wg0
您可以使用它
ip route get
来检查内核对任何特定数据包的看法:Client# ip route get 8.8.8.8 8.8.8.8 via <LanGW> dev eth0 src <LanIP> Client# ip r get 8.8.8.8 from 203.0.113.33 8.8.8.8 dev wg0 src 203.0.113.33 table 2
有了策略规则,客户端主机上的 tcpdump 现在应该显示 Echo Reply 数据包确实通过 wg0 而不是 eth0 发送,并且现在应该
ping 203.0.113.32
收到回复了。如果没有,请在每个步骤(客户端 wg0、服务器 wg0、服务器 eth0)重复 tcpdump 以查找卡住的位置。剩下的就是将 /27 的其余部分分配给你的 VM 桥,就像它是一个普通子网一样:
Client# ip addr add 203.0.113.33/27 dev vmbr0
VM_1# ip addr add 203.0.113.34/27 dev eth0
...并编辑 libvirt 网络 XML 以使其真正持久(甚至可能使其为该网络执行 DHCP)。
其他说明:
-
使用 Bird 1.6.8,
你应该使用鸟2现在。虽然 1.6.x 系列不会停止工作,但它有停止任何进一步的开发(自四年前的 1.6.8 版本以来只进行过一次修复)。
-
protocol bgp vultr { import none; export all; }
您应该对要导出到 eBGP 对等体的内容有明确的过滤器(良好做法即使对等方已过滤从您处导入的内容)。例如:
export where net = 203.0.113.0/24;
export where net ~ [ 203.0.113.0/24 ];
export filter { if net ~ [ 203.0.113.0/24 ] then accept; reject; };
-
protocol static { route 203.0.113.0/24 via 198.51.100.77; }
route 203.0.113.0/24 unreachable
可能是首选。网关规范不会成为 eBGP 公告的一部分(eBGP 始终将自身作为下一跳进行公告)——但它将要被导出到本地内核路由,并且最好有一个“不可达”的路由,覆盖网络中没有专门路由到其他地方的所有部分。 -
protocol device { scan time 5; }
scan time
在很多版本之前就不再需要了——Bird 会立即从内核获取 Netlink 通知。有一个空块就足够了protocol device {}
。