一部の LAN ホストのトラフィックと DNS を OpenVPN トンネル経由で転送するように Linux ルーターを設定しますか?

一部の LAN ホストのトラフィックと DNS を OpenVPN トンネル経由で転送するように Linux ルーターを設定しますか?

私は自宅のネットワークのルーターとしてセットアップした GNU/Linux ボックスを持っています。ネットワーク インターフェイスは 3 つあります...

  • ppp0は上流接続です
  • lanはローカルネットワークです
  • nordvpnはVPNトンネルです

私はファイアウォールに nftables を使用しており、キャッシュ DNS サーバー (dnsmasq) を実行しています。LAN からの DNS 要求は傍受され、代わりにルーターの DNS サーバーに送信されます。これまでのところ順調ですが...

デフォルトではNordVPNルート全てアップストリーム トラフィックは VPN トンネルを経由します。このように構成されている場合は機能しますが、これは望ましくありません。インターネットへのトラフィックのほとんどが ppp0 インターフェイスを経由し、特定の LAN ホストからのトラフィックは VPN 経由で送信されるようにしたいです。VPN を使用するホストの DNS 要求は、ローカル サーバーではなく、VPN プロバイダーの DNS サーバーに送信する必要があります。

同様の(ただし同一ではない)ことを実行する方法について説明したさまざまな記事を読みましたが、これまでのところうまくいきませんでした。少なくとも 1 つの点が間違っていることは明らかです。私の理解では、ファイアウォールでパケットをマークし、カスタム ルーティング テーブルを使用してそれらのパケットを nordvpn インターフェイス経由で送信できるはずです。これまでのところ、次のようになっています...

に次の行を追加しました/etc/iproute2/rt_tables

5     nordvpn

openvpn が独自のルートを設定するのを防ぐために、次の行を追加しました/etc/openvpn/client/nordvpn.conf

route-noexec
script-security 2
route-up /etc/openvpn/client/mkroute.sh

動作させるには、nordvpn インターフェースでリバース パス フィルタリングを無効にする必要があると思います。これは、openvpn がルーティングを設定するために使用するスクリプトです。

#!/bin/bash

TABLE="nordvpn"
MARK="0x5"

die() {
  echo "Error: $1"
  exit 1
}

[ $(id -u) -eq 0 ] || die "Not running as root"

ip route flush cache || echo "error 1"
ip route flush table "$TABLE" || echo "error 2"

ip route add 192.168.2.0/24 dev lan src 192.168.2.1 table "$TABLE" || echo "error 3"
ip route add $route_vpn_gateway dev lan src $ifconfig_local table "$TABLE" || echo "error 4"
ip route add default via $route_vpn_gateway dev "$dev" table "$TABLE" || echo "error 5"
ip rule delete fwmark "$MARK" || :
ip rule add fwmark "$MARK" lookup "$TABLE" || echo "error 6"
sysctl -w net.ipv4.conf.nordvpn.rp_filter=0 || echo "error 7"

最後に、これが私の nftables スクリプトです。

flush ruleset

define i_wan = ppp0
define i_nordvpn = nordvpn
define i_lan = lan

define p_dns = 53

define a4_nordvpn_dns_1 = 103.86.96.100
define m_nordvpn = 5

define h4_nimrod = 192.168.2.21

table ip ip4_firewall {

  set s_inet_nordvpn { type ipv4_addr; elements = { $h4_nimrod }; }

  chain c_here_to_wan { tcp flags syn tcp option maxseg size set rt mtu; accept; }
  chain c_here_to_nordvpn { tcp flags syn tcp option maxseg size set rt mtu; accept; }
  chain c_here_to_lan { accept; }
  chain c_wan_to_here { ip protocol icmp accept; drop; }
  chain c_wan_to_lan { ip protocol icmp accept; drop; }
  chain c_nordvpn_to_here { ip protocol icmp accept; drop; }
  chain c_nordvpn_to_lan { ip protocol icmp accept; drop; }
  chain c_lan_to_here { accept; }
  chain c_lan_to_wan { tcp flags syn tcp option maxseg size set rt mtu; accept; }
  chain c_lan_to_nordvpn { tcp flags syn tcp option maxseg size set rt mtu; accept; }

  chain preroute_nordvpn {
    udp dport $p_dns mark set $m_nordvpn dnat $a4_nordvpn_dns_1
    tcp dport $p_dns mark set $m_nordvpn dnat $a4_nordvpn_dns_1
    oif $i_wan mark set $m_nordvpn
  }

  chain prerouting { type nat hook prerouting priority -100
    ip saddr @s_inet_nordvpn goto preroute_nordvpn
    iif $i_lan oif $i_wan udp dport $p_dns redirect
    iif $i_lan oif $i_wan tcp dport $p_dns redirect
  }

  chain postrouting { type nat hook postrouting priority 100
    oif $i_nordvpn masquerade
    oif $i_wan snat <my public IP address>
  }

  chain input { type filter hook input priority 0; policy drop
    ct state established,related accept
    ct state invalid drop
    iif vmap {
      lo: accept,
      $i_wan : jump c_wan_to_here,
      $i_nordvpn : jump c_nordvpn_to_here,
      $i_lan : jump c_lan_to_here
    }
    log prefix "Unhandled input: " drop
  }

  chain output { type filter hook output priority 0; policy drop
    ct state established,related accept
    ct state invalid drop
    oif vmap {
      lo: accept,
      $i_wan : jump c_here_to_wan,
      $i_nordvpn : jump c_here_to_nordvpn,
      $i_lan : jump c_here_to_lan
    }
    log prefix "Unhandled output: " drop
  }

  chain forward { type filter hook forward priority 0; policy drop
    ct state established,related accept
    ct state invalid drop
    iif . oif vmap {
      $i_lan . $i_wan : jump c_lan_to_wan,
      $i_wan . $i_lan : jump c_wan_to_lan,
      $i_lan . $i_nordvpn : jump c_lan_to_nordvpn,
      $i_nordvpn . $i_lan : jump c_nordvpn_to_lan,
    }
    log prefix "Unhandled forward: " drop
  }
}

設定すると次のようになります。

[root@cerberus ~]# ip rule ls
0:      from all lookup local
32765:  from all fwmark 0x65 lookup nordvpn
32766:  from all lookup main
32767:  from all lookup default
[root@cerberus ~]# ip route ls
default dev ppp0 scope link 
10.0.0.1 dev ppp0 proto kernel scope link src <my public IP address> 
10.8.0.0/24 dev nordvpn proto kernel scope link src 10.8.0.12 
192.168.2.0/24 dev lan proto kernel scope link src 192.168.2.1 
[root@cerberus ~]# ip route ls table nordvpn
default via 10.8.0.1 dev nordvpn 
10.8.0.1 dev lan scope link src 10.8.0.12 
192.168.2.0/24 dev lan scope link src 192.168.2.1 
[root@cerberus ~]# ip rule ls
0:      from all lookup local
32764:  from all fwmark 0x5 lookup nordvpn
32766:  from all lookup main
32767:  from all lookup default

有効にすると、VPN に割り当てられたクライアントの DNS ルックアップが失敗します。リクエストは VPN トンネル経由で送信されるようですが、応答は転送チェーンではなく入力チェーンに到着し、クライアントに届きません。どこが間違っているのでしょうか?

ご協力いただければ幸いです:)

答え1

変更してみてください:

  chain preroute_nordvpn {
    udp dport $p_dns mark set $m_nordvpn dnat $a4_nordvpn_dns_1
    tcp dport $p_dns mark set $m_nordvpn dnat $a4_nordvpn_dns_1
    oif $i_wan mark set $m_nordvpn
  }

  chain prerouting { type nat hook prerouting priority -100
    ip saddr @s_inet_nordvpn goto preroute_nordvpn
    iif $i_lan oif $i_wan udp dport $p_dns redirect
    iif $i_lan oif $i_wan tcp dport $p_dns redirect
  }

に:

  chain fwmark { type filter hook prerouting priority -150
    ip saddr @s_inet_nordvpn mark set $m_nordvpn
  }

  chain lan_dns {
    ip saddr @s_inet_nordvpn dnat $a4_nordvpn_dns_1
    redirect
  }

  chain lan_dnat {
    udp dport $p_dns goto lan_dns
    tcp dport $p_dns goto lan_dns
  }

  chain prerouting { type nat hook prerouting priority -100
    iif $i_lan goto lan_dnat
  }

関連情報