FreeBSD/OpenBSD/NetBSDで透過プロキシのredsocksを動かしてみる

概要

特定アプリケーションのTCP・UDP通信を透過的なSocks5プロキシ経由にする方法(Windows・Linux・Androidなど) - turgenev’s blogの外伝という感じで、BSD系のOSに触ってみるついでにredsocksを動かしてみました。(※NetBSDでは動かず)わからないところは適宜↑を読んでください。

普段使いする予定はなく実機に入れるのは面倒だったのでVirtualboxで試しました。

ちなみに、全て「そのPC自体から出ていく通信」(の一部)に透過プロキシを適用するものです。ルーターのような外部からのパケットへの設定はしていません。が、むしろそちらのほうが簡単だと思うので、たぶんこの記事の通りやればできます。

変更後のソースコードGitHub - ge9/redsocks: transparent redirector of any TCP/UDP connection to proxyにあげてあります。全てDISABLE_SHADOWSOCKS=trueでmake(コマンドはgmakeですが)しています。TCP/UDP両方で動作確認しています。IPv6は確認していません(&多分このままでは動かない)。

FreeBSD

BSD系の中では一番メジャーで、pfSenseの元にもなっています。インストールはそこまで詰まることなくいけました。バージョンは14.0です。

各種パッケージはpkg install gitとかで入るようです。

外部インターフェイスにもともと(Virtualbox側のDHCPにより)10.0.2.15/24が割り当てられていたので10.0.2.25を追加しました。

ifconfig em0 alias 10.0.2.25 netmask 0xffffff00

ここから出ていくパケットに透過プロキシを適用します。テスト用コマンドはいつものstunserverの--localaddr 10.0.2.25です。本当に便利です。

ファイアウォールipfwを使います。ipfilter(ipf)とは別物です。

上記のifconfigとまとめると以下のようになります。

kldload ipfw
fwcmd=ipfw
ifconfig em0 alias 10.0.2.25 netmask 0xffffff00
$fwcmd add 100 allow all from any to any via lo0
$fwcmd add 500 fwd 127.0.0.1,22222 tcp from 10.0.2.25 to any
$fwcmd add 600 fwd 127.0.0.1,22222 udp from 10.0.2.25 to any
$fwcmd add 700 allow ip from any to any

redsocksではredirector=genericとします。これでとりあえず動くと思います。

ソースコードで修正した点としては、なんかsizeofのあたりでうまくいってなさそうなのをちょっと直したのと、bound_udp_getのbindのところでエラーが出るのを無視したらいけました。

また、FreeBSDには、OpenBSD由来であるpfもあります。結果的に動きませんでしたが一応載せます。/etc/pf.confは以下のようにします。

rdr pass on lo0 proto {tcp, udp} from 10.0.2.25 -> 127.0.0.1 port 22222
pass out quick route-to lo0 from 10.0.2.25
pass

これでちゃんとredsocksにパケットは届くのですが、宛先が本来の宛先ではなく127.0.0.1:22222として取得されてしまっていて正しく動きません。これに関してはOpenBSDのところで述べます。

ipfilterのほうも一応やってみましたがうまくいきませんでした。ipfilterにおいてはNATはipnatというものを使って行うようで、こちらにはrdrという構文がありましたが、普通のDNAT(1.1.1.1:80に来たやつは192.168.1.1:80に流すとか)しかできそうになく、透過プロキシとして動作させる方法は不明です。redsocks自体にはredirector=ipfという設定があるので、何か方法があるような気もするのですが…。

pfSense

有名なFreeBSD派生とのことでpfSenseも使ってみました。

インストール時にインターネットに接続する必要があって、DHCPでやるのですがうまくいかないことがあるので何回かリトライすると通ります。

シェルまで戻ってしまっても、インストーラの起動はpfSense-installerコマンドでいけそうです。

インストール後もまたネットワークのところでちゃんと選んでるのに無限ループ?みたいになってイライラします。enter押すんじゃなくてem0とかちゃんとインターフェイス名入れると通ったりします。また、再起動後も確率的にDHCPが失敗します(というか再起動じゃなくて完全に電源オフ→起動しないとダメ?)。うまくいっていても使用中にいきなり切れたりもします。なんなんですかね。VirtualBoxの問題な気もしますが、普通のFreeBSDでは特に問題ありませんでした。

Using Software from FreeBSD | pfSense Documentationに書いてある通り、そのままだとセキュリティ上の理由でFreeBSD由来のパッケージは無効になっていますが、有効にするとpkg installで入れられるようになります。

clangが見つからないのでgccを入れました。(g++も入っています)

が、sys/types.hが無いとかで各種ソフトウェアのコンパイルはなんか難しそうです。幸いFreeBSD派生であり基本的にはバイナリレベルで互換性があるのでそちらからバイナリを持ってきました。

あとはFreeBSDと全く同様にipfwでやってみたら、なぜかUDPしか動きませんでした。本当に全く同じように設定しているはずなんですが…TCPのほうはそもそも透過プロキシに届いていません。UDPのルールと干渉している雰囲気もないです。原因不明。

pf/ipfは未調査ですがおそらくFreeBSDと同じ状況と思われます。

OpenBSD

NetBSDのforkで、FreeBSDよりはいくぶんマイナーかと思います。堅牢らしいです。

バージョンは7.5です。まずインストールでちょっと詰まりました。パッケージが全部入っててインターネットがなくてもインストールできるCD版のisoを落としたはずなのですが結局CD(cd0)ではなくインターネット(http)にしたらインストールが成功しました。ミラーはrepo.jing.rocksを使いました。追記: cd0のエラーは多分OpenBSD 7.3 on VirtualBox 7: ゲスト OS インストールに失敗 - 原因は I/O APIC 有効化 | OpenBSD Solutions ブログこれかな?再起動したらうまくいったこともありました。感覚的には、ホスト側マシンのメモリ・CPU使用率に余裕があるほうが成功しやすい気がします。

各種パッケージはpkg_add gitとかで入れます。

まず、em0に10.0.2.25を追加するため、/etc/hostname.em0(もともとinet autoconfとだけ書いてある)にalias 10.0.2.25 netmask 255.255.255.0みたいなことを追記し、sh /etc/netstartをしました。

さて、OpenBSDではpfを使います。/etc/pf.confは以下のようにします。

pass        # establish keep-state
pass in quick proto {tcp, udp} from 10.0.2.25 to ! 10.0.2.25 divert-to 127.0.0.1 port 22222
pass out quick proto {tcp, udp} from 10.0.2.25 route-to lo0

本当に最低限で、セキュリティ皆無なので注意。透過プロキシの都合上、デフォルトでよくあるset skip on lo0をそのままにしているとたぶんうまくいかないです。to ! のところで10.0.2.25に向かうものだけ除外していますが、これがないとせっかく返ってきた応答パケットがまた透過プロキシに入ってしまうような感じになったので付けました。

pf.confのmanページのdivert-toのところを読むとIP_RECVDSTADDRがなんだかんだとかいろいろ書いてあって、これがカギです。実際、OpenBSD対応にあたっての変更点はこれらに関するものが主です。あとはccコマンドの-ldlが使えないらしいので無効にするのと、リンク時にlibevent関連の関数を見つけられてないようだったのでln -s /usr/local/lib/libevent_core.a /usr/local/lib/libevent.aをしました。あとはFreeBSD向けの変更をそのまま適用してやる感じです。

'Re: pf: difference between rdr-to and divert-to' - MARCにそれっぽいことが書いてありますが、(すくなくともこの記事のやり方だと)divert-toのかわりにrdr-toを使うと元の送り先アドレスが取得できず(最初から127.0.0.1:22222に送られたかのようになってしまう)、うまくいきません。FreeBSDのrdrの挙動もこれが理由です。FreeBSDでは、divert-toは(構文として存在はするものの全く別の動作であり実質的には)使えません。

(すくなくともこの記事のやり方だと)redirector=genericではなくredirector=pfにすると、TCP通信に関しては動きませんでした。ただredsocks側のソースをちゃんと変えれば動きそうな雰囲気はします。

NetBSD

OpenBSDのfork元ですが、ただでさえマイナーなBSD系の中でも近年はほとんど利用者がおらず、OSとしてはかなり厳しい状況のようです。

一応やってみました。バージョンは10.0です。

こっちはCDからでちゃんとインストールできました。

起動すると、まずそもそもIPアドレスが割り当たっていませんでしたがdhcpcd wm0でいけました。

pkg_addでgitを入れたいのですがエラーが出ます。どうやらPKG_PATH変数を設定しないといけないようです。日本のミラーにしてみます。ftpだとだめでhttpだといけました。

export PKG_PATH="http://ftp.allbsd.org/pkgsrc/packages/NetBSD/amd64/10.0/All/"   

ところが、衝突もしてないのにdependencyを自動で入れてくれません。仕方ないので足りないと言われたものを入れていったら一応できましたが…さすがに不便です。

で、IP_RECVDSTPORTが定義されていないというエラーで詰まりました。NetBSDのpf.confを見ると、そもそもdivertがありません。なんか無理っぽい雰囲気がしたのとさすがに需要がなさすぎるのでここでやめました。pf.confも書いていません。

まとめ

過疎っていて先行きは怪しそうですがBSD系にちょっと触ることができてよかったです。