ぶていのログでぶログ

思い出したが吉日

sshuttleでDNSリクエストを転送する

VPNからsshuttleに切り替えてから、不便だなぁと思っていたのはDNSリクエストを転送できないことだった。 VPNの接続先のDNSサーバに接続して、リモート先の内部で利用しているドメインを正引きするのに使っていた。 sshuttleはSSHのポートフォワードを元にしたツールなので、UDPなDNSリクエストを転送できないと思いこんでいた。 が、なんとDNSリクエストを転送できることに気がついた!

  --dns                 capture local DNS requests and forward to the remote DNS server
  --ns-hosts IP[,IP]    capture and forward DNS requests made to the following servers
  --to-ns IP[:PORT]     the DNS server to forward requests to; defaults to servers in /etc/resolv.conf on remote side if not given.

--dnsオプション

--dns を指定していると、ローカル環境のDNSリクエストをリモートのDNSサーバに転送するようになる。 具体的な動作としてはresolv.confやresolvctl statusなどを見て、それらに設定されているDNSサーバへのリクエストをiptablesを使ってshuttleのトンネルにリダイレクトしているようだ。

❯ sudo iptables -nvL -t nat
-- snip --
Chain sshuttle-12300 (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            TTL match TTL == 63
    0     0 REDIRECT   udp  --  *      *       0.0.0.0/0            127.0.0.53           udp dpt:53 redir ports 12299  👈 私の環境に設定されているDNSサーバの設定がリダイレクトされている
    0     0 REDIRECT   udp  --  *      *       0.0.0.0/0            127.0.0.54           udp dpt:53 redir ports 12299  👈
    0     0 REDIRECT   udp  --  *      *       0.0.0.0/0            192.168.0.1          udp dpt:53 redir ports 12299  👈
-- snip --
❯ sudo ss -unlp
State                 Recv-Q                Send-Q                                                  Local Address:Port                                Peer Address:Port               Process
UNCONN                0                     0                                                           127.0.0.1:12299                                    0.0.0.0:*                   users:(("sshuttle",pid=938219,fd=8))  👈 udp:12299はshuttleが使っている
-- snip --

☆リモートサーバを見る
$ ss -unp
Recv-Q     Send-Q          Local Address:Port            Peer Address:Port     Process
0          0                   127.0.0.1:41739             127.0.0.53:53        users:(("python3",pid=32560,fd=11))  👈 sshuttleがリモートのpython3を起動してポートフォワードしている
-- snip --
$ ps auxwwff | grep 32560
user     32560  0.1  0.1  18920 12252 ?        Ss   23:53   0:00          \_ python3 -c import sys, os; verbosity=0; sys.stdin = os.fdopen(0, "rb"); exec(compile(sys.stdin.read(1486), "assembler.py", "exec"))

すべてのDNSリクエストを転送してしまうので、特定ドメインのリクエストのみ転送したいという時はこちらは使えない。

--ns-hosts オプション

--ns-hosts オプションを使うとこのオプションで指定したIPへのDNSリクエストのみ転送することができる。 例えば 192.0.2.1192.0.2.2 へのリクエストを転送したい場合は --ns-hosts 192.0.2.1,192.0.2.2 みたいなオプションになる。 このオプションをつけてsshuttleを起動すると以下のようなiptablesルールが追加される。

Chain sshuttle-12299 (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            TTL match TTL == 63
    0     0 REDIRECT   udp  --  *      *       0.0.0.0/0            192.0.2.1             udp dpt:53 redir ports 12298 👈 追加されたルール
    0     0 REDIRECT   udp  --  *      *       0.0.0.0/0            192.0.2.2             udp dpt:53 redir ports 12298 👈
-- snip --

リモートの状態等々は --dns オプションのときと同じ。 dnsmasqなどと組み合わせて特定のドメインのみ、転送先のDNSサーバのIPにする。 例えば、dnsmasqでexample.comのリクエストのみ192.0.2.1 / 192.0.2.2 にするには以下のような設定になる。

$ cat /etc/dnsmasq.conf
server=/example.com/192.0.2.1
server=/example.com/192.0.2.2

--to-ns オプション

デフォルトでは転送先のDNSサーバは、リモートサーバの/etc/resolv.confに従うのだが、--to-ns オプションを使うと変更できる。 っとヘルプに書いてあるのだが未検証。

おわりに

sshuttleに --dns / --ns-hostsを指定することでDNSリクエストを転送することができる。 転送先のDNSサーバはIPだけではなくPORTも指定できるようなので、リモート先のConsul DNSを参照するということができると思う。 sshuttleを使えばDNSリクエストの転送もできるので、L2で通信する必要がないのであればVPNよりお手軽なSSHトンネルでいいのではと言う気がしている。便利だ。