📖
HAProxy で複数の Minecraft サーバへのプロキシを作る
概要
- 単一の HAProxy コンテナで、複数のドメインの Minecraft サーバへのプロキシをしたい場合の設定例
- HAProxy を ひとつのグローバルIP しか持たない VPS などに置いていて、かつ複数のバックエンドサーバに異なるホスト名でプロキシさせたい、というような状況を想定
- この記事は HAProxy + Tailscale で公開する自宅 Minecraft サーバ を読んでいることを前提にしています
haproxy.conf の設定
プロキシサーバ xxx.xxx.xxx.xxx にて、mc1.example.net と mc2.example.net への接続を受け付け、それぞれ 192.168.2.2, 192.168.2.3 へ振り分けたいとします。
haproxy.conf を以下のように設定します。
defaults
mode tcp
timeout connect 10s
timeout client 300s
timeout server 300s
log global
frontend mc-proxy
mode tcp
bind xxx.xxx.xxx.xxx:25565
tcp-request inspect-delay 3s
# Handshake パケットであれば通す
tcp-request content accept if { req.payload(0,1) -m bin 00 }
# パケットの Payload の 4バイト目から32バイト目までの値(ホスト名)をチェック
# ホスト名に一致する値があれば、各ホストのフラグを立てる
acl is_mc1 req.payload(4,32) -m sub mc1.example.net
acl is_mc2 req.payload(4,32) -m sub mc2.example.net
# フラグによって転送するバックエンドサーバを切り替える
use_backend mc1_backend if is_mc1
use_backend mc2_backend if is_mc2
default_backend fallback_backend
backend mc1_backend
mode tcp
server mc1 192.168.2.2:25565 check
backend mc2_backend
mode tcp
server mc2 192.168.2.3:25565 check
# 意図しないホスト名へのアクセスは切断する
backend fallback_backend
mode tcp
tcp-request content reject
上記の設定が済んだら、HAProxy を再起動すればOKです。
#Docker で HAProxy を動かしている場合、構成によってはイメージのリビルドが必要になります。
振り分けの仕組み
Minecraft Java Edition のパケットの資料をざっと斜め読みしただけなので、理解が誤っているかもしれませんが……。
- Minecraft のクライアントは、TCP セッションが開いた直後に Handshake パケットを送信する
- パケットの Payload の0-1バイトが 00 であれば Handshake パケットである
- haproxy.conf の
req.payload(0,1) -m bin 00
で判定
- Handshake パケットのペイロードの4-32バイトに接続先ホスト名が含まれる
- HAProxy がここをチェックする時間を確保する必要がある
- haproxy.conf の
tcp-request inspect-delay 3s
でわざと遅延を発生させている - haproxy.conf の
req.payload(4,32) -m sub mc1.example.net
でホスト名を判定する
- 接続先ホスト名によって転送先バックエンドを切り替える
- 以降の通信は TCP セッション内で行われる
参考資料
- Java Edition protocol – Minecraft Wiki
https://minecraft.wiki/w/Java_Edition_protocol#Set_Compression - HAProxyでMinecraftでもVirtualHostを実現させる #minecraft - Qiita
https://qiita.com/PitneS/items/f3a3357c93d307c7ef0f
Discussion