(JE&BE対応) VPSにHaProxyとNginxを入れてBungeeCordの通信をリバースプロキシする

Minecraftで、大本のIPを隠すためにVPSを使ってリバースプロキシしていきます。
複数のVPSを用意してGSLBなどで分散することにより、DDoS対策としても使えます。

構成としては、
```
[BungeeCord - MCServer] <--> [VPS(Proxy)] <--> [クライアント]
```
のようになります。

接続してきたユーザーのIPアドレスも欲しいので、proxy_protocolを使って実現していきます。

:::note warn
知識が浅いため、様々な記事やドキュメントを調べながら行っています。
最低限動作する事を目標にしているので、動作が不安定かもしれません。
:::

## 今回使うVPS
WebARENA Indigoの8GB 6vCPUプランのVPSを使用します。
OSはUbuntu 20.04です。
オーバースペックな気もしますが...まぁいいでしょう。
~~友人からもらったVPSを使っているので...~~

# 準備 - BungeeCord / Geyser

BungeeCordのconfig.yml内の
`proxy_protocol`を`true`に変更。
***
Geyserのconfig.ymlにある
`enable-proxy-protocol`
`proxy-protocol-whitelisted-ips`を以下のように変更
```yml
enable-proxy-protocol: true #有効化
proxy-protocol-whitelisted-ips: "VPSのIP" #ホワリス
```
複数のVPSからのアクセスを許可したい場合は
`proxy-protocol-whitelisted-ips: [ "VPSのIP", "VPSのIP(2)" ]`のようにします。

# 準備 - VPS側

HaProxyをインストール
```bash
$sudo apt install haproxy
```

Nginxをビルド

:::note alert
aptで入手可能なNginxには`ngx_stream_*`モジュールが含まれていません。
そのため、自分でビルドしなければなりません。
:::

[Nginxの最新版](https://nginx.org/download/)をダウンロードします。
(2024/01/19時点では1.25.3が最新でした。)
この記事では1.24.0を使用します。
```bash
$wget http://nginx.org/download/nginx-1.24.0.tar.gz
$tar zxvf nginx-1.24.0.tar.gz
$cd nginx-1.24.0
```

ビルドに必要なライブラリをダウンロードします。
```bash
$apt install build-essential libpcre3-dev zlib1g-dev
```

ユーザーを作成します。
```bash
$ sudo groupadd nginx
$ sudo useradd -g nginx nginx
$ sudo usermod -s /bin/false nginx
```

ビルドします。

```bash
$sudo ./configure \
    --sbin-path=/usr/sbin/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --pid-path=/var/run/nginx.pid \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --user=nginx \
    --group=nginx \
    --with-stream

$sudo make
$sudo make install
```

起動し、無事に動作していることを確認
```bash
$ sudo systemctl start nginx.service
$ sudo systemctl status nginx.service
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset:>
     Active: active (running) since Mon 2023-09-25 20:01:45 JST; 16h ago
       Docs: man:nginx(8)
    Process: 660193 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_pro>
    Process: 660200 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; >
   Main PID: 660205 (nginx)
      Tasks: 3 (limit: 9513)
     Memory: 2.3M
     CGroup: /system.slice/nginx.service
             tq660205 nginx: master process /usr/sbin/nginx -g daemon on; maste>
             tq660207 nginx: worker process
             mq660208 nginx: worker process
```

# TCP(Java版)

Java版(TCP)のリバースプロキシを作っていきます。
エディタを使って、`/etc/haproxy/haproxy.cfg`のconfigを
```config
#Global
global

    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats


# Gateway Settings
defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000


# MC Gateway
listen minecraft01
    bind 0.0.0.0:25565
    mode tcp
    option tcplog
    balance roundrobin
    server mc01 <BungeeCord-IP>:<Port> send-proxy-v2
```
のようにしてください。

:::note info
`<BungeeCord-IP>`と`<Port>`はBungeeCordが動いているサーバーのIP(ドメイン):ポートにしてください。
例: `123.456.78.90:25565`、`mc.example.com:25566`
:::

設定したら、念のために
```bash
haproxy -f /etc/haproxy/haproxy.cfg -c
```
を行い、`Configuration file is valid`となっていることを確認したら
```bash
systemctl restart haproxy
```
で再起動しましょう。
再起動後、`VPSのIPアドレス:ポート番号`から正常に接続できれば成功です。


# UDP(BE版)

BE版(UDP)のリバースプロキシを作っていきます。
エディタを使って、`/etc/nginx/nginx.conf`のconfigを
```conf
worker_processes 2;

events {
    worker_connections 1024;
}

stream {
    log_format proxy '$remote_addr [$time_local] '
                     '$protocol $status $bytes_sent $bytes_received '
                     '$session_time "$upstream_addr" '
                     '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

    upstream minecraft_udp_upstreams {
        server <BungeeCord-IP>:<Port>;
    }

    server {
        error_log /var/log/nginx/error.log debug;
        access_log /var/log/nginx/access.log proxy buffer=32k;
        listen 19132 udp;
        proxy_pass minecraft_udp_upstreams;
        proxy_timeout 120s;
        proxy_protocol on;
    }
}
```
のようにしてください。

:::note info
Java版と同様`<BungeeCord-IP>`と`<Port>`はBungeeCordが動いているサーバーのIP(ドメイン):ポートにしてください。
例: `123.456.78.90:19132`
:::

:::note warn
upstreamの部分でドメインを使用する場合、`ngx_upstream_jdomain`のようなドメインを解決できるものが必要となるらしいです。
:::

念のために構文チェック。
```bash
$nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
となっているのを確認したら
```bash
$systemctl restart nginx
```
で再起動しましょう。
再起動後、`VPSのIPアドレス:ポート番号`から正常に接続できれば成功です。

# おわりに

試してみた感じでは特に問題もなく安定しており、期待通りの動作となりました。
皆さんも、是非試してみてはいかがでしょうか。

> NginxのUDPでproxy_protocolを使う方法に関する記事がかなり少なく、数時間ぐらい格闘しました。
> 解決策を見つけられた時の達成感はやっぱり最高 :thumbsup:

## 参考記事


https://unchama.hatenablog.com/entry/2019/05/19/153830


https://unchama.hatenablog.com/entry/2019/05/19/164159


https://qiita.com/ryysud/items/b0c9d8384efb2ea148da