HAProxyを透過型のプロキシとして使う(HAProxy with tproxy)

by edwin van buuringen



HAProxyは基本的にL7レイヤのロードバランサー(リバースプロキシ)なので、バックエンドにいるリアルサーバには、フロントエンドから届いたリクエストが、ロードバランサのIPアドレスからアクセスが来たかのように振舞います。


で、HAProxyはtproxy(transparent proxy)をサポートしているようなので、L4で動く透過型のプロキシとしても振舞うことが出来るようです。ので、ちょっと試してみました。
使ったOSは、CentOS 6.4で、HAProxyは開発版の1.5-dev19です。

参考: Transparent proxy support (www.kernel.org/doc)

Linux kernelのnf_tproxy_coreモジュールをロードする

ここからは、実際にHAProxyの稼動するサーバに対してのセットアップを行います。


まず、tproxy関連のモジュールのロードが必要です。
今回使ったCentOS 6.4には既に組み込まれていたので、ロードするだけです。

# modprobe -l | grep tproxy
kernel/net/netfilter/nf_tproxy_core.ko

tproxy関連のモジュールを探してみるとありました。"nf_tproxy_core" これっぽい。

# modinfo nf_tproxy_core
filename:       /lib/modules/2.6.32-358.18.1.el6.x86_64/kernel/net/netfilter/nf_tproxy_core.ko
description:    Transparent proxy support core routines
author:         Krisztian Kovacs
license:        GPL
srcversion:     C0E2C4553F0913555683F1E
depends:
vermagic:       2.6.32-358.18.1.el6.x86_64 SMP mod_unload modversions

Transparent proxy support core routines」とあります。

# modprobe nf_tproxy_core

このモジュールをロードして、、、

# lsmod | grep tproxy
nf_tproxy_core          1332  0 [permanent]

組み込まれました。

HAProxy側の確認

開発版(1.5-dev19)で確認する限り、デフォルトのままビルドしても、以下の通り "haproxy -vv"を実行すると、、、

Built with transparent proxy support using: IP_TRANSPARENT IP_FREEBIND

transparent proxy support using の文字列が見えます。


一応、HAProxyをビルドする際に "USE_LINUX_TPROXY=1" のフラグを立ててビルドしたら、以下のようになりました。

Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND

一応、今回はIPv6環境下での確認もしたかったので、↑でいきます。

カーネルパラメータの設定

"/etc/sysctl.conf"に以下の設定を入れます。(利用しているインターフェース名を考慮して適宜書き換えてください。)

net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.lo.send_redirects = 0

iptables / route の設定

ドキュメントの通りではありますが、以下のようにiptablesと、ip rule、ip routeの設定を入れます。

# iptables -t mangle -N DIVERT
# iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
# iptables -t mangle -A DIVERT -j MARK --set-mark 1
# iptables -t mangle -A DIVERT -j ACCEPT

と、

# ip rule add fwmark 1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100


"/etc/sysconfig/iptables"的には以下のような感じですね。

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:DIVERT - [0:0]
-A PREROUTING -p tcp -m socket -j DIVERT
-A DIVERT -j MARK --set-mark 1
-A DIVERT -j ACCEPT
COMMIT

HAProxy の設定

最後に、"/etc/haproxy/haproxy.cfg"に以下の設定を入れます。

source  0.0.0.0  usesrc  clientip

この一行を、listenとかbackendの部分に入れておけばOKです。

動作確認

では、実際に動かしてtcpdumpでパケットの動きを眺めてみます。試した構成は、

Client <=> HAProxy <=> Real Server

になります。ClientとなるマシンからHAProxyのマシンが持つVIP(仮想IPアドレス)にリクエストを投げて、それをHAProxyがバックエンドのReal Serverへ渡す一般的な構成です。

バックエンドのReal Serverのインターフェースのところで、tcpdumpを動かしてキャプチャした結果が以下です。

通常時(tproxy設定なし)

17:58:42.554387 IP haproxy.54544 > real-server.http: Flags [S], seq 1028255911, win 14600, options [mss 1460,sackOK,TS val 800508775 ecr 0,nop,wscale 7], length 0
17:58:42.554405 IP real-server.http > haproxy.54544: Flags [S.], seq 4019287287, ack 1028255912, win 65160, options [mss 1460,sackOK,TS val 11233434 ecr 800508775,nop,wscale 12], length 0
17:58:42.554598 IP haproxy.54544 > real-server.http: Flags [.], ack 1, win 115, options [nop,nop,TS val 800508775 ecr 11233434], length 0
17:58:42.554647 IP haproxy.54544 > real-server.http: Flags [P.], seq 1:168, ack 1, win 115, options [nop,nop,TS val 800508775 ecr 11233434], length 167
17:58:42.554653 IP real-server.http > haproxy.54544: Flags [.], ack 168, win 16, options [nop,nop,TS val 11233434 ecr 800508775], length 0
17:58:42.554793 IP real-server.http > haproxy.54544: Flags [P.], seq 1:216, ack 168, win 16, options [nop,nop,TS val 11233435 ecr 800508775], length 215
17:58:42.554828 IP real-server.http > haproxy.54544: Flags [P.], seq 216:237, ack 168, win 16, options [nop,nop,TS val 11233435 ecr 800508775], length 21
17:58:42.554991 IP haproxy.54544 > real-server.http: Flags [.], ack 216, win 123, options [nop,nop,TS val 800508776 ecr 11233435], length 0
17:58:42.555015 IP haproxy.54544 > real-server.http: Flags [.], ack 237, win 123, options [nop,nop,TS val 800508776 ecr 11233435], length 0
17:58:42.555407 IP haproxy.54544 > real-server.http: Flags [F.], seq 168, ack 237, win 123, options [nop,nop,TS val 800508776 ecr 11233435], length 0
17:58:42.555426 IP real-server.http > haproxy.54544: Flags [F.], seq 237, ack 169, win 16, options [nop,nop,TS val 11233435 ecr 800508776], length 0
17:58:42.555725 IP haproxy.54544 > real-server.http: Flags [.], ack 238, win 123, options [nop,nop,TS val 800508776 ecr 11233435], length 0

バックエンドの実サーバ的には、HAProxyのホストからアクセスがきているように見えます。L7リバースプロキシの挙動としては普通の状態ですね。

透過設定時(tproxy設定あり)

17:59:28.514936 IP client.42447 > real-server.http: Flags [S], seq 3757471154, win 14600, options [mss 1460,sackOK,TS val 800554735 ecr 0,nop,wscale 7], length 0
17:59:28.514969 IP real-server.http > client.42447: Flags [S.], seq 2578532225, ack 3757471155, win 65160, options [mss 1460,sackOK,TS val 11279395 ecr 800554735,nop,wscale 12], length 0
17:59:28.515320 IP client.42447 > real-server.http: Flags [.], ack 1, win 115, options [nop,nop,TS val 800554736 ecr 11279395], length 0
17:59:28.515337 IP client.42447 > real-server.http: Flags [P.], seq 1:168, ack 1, win 115, options [nop,nop,TS val 800554736 ecr 11279395], length 167
17:59:28.515343 IP real-server.http > client.42447: Flags [.], ack 168, win 16, options [nop,nop,TS val 11279395 ecr 800554736], length 0
17:59:28.515458 IP real-server.http > client.42447: Flags [P.], seq 1:216, ack 168, win 16, options [nop,nop,TS val 11279395 ecr 800554736], length 215
17:59:28.515485 IP real-server.http > client.42447: Flags [P.], seq 216:237, ack 168, win 16, options [nop,nop,TS val 11279395 ecr 800554736], length 21
17:59:28.515838 IP client.42447 > real-server.http: Flags [.], ack 216, win 123, options [nop,nop,TS val 800554736 ecr 11279395], length 0
17:59:28.515844 IP client.42447 > real-server.http: Flags [.], ack 237, win 123, options [nop,nop,TS val 800554736 ecr 11279395], length 0
17:59:28.516162 IP client.42447 > real-server.http: Flags [F.], seq 168, ack 237, win 123, options [nop,nop,TS val 800554737 ecr 11279395], length 0
17:59:28.516181 IP real-server.http > client.42447: Flags [F.], seq 237, ack 169, win 16, options [nop,nop,TS val 11279396 ecr 800554737], length 0
17:59:28.516396 IP client.42447 > real-server.http: Flags [.], ack 238, win 123, options [nop,nop,TS val 800554737 ecr 11279396], length 0

この通り、HAProxyを介していますが、バックエンドの実サーバ的にも、どのクライアントからアクセスが来たのかわかるようになりました。

注意点

Client <=> HAProxy <=> Real Server

色々やってみたのですが、今回のこの構成では、"Client 〜 HAProxy"のネットワークと"HAProxy 〜 Real Server"のネットワークが別セグメントで、Real ServerからClientの戻りパケットをHAProxyのマシンを介す(つまり、ルーティング上、Clientのいるネットワーク宛のゲートウェイをHAProxyのホストにする)ようなパターンで、きちんと動きました。

Client、HAProxy、Real Serverが同じネットワークセグメントにいると動かなかったです。(正確にはパケットは戻るのですが、Real ServerからClientにSYN ACKが戻りRSTが返されるような状態。)
Real Serverのところを小細工すれば何とかなるような気がしなくも無いですが、あまり手を入れて複雑にはしたくない気もする。


と、こんな感じで軽く検証しただけで、ドキュメントとかユーザ事例をまだちゃんと追えてないので、何ともいえないですが、何か分かれば追記したいと思います。
それでは!=͟͟͞͞(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́

参考リンク


詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版

  • 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
  • 出版社/メーカー: オライリー・ジャパン
  • 発売日: 2007/02/26
  • メディア: 大型本
  • 購入: 9人 クリック: 269回
  • この商品を含むブログ (73件) を見る