今回の記事ではHTTPプロキシの定番であるSquidで透過プロキシを設定してみます。以前の関連記事とあわせて読むことをおすすめします。
概要
Squidの透過プロキシにはinterceptとtproxyの2種類があります。
この2つを実際にどう使い分けるかは正直よく分かっていないのですが、動作上の違いとしては、前者がsquid自身のIPを使ってインターネット側と通信するのに対し、後者はクライアントのIPをそのまま使ってインターネット側と通信します。また、iptablesの設定上の違いとしては前者にはREDIRECTターゲット、後者にはTPROXYターゲットを使います。
squid.conf
squid.confの中身は以下のようにします。
http_port 3128
http_port 4128 intercept
http_port 5128 tproxy
acl ipoe localport 3128
acl pppoe myportname 4128
tcp_outgoing_address 192.168.1.12 ipoe
tcp_outgoing_address 192.168.1.13 pppoe
↑これだけを書いても動くと思いますが、それだとセキュリティがなさすぎるので、基本的にはデフォルトのsquid.confのhttp_port 3128と書いてあるあたりに↑をコピペするようなイメージになると思います。3128は普通のHTTPプロキシなので今回の記事とは関係ありませんが、普通のHTTPプロキシが最低1個は設定されていないとエラーになります。またデフォルトのsquid.confではhttp_access allow localnetがコメントアウトされているかもしれないので適宜有効化しましょう。
tcp_outgoing_addressは記事の本題とは関係ありませんが、せっかくなので入れておきました(以前の記事でも扱っています)。この例では3128番(普通のHTTPプロキシ)に関する通信は192.168.1.12、4128番(intercept)に関する通信は192.168.1.13から出ていくように設定しています。tproxyはそもそもクライアントのIPをそのまま使う動作なのでtcp_outgoing_addressは使えません。
上記で、3128には「localport」、4128には「myportname」を使っています。これは「localport」が「クライアント側が接続しに行くポート」を指すからです。つまり、通常の(明示的な)HTTPプロキシではlocalportは(上記の例でいえば)3128番ですが、interceptによる透過プロキシでは(クライアント側はプロキシの存在を意識しないため)80番や443番がそれにあたるということになります。従って、interceptに対してtcp_outgoing_addressを指定したいならかわりに「myportname」を使う必要があります(参考)。ちなみに、通常プロキシのほうでmyportnameを使う(例: acl ipoe myportname 3128)のは問題なく動きます。以前は「localport」が「myport」という名前でしたが、こちらは非推奨となりました(使うとcache.logに警告が出ます)。
また、以前(squid v2くらい?)は「intercept」のかわりに「transparent」という名前だったようです(現在も動くし警告も出ないっぽい)。
interceptの設定
interceptの方が簡単なのでまずはそちらを扱います。
この記事では、ローカルマシンのトラフィックを転送するのと他のマシンのトラフィックを転送するのと両方を書いておきます。ローカルマシンのIPとしてはlo(ループバックデバイス)に192.168.5.5を割り当て(以前の関連記事と同様)、他のマシンとしては192.168.1.90を使うことにします。
両方ともiptablesの設定はたった一行だけです。
ローカルマシンの場合はOUTPUTチェーンを使います。
sudo iptables -t nat -A OUTPUT -s 192.168.5.5 -p tcp --dport 80 -j REDIRECT --to-ports 4128
他のマシンの場合はPREROUTINGチェーンです。
sudo iptables -t nat -A PREROUTING -s 192.168.1.90 -p tcp --dport 80 -j REDIRECT --to-ports 4128
443番も同様に設定しましょう。ただしSSLの復号のためssl_bumpや証明書に関する設定が必要になるはずです。この部分についてはちゃんと理解していないので他サイトを参考にしてください。
REDIRECTではポート番号は指定できますがIPは指定できず、暗黙にlocalhostになるようです。多分。
interceptの動作例(tcpdump)
正常動作時のinterceptモードの一連のHTTPリクエストをtcpdumpした結果を載せます。通信先(104.16.185.241)はipv4.icanhazip.comです。
まずローカルのトラフィックです。
12:59:10.913373 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [S], seq 329971803, win 64240, options [mss 1460,sackOK,TS val 3271536526 ecr 0,nop,wscale 7], length 0
12:59:10.913413 lo In IP 104.16.185.241.80 > 192.168.5.5.53472: Flags [S.], seq 2100907541, ack 329971804, win 65483, options [mss 65495,sackOK,TS val 1048421380 ecr 3271536526,nop,wscale 7], length 0
12:59:10.913450 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [.], ack 2100907542, win 502, options [nop,nop,TS val 3271536526 ecr 1048421380], length 0
12:59:10.913567 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [P.], seq 0:81, ack 1, win 502, options [nop,nop,TS val 3271536526 ecr 1048421380], length 81
12:59:10.913593 lo In IP 104.16.185.241.80 > 192.168.5.5.53472: Flags [.], ack 82, win 511, options [nop,nop,TS val 1048421380 ecr 3271536526], length 0
12:59:10.914135 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [S], seq 318378563, win 64240, options [mss 1460,sackOK,TS val 3125673431 ecr 0,nop,wscale 7], length 0
12:59:10.925618 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58402: Flags [S.], seq 179350535, ack 318378564, win 65535, options [mss 1400,sackOK,TS val 2604158511 ecr 3125673431,nop,wscale 13], length 0
12:59:10.925722 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3125673443 ecr 2604158511], length 0
12:59:10.926005 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [P.], seq 1:250, ack 1, win 502, options [nop,nop,TS val 3125673443 ecr 2604158511], length 249: HTTP: GET / HTTP/1.1
12:59:10.937750 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58402: Flags [.], ack 250, win 9, options [nop,nop,TS val 2604158526 ecr 3125673443], length 0
12:59:10.944044 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58402: Flags [P.], seq 1:559, ack 250, win 9, options [nop,nop,TS val 2604158533 ecr 3125673443], length 558: HTTP: HTTP/1.1 200 OK
12:59:10.944122 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [.], ack 559, win 498, options [nop,nop,TS val 3125673461 ecr 2604158533], length 0
12:59:10.944511 lo In IP 104.16.185.241.80 > 192.168.5.5.53472: Flags [P.], seq 1:625, ack 82, win 512, options [nop,nop,TS val 1048421411 ecr 3271536526], length 624: HTTP: HTTP/1.1 200 OK
12:59:10.944552 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [.], ack 625, win 498, options [nop,nop,TS val 3271536557 ecr 1048421411], length 0
12:59:10.944611 lo In IP 104.16.185.241.80 > 192.168.5.5.53472: Flags [P.], seq 625:640, ack 82, win 512, options [nop,nop,TS val 1048421411 ecr 3271536557], length 15: HTTP
12:59:10.944632 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [.], ack 640, win 498, options [nop,nop,TS val 3271536557 ecr 1048421411], length 0
12:59:10.944818 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [F.], seq 81, ack 640, win 498, options [nop,nop,TS val 3271536557 ecr 1048421411], length 0
12:59:10.944987 lo In IP 104.16.185.241.80 > 192.168.5.5.53472: Flags [F.], seq 640, ack 83, win 512, options [nop,nop,TS val 1048421411 ecr 3271536557], length 0
12:59:10.945034 lo In IP 192.168.5.5.53472 > 127.0.0.1.4128: Flags [.], ack 641, win 498, options [nop,nop,TS val 3271536557 ecr 1048421411], length 0
13:00:10.354878 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [F.], seq 250, ack 559, win 498, options [nop,nop,TS val 3125732872 ecr 2604158533], length 0
13:00:10.363926 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58402: Flags [F.], seq 559, ack 251, win 9, options [nop,nop,TS val 2604217952 ecr 3125732872], length 0
13:00:10.364094 enp1s0 Out IP 192.168.1.13.58402 > 104.16.185.241.80: Flags [.], ack 560, win 498, options [nop,nop,TS val 3125732881 ecr 2604217952], length 0
このように、まずクライアントとsquidの間でTCP接続を確立していますが、クライアント側からはsquidではなく本来の通信先と通信しているように見えています(tcpdump上では127.0.0.1:4128が宛先側としてしか現れず非対称に見えるのが面白いところ)(loに関してはInのパケットしか表示されないのが関係している?)。その上でsquidが自分で192.168.1.13から出て行ってインターネット側と通信をしています。クライアント側↔squidのTCP接続は即座に切れますが、squid↔インターネット側のTCP接続は1分間生きているようです。
次に他デバイスからの通信です。本質的に上と全く変わりませんが、こちらでは127.0.0.1:4128が現れず本来のインターネット側IPになっています。なぜでしょうね…。
20:35:43.667221 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [S], seq 2065896686, win 65535, options [mss 1460,sackOK,TS val 1050483665 ecr 0,nop,wscale 8], length 0
20:35:43.667369 enp1s0 Out IP 104.16.185.241.80 > 192.168.1.90.32978: Flags [S.], seq 2113036274, ack 2065896687, win 65160, options [mss 1460,sackOK,TS val 2011461503 ecr 1050483665,nop,wscale 7], length 0
20:35:43.670019 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [.], ack 1, win 256, options [nop,nop,TS val 1050483669 ecr 2011461503], length 0
20:35:43.670339 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [P.], seq 1:83, ack 1, win 256, options [nop,nop,TS val 1050483670 ecr 2011461503], length 82: HTTP: GET / HTTP/1.1
20:35:43.670408 enp1s0 Out IP 104.16.185.241.80 > 192.168.1.90.32978: Flags [.], ack 83, win 509, options [nop,nop,TS val 2011461506 ecr 1050483670], length 0
20:35:43.671067 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [S], seq 1701555618, win 64240, options [mss 1460,sackOK,TS val 3153066188 ecr 0,nop,wscale 7], length 0
20:35:43.678919 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58124: Flags [S.], seq 3076138744, ack 1701555619, win 65535, options [mss 1400,sackOK,TS val 1804533114 ecr 3153066188,nop,wscale 13], length 0
20:35:43.679026 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3153066196 ecr 1804533114], length 0
20:35:43.679303 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [P.], seq 1:202, ack 1, win 502, options [nop,nop,TS val 3153066197 ecr 1804533114], length 201: HTTP: GET / HTTP/1.1
20:35:43.688668 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58124: Flags [.], ack 202, win 9, options [nop,nop,TS val 1804533124 ecr 3153066197], length 0
20:35:43.703627 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58124: Flags [P.], seq 1:559, ack 202, win 9, options [nop,nop,TS val 1804533137 ecr 3153066197], length 558: HTTP: HTTP/1.1 200 OK
20:35:43.703719 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [.], ack 559, win 498, options [nop,nop,TS val 3153066221 ecr 1804533137], length 0
20:35:43.703991 enp1s0 Out IP 104.16.185.241.80 > 192.168.1.90.32978: Flags [P.], seq 1:618, ack 83, win 509, options [nop,nop,TS val 2011461539 ecr 1050483670], length 617: HTTP: HTTP/1.1 200 OK
20:35:43.704039 enp1s0 Out IP 104.16.185.241.80 > 192.168.1.90.32978: Flags [P.], seq 618:633, ack 83, win 509, options [nop,nop,TS val 2011461539 ecr 1050483670], length 15: HTTP
20:35:43.709210 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [.], ack 618, win 261, options [nop,nop,TS val 1050483707 ecr 2011461539], length 0
20:35:43.709211 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [.], ack 633, win 261, options [nop,nop,TS val 1050483707 ecr 2011461539], length 0
20:35:43.709295 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [F.], seq 83, ack 633, win 261, options [nop,nop,TS val 1050483708 ecr 2011461539], length 0
20:35:43.709471 enp1s0 Out IP 104.16.185.241.80 > 192.168.1.90.32978: Flags [F.], seq 633, ack 84, win 509, options [nop,nop,TS val 2011461545 ecr 1050483708], length 0
20:35:43.712018 enp1s0 In IP 192.168.1.90.32978 > 104.16.185.241.80: Flags [.], ack 634, win 261, options [nop,nop,TS val 1050483712 ecr 2011461545], length 0
20:36:43.819018 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [F.], seq 1701555820, ack 3076139303, win 498, options [nop,nop,TS val 3153126336 ecr 1804533137], length 0
20:36:43.829303 enp1s0 In IP 104.16.185.241.80 > 192.168.1.13.58124: Flags [F.], seq 1, ack 1, win 9, options [nop,nop,TS val 1804593263 ecr 3153126336], length 0
20:36:43.829474 enp1s0 Out IP 192.168.1.13.58124 > 104.16.185.241.80: Flags [.], ack 2, win 498, options [nop,nop,TS val 3153126347 ecr 1804593263], length 0
tproxyの設定
tproxyの設定はかなりめんどくさいというかわかりづらいです。
まずは以前の関連記事でも必要だった、loへのルーティング設定をします。つまりrt_tablesに番号100のテーブルを追加した上で
sudo ip route add local default dev lo table 100
sudo ip rule add fwmark 1 lookup 100
をしておきます。これで、マークが1のパケットが全てloにルーティングされるようになります。
次に、インターネット側からの戻りパケットの処理に使うDIVERTチェインを用意します(markの番号はさっきの1と合わせています)。
sudo iptables -t mangle -N DIVERT
sudo iptables -t mangle -A DIVERT -j MARK --set-mark 1
sudo iptables -t mangle -A DIVERT -j ACCEPT
このDIVERTチェインを使って以下のsocketマッチのルールを設定します。意味はあとで説明します。
sudo iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
次に、まずは他のマシン(192.168.1.90)のトラフィックに関して、以下のTPROXYとMASQUERADE(あるいはSNAT)のルールを設定します。インターフェイス名(eth0)は適宜変えてください。なお、ここではHTTP80だけにMASQUERADEを設定していますが、ルーターとして使うならば全プロトコル対象にするのが普通だと思います。
sudo iptables -t mangle -A PREROUTING -s 192.168.1.90 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 5128
sudo iptables -t nat -A POSTROUTING -s 192.168.1.90 -o eth0 -p tcp --dport 80 -j MASQUERADE
あと、MASQUERADEのためにIP転送を有効化します。
sudo sysctl -w net.ipv4.ip_forward=1
さらに以前の関連記事にも書いた重要な注意として、ufwを使っている場合はloの透過プロキシに向かっていくパケットがufw-not-localというチェインに引っかかってDROPされてしまうので、sudo iptables -I ufw-not-local -s 192.168.1.90 -j ACCEPTのようなルールを適宜追加してください(通常のufw allowは効果なし)。
これで完了です。他のルールとの兼ね合いですが、0x1/0x1のところは単に1でも動くと思います(公式ドキュメントには0x1/0x1で載っていました)。公式ドキュメントにはrp_filterの設定も書いてありますが、試した限り2(自分の環境のデフォルト)、あるいは何ならそれより厳しい1にしても動きました。
この設定例に従って、1.1.1.1:80に接続した際の動作を説明すると、次のようになります。
- 192.168.1.90から1.1.1.1の80番へのSYNパケットが送信され、接続が開始される。
- 上記SYNパケットは、Squidが走っているマシンに到着する際にTPROXYルールによりfwmarkが1となり、loにルーティングされ、さらに5128番の透過プロキシのポートに向かう
- SquidがSYNパケットを処理し、送信元IPを1.1.1.1:80に偽装してSYN+ACKを送り返し、接続が確立(この時点でクライアント側からは1.1.1.1:80への接続が確立できているように見えるが実際にはインターネット側には何も送信されていない)
- クライアントがTCPストリームの送信を開始する。これもSYNと同様にloにルーティングされて透過プロキシに向かう。
- ここからは実際に1.1.1.1:80との通信が必要になるので、Squidが1.1.1.1:80に向けたSYNパケットを送信するが、この際送信元アドレスはクライアントのIP(192.168.1.90)に偽装する
- 上記のSYNパケットはMASQUERADEルールにより送信元アドレスが192.168.1.12(Squidが動いているマシン)に変更され、インターネットに出て行って1.1.1.1:80に届く
- 1.1.1.1:80から戻ってきたSYN+ACKパケットは、MASQUERADEされていたのがもとに戻って宛先アドレスは192.168.1.90となる。また、このパケットはSquidがさっきのSYNを送信するために使い始めたsocketにマッチするため、DIVERTチェインに入る
- DIVERTチェインではfwmarkが1となるのでloにルーティングされることになるが、透過プロキシの処理はされない。
- 結果としてloで処理を行っている(←多分)Squidに無事パケットが届く。
- このあとも同様に通信が行われ、結果は適宜クライアント側に伝えられる。
特に7-9のところがキモになるかと思います。透過プロキシルールが適用される192.168.1.90からのパケットをloにルーティングするのはわかりやすいところですが、1.1.1.1から戻ってくる方向のパケットもloにルーティングさせるために先ほどの"DIVERT"チェインがあったというわけです。これがないとSYN+ACKがいつまでも受け取れず、SYNを何度も送り付けるような動作になってタイムアウトします(公式ドキュメントの"Traffic going through Squid but then timing out")。
ちなみに、socketでリッスンしているパケット全てを対象にしてしまっては、Squidが稼働しているマシンの普通のTCP通信(SSH待ち受けやHTTPリクエストなど)に影響が出てしまうのでは?と思うかもしれませんが、こういったマシン自体に向かっているパケットはそもそも最終的には「ルーティング」されることなく処理されるので、問題は起こりません。「socketがリッスンしている」にもかかわらず「宛先がマシンのIPと異なる」という非常に"特殊な"TPROXYのパケットだけにうまくマッチしているということです。
さらに、https://lists.openwall.net/netdev/2024/09/24/210 で指摘されている通り、socketマッチのルールに--transparentというオプションを追加すると、非透過ソケットが無視される(?)らしく、より効率良くマッチングができそうです(つまり、基本的には付けることを推奨します)。もっと言えば、例えば192.168.1.90に関連する透過プロキシパケットだけを対象にしたいという場合には、conntrack系の--ctorigsrcを使うことができます(が、そこまでする意味があるのかはわかりません)。
例としてはこんな感じです。
sudo iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -m conntrack --ctorigsrc 192.168.1.90 -j DIVERT
iptablesに慣れていれば分かると思いますがこのsocketルールはTPROXYルールの前に処理されないと意味がないので適宜-Aではなく-Iを使うなどして順番を調整してください。
tproxyの設定(ローカルマシンのIP)
ここまで外部マシン(192.168.1.90)にtproxyを適用してきましたが、interceptの場合と同じくローカルマシン(192.168.5.5)に適用することも可能です。
前節で設定したルールの中で192.168.1.90が含まれていたのはTPROXYとMASRUERADEのルールだけだったのでその2つは192.168.5.5向けに設定し直します。他はそのままで構いません。
sudo iptables -t mangle -A PREROUTING -s 192.168.5.5 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 5128
sudo iptables -t nat -A POSTROUTING -s 192.168.5.5 -o eth0 -p tcp --dport 80 -j MASQUERADE
MASQUERADEについても、面倒であればソースを限定せずにsudo iptables -t nat -A POSTROUTING -o eth0 -p tcp --dport 80 -j MASQUERADEとしてしまってもそこまで副作用は出ないかと思います。
さらに、192.168.5.5から出ていくパケットは普通PREROUTINGチェインには引っかからないので、ループバックデバイスを通って自分自身に向かっていくように設定する必要があります(このへんも以前の関連記事と同様)。
この際重要な注意点として、192.168.5.5からTCP80番に向かって出ていくパケットを全て対象にしてしまうと、Squidが実際にインターネット側に接続しにいくときの通信まで対象になって無限ループしてしまいます。そこで、Squidの通信はproxyというユーザー名で行われることを活かし、--uid-ownerでマッチしてSquidの通信を除外します。
sudo iptables -t mangle -A OUTPUT0 -s 192.168.5.5 -p tcp --dport 80 -m owner ! --uid-owner proxy -j
MARK --set-mark 1
このようにfwmark 1を付けることでloにルーティングされることになります。ちなみにここでmarkを付けているので先ほどのTPROXYルールで--tproxy-markを付ける必要はなくなります(以前の関連記事と同様)。また実は、192.168.5.5はこのマシン自体のアドレスなので、前節の場合と異なり、DIVERT関連のルールは全く必要ありません。
先ほどの--uid-ownerの設定がうまくいかずにループが発生すると、Squidからエラー応答が返り、cache.logにloop警告が記録されます。これは特にデバッグログを有効にしたりしなくてもデフォルトの設定で記録されます。ちゃんと検出できていて偉い。
2025/03/20 21:08:06 kid1| WARNING: Forwarding loop detected for:
GET / HTTP/1.1
Host: ipv4.icanhazip.com
User-Agent: curl/8.5.0
Accept: */*
Via: 1.1 My-LAPTOP (squid/6.10)
X-Forwarded-For: 192.168.5.5
Cache-Control: max-age=259200
Connection: keep-alive
current master transaction: master61
--uid-ownerで使ったproxyというユーザー名はcache_effective_user指定で変えられるようです。
tproxyの動作例(tcpdump)
正常動作時のtproxyモードの一連のHTTPリクエストをtcpdumpした結果を載せます。しかし、tcpdumpはiptablesのMASQUERADEの処理よりも外側でキャプチャしているので、MASQUERADEされた結果のIP(192.168.1.12)が表示されており、結果としてクライアントIP偽装の効果が全くわからずinterceptの場合と一切変わらない見た目になってしまいました。
13:03:12.308617 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [S], seq 1896807676, win 65535, options [mss 1460,sackOK,TS val 188001079 ecr 0,nop,wscale 8], length 0
13:03:12.308732 enp1s0 Out IP 1.1.1.1.80 > 192.168.1.90.40706: Flags [S.], seq 3677301018, ack 1896807677, win 65160, options [mss 1460,sackOK,TS val 1552045607 ecr 188001079,nop,wscale 7], length 0
13:03:12.311136 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [.], ack 1, win 256, options [nop,nop,TS val 188001090 ecr 1552045607], length 0
13:03:12.318286 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [P.], seq 1:72, ack 1, win 256, options [nop,nop,TS val 188001091 ecr 1552045607], length 71: HTTP: GET / HTTP/1.1
13:03:12.318356 enp1s0 Out IP 1.1.1.1.80 > 192.168.1.90.40706: Flags [.], ack 72, win 509, options [nop,nop,TS val 1552045617 ecr 188001091], length 0
13:03:12.318857 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [S], seq 1593039130, win 64240, options [mss 1460,sackOK,TS val 3277136633 ecr 0,nop,wscale 7], length 0
13:03:12.327464 enp1s0 In IP 1.1.1.1.80 > 192.168.1.12.36348: Flags [S.], seq 2443115970, ack 1593039131, win 65535, options [mss 1420,sackOK,TS val 3618305331 ecr 3277136633,nop,wscale 13], length 0
13:03:12.327663 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3277136642 ecr 3618305331], length 0
13:03:12.328018 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [P.], seq 1:191, ack 1, win 502, options [nop,nop,TS val 3277136642 ecr 3618305331], length 190: HTTP: GET / HTTP/1.1
13:03:12.335066 enp1s0 In IP 1.1.1.1.80 > 192.168.1.12.36348: Flags [.], ack 191, win 9, options [nop,nop,TS val 3618305340 ecr 3277136642], length 0
13:03:12.335829 enp1s0 In IP 1.1.1.1.80 > 192.168.1.12.36348: Flags [P.], seq 1:387, ack 191, win 9, options [nop,nop,TS val 3618305341 ecr 3277136642], length 386: HTTP: HTTP/1.1 301 Moved Permanently
13:03:12.335927 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [.], ack 387, win 501, options [nop,nop,TS val 3277136650 ecr 3618305341], length 0
13:03:12.336415 enp1s0 Out IP 1.1.1.1.80 > 192.168.1.90.40706: Flags [P.], seq 1:294, ack 72, win 509, options [nop,nop,TS val 1552045635 ecr 188001091], length 293: HTTP: HTTP/1.1 301 Moved Permanently
13:03:12.336506 enp1s0 Out IP 1.1.1.1.80 > 192.168.1.90.40706: Flags [P.], seq 294:461, ack 72, win 509, options [nop,nop,TS val 1552045635 ecr 188001091], length 167: HTTP
13:03:12.340538 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [.], ack 294, win 261, options [nop,nop,TS val 188001119 ecr 1552045635], length 0
13:03:12.340539 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [.], ack 461, win 265, options [nop,nop,TS val 188001120 ecr 1552045635], length 0
13:03:12.341345 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [F.], seq 72, ack 461, win 265, options [nop,nop,TS val 188001120 ecr 1552045635], length 0
13:03:12.341539 enp1s0 Out IP 1.1.1.1.80 > 192.168.1.90.40706: Flags [F.], seq 461, ack 73, win 509, options [nop,nop,TS val 1552045640 ecr 188001120], length 0
13:03:12.344530 enp1s0 In IP 192.168.1.90.40706 > 1.1.1.1.80: Flags [.], ack 462, win 265, options [nop,nop,TS val 188001124 ecr 1552045640], length 0
13:04:12.481473 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [F.], seq 1593039321, ack 2443116357, win 501, options [nop,nop,TS val 3277196796 ecr 3618305341], length 0
13:04:12.489537 enp1s0 In IP 1.1.1.1.80 > 192.168.1.12.36348: Flags [F.], seq 1, ack 1, win 9, options [nop,nop,TS val 3618365494 ecr 3277196796], length 0
13:04:12.489706 enp1s0 Out IP 192.168.1.12.36348 > 1.1.1.1.80: Flags [.], ack 2, win 501, options [nop,nop,TS val 3277196804 ecr 3618365494], length 0
ローカルマシンから使用した場合も特に変わらないので割愛します。
動かないときは
ご覧の通り、特にtproxyの場合は結構な数のコマンドを使って設定するので、それに応じて様々な原因で動かないことがあります。tcpdumpコマンドを使って、さっきの1-10のどのあたりで詰まっているのかを特定し、原因を探りましょう。
ブリッジ関連の設定について
公式ドキュメントにはebtablesだとかブリッジ接続に関するコマンドもいろいろ案内されています。このへんのコマンドの意味は筆者にはよくわかりませんが、とりあえずこの記事の内容を動作させるには不要でした。
redsocks、hev-socks5-tproxyなど他の透過プロキシクライアントとの違い
以前の関連記事ではSOCKS5プロキシを使ってTCP/UDPレイヤで透過プロキシを行う2つのソフトウェア(redsocks, hev-socks5-tproxy)のためのTPROXY設定を紹介しました。これらとSquidのtproxyの違いとして、まずTCP/UDPのL4レイヤかHTTPのL7レイヤかという機能的な違いがあるのはもちろんなんですが、TPROXY設定上の大きな違いとして、クライアント側IPを偽装するかどうかというのがあります。
Squidでは前述の通りインターネット側との通信においてもクライアント側IPを偽装します。一方、redsocksやhev-socks5-tproxyなどは、インターネット側との通信では普通に自分自身のIPを使います。この点ではSquidのinterceptモードに近いともいえるかもしれません。
従って、戻りパケットを正しく処理するためのDIVERT関連のルールはredsocksなどでは一切必要ありません(以前の関連記事でも使っていません)。MASQUERADEの必要もなく、従ってip_forwardも有効にする必要はありません。iptables(netfilter)のTPROXY自体のドキュメントにDIVERT云々が載っていることから、むしろredsocks側のTPROXYの使い方が邪道という可能性はありますが、そのへんは筆者の知識の範疇を超えています。
もっと言えば、netfilterのREDIRECTとTPROXYの一番大きな違いはUDPを透過的に処理できるかどうか(できるのはTPROXYのみで、だからredsocksとかはTPROXYを使っている)だと個人的には理解していて、クライアントIPを偽装する(IP_TRANSPARENT)こと自体はTPROXYを必要とするわけではない(そもそもTPROXYはSquid↔クライアントの間、クライアントIP偽装はSquid↔インターネットの間の話だし)ので、Squidのtproxyの機能をinterceptと同様にREDIRECTを使って実装すればよかったんじゃないかと思うわけですが、筆者が何か誤解しているのかもしれません。