なるほど!面白そうです!
— k1LoW (@k1LoW) 2019年1月30日
というわけで、勉強も兼ねてtcpdpをPROXY protocolに対応させてみました。
今回想定したユースケース
私自身がPROXY protocolのユースケースを持っているわけではなかったので、まずは「TCPパケットをいじらないユースケース」を想定して実装しました。
PROXY protocolを有効にして通信しているサーバの間に入ってパケットを取得する イメージです。
例えばフロントサーバにHAProxy、バックエンドにMariaDBとします。
まず、MariaDBでPROXY protocolを受け入れられるように設定します。
[mysqld] proxy-protocol-networks=::1, 0.0.0.0/0 ,localhost
tcpdp proxy を使う場合
proxyモードの場合は以下のような構成で考えます。
+------------------------+ +-------------------------------------+ | proxy.example.internal | | db.example.internal | | | | | | HAProxy:3306+---------------->tcpdp proxy:33306+--->MariaDB:3306 | | | | | +------------------------+ +-------------------------------------+
HAProxyは以下のように tcpdp が待ち受けるポートをbackendにします
global quiet defaults mode tcp frontend proxy bind *:3306 default_backend tcpdp-proxy backend tcpdp-proxy server tcpdp db.example.com:33306 send-proxy-v2
tcpdp はproxyモードで33306
とlocalhost:3306
をプロキシするようにして起動します。
$ tcpdp proxy -l 0.0.0.0:33306 -r localhost:3306 -d mysql --proxy-protocol --stdout
この状態でmysqlコマンドで proxy.example.internal:3306
に接続し、クエリを発行すると以下のように proxy_protocol_src_addr
にmysqlコマンドを発行したIPアドレスとポートが取得できます。
{"query":"INSERT INTO t1 VALUES (NULL,1804289383,'mxvtvmC9127qJNm06sGB8R92q2j7vTiiITRDGXM9ZLzkdekbWtmXKwZ2qG1llkRw5m9DHOFilEREk3q7oce8O3BEJC0woJsm6uzFAEynLH2xCsw1KQ1lT4zg9rdxBL')","seq_num":0,"command_id":3,"conn_id":"bhjpukel0s151gfqpo1g","client_addr":"127.0.0.1:56571","proxy_listen_addr":"127.0.0.1:33306","proxy_client_addr":"127.0.0.1:56572","remote_addr":"127.0.0.1:3306","proxy_protocol_src_addr":"172.24.0.1:37948","proxy_protocol_dst_addr":"172.24.0.3:3306","character_set":"utf8","username":"root","conn_seq_num":12,"direction":"->","ts":"2019-02-16T14:27:45.318+0900"}
proxy.
db.
それぞれ 127.0.0.1 になっているのは Docker でローカルに構築しているからです。わかりにくいので例としてみてください
tcpdp probe を使う場合
probeモードの場合は以下のような構成で考えます。
+------------------------+ +---------------------------+ | proxy.example.internal | | db.example.internal | | | | | | HAProxy:3306+-----------------+->MariaDB:3306 | | | | ^ | +------------------------+ | | | | ++------------+ | | | tcpdp probe | | | +-------------+ | +---------------------------+
HAProxyは以下のように tcpdp が待ち受けるポートをbackendにします
global quiet defaults mode tcp frontend proxy bind *:3306 default_backend mariadb backend mariadb server db db.example.internal:3306 send-proxy-v2
tcpdp はprobeモードでネットーワークインターフェースにアタッチします
$ tcpdp probe -i eth0 -t 3306 -d mysql --proxy-protocol --stdout
この状態でmysqlコマンドで proxy.example.internal:3306
に接続し、クエリを発行すると以下のように proxy_protocol_src_addr
にmysqlコマンドを発行したIPアドレスとポートが取得できます。
{"ts":"2019-02-18T00:35:31.925+0900","src_addr":"127.0.0.1:53786","dst_addr":"127.0.0.1:3306","query":"INSERT INTO t1 VALUES (NULL,866596855,'naQuzhMt1IrZIJMkbLAKBNNKKK2sCknzI5uHeGAgQuDd5SLgpN0smODyc7qorTo1QaI5qLl97qmCIzl0Mds81x7TxpIoJyqlY0iEDRNKA1PS0AKEn5NhuMAr3KgEIM')","seq_num":0,"command_id":3,"interface":"lo0","probe_target_addr":"3306","conn_id":"bhknugkrtr39kl5h32ng","mss":16344,"proxy_protocol_src_addr":"172.24.0.1:49854","proxy_protocol_dst_addr":"172.24.0.2:3306","character_set":"utf8","username":"root","database":"mysqlslap"}
こちらも同様にわかりにくいので例としてみてください
今回想定しなかったユースケース
今回想定しなかったユースケースとして、
PROXY protocolに対応していないRDBMSの前段に tcpdp proxy でプロキシサーバをたてて、upstreamのRDBMSにはPROXY protocolヘッダを外したパケットを流す
というものもありました。
これを実装するとMySQLやPostgreSQLでもPROXY protocolで接続元の情報を取得できるようになります。 (今回MariaDBを採用した理由もPROXY protocolに対応しているからでした)
ただ、そこまで必要なのかがわからなかったので、パケットをいじらない実装をしました。
こちらも要望があれば検討しようと思っています。
というわけで
tcpdpのProxy Protocol対応のホリデープログラミング無事完了ーhttps://t.co/6unKetRoc8
— k1LoW (@k1LoW) 2019年2月17日
休日の空いた時間にチマチマと進めていたのですが、PROXY protocol、勉強になりました!
haproxyとか?
— ぶてい (@buty4649) 2019年2月11日
あと、今回はじめてHAProxyを触ったのですが便利ですねコレ。これも良い経験になりました。