やったこと
ドメイン名によって経路(インタフェース)を選択的にすることができるのかどうか試してみた。家の環境で、通常のインターネットにつなぐ経路とVPNのトンネル経由の経路が利用できるときに、特定のドメイン名の場合には希望するインタフェースからパケットを流す、ということが目標。webで調べて試してみて、1)ipset+dnsmasq 2)iptablesを使ったパケットの印付け 3) ip ruleの設定を組み合わせることでできた。
環境
- raspberry pi3(B+, "Raspbian GNU/Linux 10 (buster)")
有線/無線ともにstaticでアドレス振ってる
無線はアクセスポイントとして設定してる
無線側PCには、dnsmasqがdhcp機能でPCにアドレス供給し、DNS問い合わせにも応答する
pi3ではインターネット向けに、通常のhome-GWを抜けるインタフェースeth0と、OpenVPN,OpenConnectでそれぞれインタフェースtun0,tun1のトンネルでhome-GWを通る経路を設定している
+-----+ +--------------+ +-------+ | PC |/////|pi3(vpnclient)|---|home-GW|====(internet) +-----+ +------------- + +-------+ wifi区間 イーサ
step1 : ipsetの仕掛け
dnsによる名前解決が走った時に、そのdnsが設定した条件にマッチしていたら、ipsetのメンバーに追加されるようにする。dnsmasqを使うことで、実際に解決に走ったドメイン名に対しipsetできるとのこと。
先にipsetを作成することが必要で、これはiptablesで参照されるSETNAMEのついた入れ物といえる。
$ sudo ipset create GOOGLE hash:ip
/etc/dnsmasq.confに
ipset=/*.google.com/google.com/GOOGLE
の一行*1追加して、dnsmasqを再起動する。ping dns.google.comとかping map.google.comとかするとsudo ipset list GOOGLEでみたときMembers:にIPアドレスが追加されていくのがわかる。*2
step 2 : iptablesの仕掛け
iptablesのmangle tableを使うと、パケットに特殊な印付けをできるとのこと。パケットがカーネルの中を通過している間だけ有効な印らしい。ここで付けた値201は後のルーティングテーブル番号に合わせただけで、この数字にする必然はない。
sudo iptables -A PREROUTING -t mangle -j MARK -m set --match-set GOOGLE dst --set-mark 201 sudo iptables -A OUTPUT -t mangle -j MARK -m set --match-set GOOGLE dst --set-mark 201
1行目は、フォワードパケット用。2行目はpi3上から発信する場合用。
step 3 : ip ruleの設定
Linuxのルーティングテーブルは、テーブル自体を複数持たせることができるとのこと。今まで知らなかったけれども、経路テーブルを切り替える潜在的な能力がカーネル2.4以来備わっているとのこと。
VPNを通したいときのルーティングテーブルを追加してあげて、MARKが付いているパケットの場合はそのルーティングテーブルを参照するということができれば、目的を達成できる。
今回vpn_tableという名前で追加してみる。/etc/iproute2/rt_tablesを編集
# # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 201 vpn_table
vpn_tableに経路を追加してあげる。このテーブルを使う場合はすべて同じinterface tun1にしたいので
sudo ip route add default dev tun1 table vpn_table
とすることになる。
最後、
sudo ip rule add fwmark 201 lookup vpn_table
というコマンドで、MARK=201のパケットの場合vpn_tableテーブルを参照する、ということが実現できることになる。
PCからtracert dns.google.comすると、ip rule add前後で経路が変わってくれた。
参考にさせていただいたもの
networking - Linux: routing based on domain names - Super User
やや長くいろいろ書かれていて全体通して手法を理解するのに参考になった。
iptables - Forwarding packets from loopback interface with policy-based routing - Unix & Linux Stack Exchange
pi3発信のケースでMARKされずにいたときに、これで解決できた。
GNU/Linux host name resolution - /dev/posts/
名前解決周辺のデーモン・設定を俯瞰して見ることができて大変参考になった。