使用 Bird 和 WireGuard 将公共 IPv4 子网隧道连接到本地服务器

使用 Bird 和 WireGuard 将公共 IPv4 子网隧道连接到本地服务器

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

  1. 在两台主机上,通过Table = 2在两个配置中设置 eg 来禁用 wg-quick 的自动策略路由。(我甚至会Table = off禁用它的自动路由添加,并手动完成所有操作,但我认为在你的情况下保留它是可以的;它以后会很有用。)

    [Interface]
    Table = 2
    

    当 WireGuard 隧道重新启动时,查看ip ruleip 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
    
  2. 更新隧道的 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
    
  3. 在服务器上,实际上通过 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 聚合路由之外的任何内容。)

  4. 使用 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 规则。
    • 再次在客户端上运行它以确保数据包到达。
  5. 在客户端上,您现在正在接收“发往”203.0.113.32/27 的数据包。无论出于何种目的,您都可以像客户端直接进行 BGP 公告一样处理该问题,并且就像隧道不存在一样(除了仍然需要策略路由来在两个上行链路之间进行选择)。

    我假设您的 VM 主机有一个vmbr0所有 VM 都连接到的单桥。

    我无法提供任何有关 libvirt 网络的说明,因为我几乎只接触过 libvirt,但所需的只是配置 libvirt 网络 IP 地址 —— 其中不会有任何特定于 WireGuard 或 BGP 的内容。

  6. 为主机本身分配一个地址。将其作为 /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"
    
  7. 添加一条策略路由规则,根据源地址通过 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
    
  8. 有了策略规则,客户端主机上的 tcpdump 现在应该显示 Echo Reply 数据包确实通过 wg0 而不是 eth0 发送,并且现在应该ping 203.0.113.32收到回复了。如果没有,请在每个步骤(客户端 wg0、服务器 wg0、服务器 eth0)重复 tcpdump 以查找卡住的位置。

  9. 剩下的就是将 /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 {}

相关内容