14
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

tproxy 構築メモ

Last updated at Posted at 2015-08-14

http proxy を使わないと外に出れない環境で、事実上 proxy 設定を入れることができない場合、transparent proxy (intercepting proxy) を設置することで解決できる。通称 tproxy。その構築メモ。tproxy とは直接は関係ないけれど、ここでは MASQUERADE の機能も同時に入れている。

raspi2にarchlinux入れる。USB NIC追加する(eth1)。eth0 が uplink とする。

とりあえず満たしたい要件は dual-stack masquerade, http tproxy の二つ。

sysctl

/etc/sysctl.d/nat.conf に以下の内容を保存する。これは ipv6 forwarding が有効になった時に、uplink から貰った経路を使い続けるため。

net.ipv6.conf.eth0.accept_ra=2

systemd-networkd

/etc/systemd/network/eth.network/eth0.networkIPForward=yes を追加する。ちなみにこれは sysctl より後に実行されて設定を上書きすることに注意。「sysctlに設定したからオッケー」とはならない。また uplink から受け取った DNS サーバを使いたいので UseDomains=yes を追加する。

eth1 に static アドレス割り当てする。マニュアルを見ながら、同様に /etc/systemd/network/eth1.network を作る。内容はこんな感じ。IPMasqueradeオプションもあるけど、iptablesで他のものを設定するついでがあるので、ここではやらなかった。

[Match]
Name=eth1

[Network]
Address=192.168.0.1/24
Address=fd00::1/64
IPForward=yes

systemd-resolved

FallbackDNS= という行を追加しておく。さもないとマニュアルにある通り組み込みのDNSサーバが参照されてしまうから。そしてなぜか組み込みのDNS サーバは google のものだったりする。uplink proxy server を名前で登録する場合には、引けない名前を引きに行って timeout するとか起こるので、明示的に無効化しておく。

dnsmasq

IPv4, IPv6 dual stack で対応してくれる。eth1 に割り当てた IP アドレスと対応をとり、同一アドレス帯になるようにすること。

dhcp-range=192.168.0.50,192.168.0.150,12h
dhcp-range=fd00::, ra-stateless

systemctl enable dnsmasq で起動登録する。

iptables

通常の MASQUERADE の設定にさらに、squid の設定を参考に TPROXY の設定を入れる。どちらも tproxy 的には dev lo port 3128 で待ち受けるようにした。

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT

ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
ip6tables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i eth1 -o eth0 -j ACCEPT

iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -A PREROUTING -d 10.0.0.0/8 -j ACCEPT
iptables -t mangle -A PREROUTING -d 172.16.0.0/12 -j ACCEPT
iptables -t mangle -A PREROUTING -d 192.168.0.0/16 -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
  --tproxy-mark 1 --on-port 3128
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j TPROXY \
  --tproxy-mark 1 --on-port 3128
iptables -t mangle -A PREROUTING -p tcp --dport 11371 -j TPROXY \
  --tproxy-mark 1 --on-port 3128

ip6tables -t mangle -N DIVERT
ip6tables -t mangle -A DIVERT -j MARK --set-mark 1
ip6tables -t mangle -A DIVERT -j ACCEPT
ip6tables -t mangle -A PREROUTING -d fec0::/10 -j ACCEPT
ip6tables -t mangle -A PREROUTING -d fc00::/7 -j ACCEPT
ip6tables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
ip6tables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
  --tproxy-mark 1 --on-port 3128
ip6tables -t mangle -A PREROUTING -p tcp --dport 443 -j TPROXY \
  --tproxy-mark 1 --on-port 3128
ip6tables -t mangle -A PREROUTING -p tcp --dport 11371 -j TPROXY \
  --tproxy-mark 1 --on-port 3128

# 起動登録
iptables-save > /etc/iptables/iptables.rules
ip6tables-save > /etc/iptables/ip6tables.rules
systemctl enable iptables
systemctl enable ip6tables

https_tproxy

無かったので作った。

$ export GOPATH=$HOME
$ go get github.com/hkwi/https_tproxy

tproxy.service

squid の設定を参考にした際のiptables以外、ip rule, ip route や ebtables のエントリを入れる。起動用のファイルは次のように整えてみた。systemctl enable tproxy で有効にする。

/etc/systemd/system/tproxy.service
[Unit]
Description=tproxy

[Service]
Type=oneshot
ExecStart=/etc/tproxy
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

ebtables は Linux kernel で、DST が自分自身でないパケットを自分自身に向かうようにする(tproxy)ために必要。逆向きが必要ないのは、MASQUERADE しているため。

/etc/tproxy
#!/bin/bash
ip -4 rule add fwmark 1 lookup 100
ip -4 route add local default dev lo table 100
ip -6 rule add fwmark 1 lookup 100
ip -6 route add local default dev lo table 100

ebtables -t broute -A BROUTING -i eth1 -p ipv6 \
  --ip6-proto tcp --ip6-dport 80 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i eth1 -p ipv4 \
  --ip-proto tcp --ip-dport 80 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i eth1 -p ipv6 \
  --ip6-proto tcp --ip6-dport 443 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i eth1 -p ipv4 \
  --ip-proto tcp --ip-dport 443 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i eth1 -p ipv6 \
  --ip6-proto tcp --ip6-dport 11371 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i eth1 -p ipv4 \
  --ip-proto tcp --ip-dport 11371 -j redirect --redirect-target DROP

GOMAXPROCS=4 /root/bin/https_tproxy -out=$HTTP_PROXY 2>&1 |& logger &

解説

本家本元の解説は Linux kernel source tree の Documentation/networking/tproxy.txt にある。ポイントは、TCP socket を通常と同じように開いて扱うことができる+Listen しているポートはダミーで、本来の宛先を socket name として取得できる、というところでしょう。

squid でできるんじゃないか…というのは、http proxy 時に cache_peer 設定で parent cache server を指定できる、ということだと思いますが、これはダメでした。parent cache server に接続できなかった場合に、自分で外部に接続しに行こうとします。それから https の tproxy は実装がありません。ということで、私は自作に切り替えました。

tproxy にもデメリットはあります。プロクシ通信路でエラーがあった場合に、ブラウザがエラーを認識できないのが原因で 1) エラーをユーザに提示できない 2) ブラウザがリトライを試行しない というものがあります。

IPv6 には masquerade なんて無い!という意見があるかもしれません。が、実際現実問題として DHCPv6 でステートフルにアドレスが割り当てられていて、かつ、Prefix delegation が無かった時にどうするかというと、masquerade せざるを得ません。

port 11371 は hkp で、gpg key server から鍵をダウンロードする際に使用されます。特に docker build の最中に key ダウンロードがある場合など、これも tproxy に入れておいたほうが簡単です。

追記:tproxy の部分は、今までに見つけた範囲では、次のプログラムでも実現できそうです。他にもありそうでしたら教えてください!

14
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?