nginxをminecraftのプロキシとして使う

# はじめに
Webサーバーとして知られている[nginx](https://nginx.org/en/)ですが、ロードバランサー的な使い方をすることもできます。
今回はnginxをminecraftマルチサーバーのフロントエンドサーバー（TCPリバースプロキシ）として使う実験をしたいと思います。

minecraftに限らず、サービスを動かすサーバーに直接接続させずにリバースプロキシを経由させることはセキュリティ的に意味のある事だと思います。
また、自宅サーバーを立てている場合にVPS等をリバースプロキシとして使用することで、レイテンシを犠牲にDoS攻撃のリスクを回避できる可能性があります。
VPSを利用したリバースプロキシは自宅サーバーを立てる際に障害となる固定IPアドレス問題の解決策でもあります。

# つくるもの
[VirtualBox](http://www.oracle.com/technetwork/jp/server-storage/virtualbox/downloads/index.html)を使用してローカル環境で実験をしていきます。
※VirtualBoxはこういった遊びができるのでいいですよね。

![20180131(3).png](https://qiita-image-store.s3.amazonaws.com/0/126305/581f8534-60d8-9624-7feb-e0fa3c333c4c.png)

※図は[draw.io](https://www.draw.io)を使用して作成しました。

VirtualBox上に二つの仮想マシンを用意します。
一方は**nginxマシン**、もう一方は**minecraftマシン**として使用します（以降この呼び分けに注意）。
この二つは互いに**内部ネットワーク**で接続します。
ホストOSからはnginxマシンに**NAT**で接続します。

ホストOSから、nginxマシンにNATで接続した後、nginxを経由してminecraftマシンに接続します。

# 準備するもの
- VirtualBox上のCentOS7をインストールした仮想マシンx2

（一応）
VirtualBoxのダウンロード元：http://www.oracle.com/technetwork/server-storage/virtualbox/downloads/index.html?ssSourceSiteId=otnjp
CentOS7のダウンロード元：https://www.centos.org/download/

# 実験開始
## 仮想マシンの初期設定
仮想マシンを二つ扱うので、設定する方を間違えるのを防ぐためにホスト名を変更しておきましょう。
この作業は必須ではありませんが事故防止のためやっておきましょう。

参考：https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/sec_configuring_host_names_using_hostnamectl

```
[root@localhost ~]# hostnamectl set-hostname minecraft01
```

ログインしなおして、設定が反映されていることを確認しましょう。

```
[root@minecraft01 ~]#
```

nginx用の仮想マシンも変更します。

```
[root@localhost ~]#  hostnamectl set-hostname nginx01
```

以降の説明でどちらの仮想マシンを操作しているかはホスト名で見分けてください。
## NATの設定
nginxマシンの設定からNATのポートフォワーディングの設定をしましょう。
これは上の図で、ホストOSが25565ポートでnginxに接続するための設定です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/04d27f2f-81b7-aada-05a4-d5d51406a88a.png)
ポートフォワーディングをクリック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/4efec2b8-8dfa-17b7-6806-960ac4fe5c02.png)
**Rule2**をこのように追加しました。
Rule1はssh接続をするための設定なので気にしなくて大丈夫です。

## 内部ネットワークの設定
### アダプターの追加
二つの仮想マシンが互いに通信するためのネットワークの設定をします。
この設定をするときは**仮想マシンの電源をオフ**にしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/3a2bb426-8ae5-3557-8fb1-ba15c98732f1.png)

1. ネットワークの項目を開き、アダプター2を選択します
1. 「ネットワークアダプターを有効化」にチェックを入れ割り当てを内部ネットワークに指定します

名前は何でもいいですが、**もう一つの仮想マシンと同じ**にします。

### アダプターの有効化とIPの設定
仮想マシンを起動します。

そのままでは追加したネットワークアダプターは有効になっていないので、有効にします（同時にIPアドレスを設定します）。

```
[root@nginx01 ~]# nmcli d
デバイス  タイプ    状態                       接続
enp0s3    ethernet  接続済み                   enp0s3
enp0s8    ethernet  接続中（IP 設定を取得中）  有線接続 1
lo        loopback  管理無し  
[root@nginx01 ~]# nmcli c add type ethernet ifname enp0s8 con-name enp0s8
接続 'enp0s8' (f2bff77f-da44-4744-89b7-2ff70309dba1) が正常に追加されました。
[root@nginx01 ~]# nmcli c m enp0s8 ipv4.method manual ipv4.addresses 192.168.0.1/24 connection.autoconnect true
[root@nginx01 ~]# nmcli d
デバイス  タイプ    状態      接続
enp0s3    ethernet  接続済み  enp0s3
enp0s8    ethernet  接続済み  enp0s8
lo        loopback  管理無し  --
```
nginxマシンのIPを192.168.0.1に設定しました。
同様の操作でminecraftマシンのIPを192.168.0.2に設定します。

次に仮想マシン間で通信ができることを確認します。

```
[root@minecraft01 ~]# ping 192.168.0.1 -c 3
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.237 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.426 ms
64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=0.682 ms

--- 192.168.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.237/0.448/0.682/0.183 ms
```

minecraftマシンから192.168.0.1（nginxマシン）にpingを投げました。

## nginxのインストールと設定
nginxをTPCモードで使うためには自前でビルドする必要があります。
参考（公式）：https://www.nginx.com/resources/admin-guide/tcp-load-balancing/

### ビルド
ビルドしていきましょう。
※gccやgit等は各自インストールしておきましょう。

`--with-stream`オプションを付けることでTCP/UDPモードで動作するようになります。

```
# nginxのリポジトリをクローン
[root@nginx01 nginx]# git clone https://github.com/nginx/nginx
# ビルド
[root@nginx01 nginx]# cd nginx
[root@nginx01 nginx]# ./auto/configure --prefix=/usr/local/nginx --without-http_rewrite_module --without-http_gzip_module --with-stream
[root@nginx01 nginx]# make && make install
```
### pidファイルの指定
設定ファイルの場所を確認しましょう

```
[root@nginx01 nginx]# ls /usr/local/nginx/conf/nginx.conf
/usr/local/nginx/conf/nginx.conf
```

pidファイルの設定がコメントアウトされているので次のように変更します。

```/usr/local/nginx/conf/nginx.confの9行目あたり
pid        /var/run/nginx.pid;
```

### systemdの設定
systemd用の設定ファイルを作成します

```
[root@nginx01 nginx]# emacs /usr/lib/systemd/system/nginx.service
```

```/usr/lib/systemd/system/nginx.service
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target
```

**この書き方は一例です**。
**私は書き方がわからなかったのでyumで入るnginxの設定を流用しました（書き方が間違えていたらすみません）**。

### 起動確認

```
[root@nginx01 nginx]# systemctl start nginx
[root@nginx01 nginx]# systemctl status nginx
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since 木 2018-02-01 01:49:49 JST; 5s ago
     Docs: http://nginx.org/en/docs/
  Process: 14972 ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf (code=exited, status=0/SUCCESS)
  Process: 14971 ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 14974 (nginx)
   CGroup: /system.slice/nginx.service
           ├─14974 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
           └─14976 nginx: worker process

 2月 01 01:49:49 nginx01 systemd[1]: Starting nginx - high performance web server...
 2月 01 01:49:49 nginx01 nginx[14971]: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
 2月 01 01:49:49 nginx01 nginx[14971]: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is ...ssful
 2月 01 01:49:49 nginx01 systemd[1]: Failed to read PID from file /var/run/nginx.pid: Invalid argument
 2月 01 01:49:49 nginx01 systemd[1]: Started nginx - high performance web server.
Hint: Some lines were ellipsized, use -l to show in full.
[root@nginx01 nginx]#
```

なんか`Failed to read PID from file /var/run/nginx.pid: Invalid argument`と不安な文字が出ていますが起動できています（これの直し方がわかりませんでした）。

### TCPリバースプロキシのための設定
設定ファイルを次のように書き換えます。
これはあくまでも一例ですので適宜修正して使用してください。

```/usr/local/nginx/conf/nginx.conf
worker_processes  1;

pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}
stream {
    upstream mcserver {
        server 192.168.0.2:25565;
        server 127.0.0.1:25566 backup;
    }
    server {
        listen     25565;
        proxy_pass mcserver;
    }
}
```

minecraftマシン上のminecraftサーバーに接続できない場合に、nginxマシンのローカルのminecraftサーバーに接続するよう`server 127.0.0.1:25566 backup;`を設定しておきます。

最後にfirewallの設定をします。

```
[root@nginx01 nginx]# firewall-cmd --add-port=25565/tcp --zone=public --permanent
success
[root@nginx01 nginx]# firewall-cmd --reload
success
```

これでnginxの準備は整いました。
あとはminecraftサーバーの準備をしていきましょう。

## minecraftサーバーの立ち上げ
nginxの設定に書いた通り、**minecraftマシン上のminecraftサーバーに接続できない場合に、nginxマシン上のminecraftサーバーに接続する**ようにしたいので**両方の仮想マシンに**minecraftサーバーをインストールします。

ただし、nginxマシン上のminecraftサーバーは**nginxがListenしているポートを避けるため**25566番ポートで起動します。**ローカルからのアクセス**となるためfirewallの設定は不要です。

今回は設定しませんが、minecraftマシンにはnginxマシンからしかアクセスしないので192.168.0.1からの接続だけを通すfirewallの設定にするとよいと思います。

設定していきましょう。

### minecraftサーバーのインストール
javaのインストール

```
[root@minecraft01 ~]# yum  -y install java-1.8.0-openjdk.x86_64
```

minecraftサーバーのダウンロード

```
[root@minecraft01 ~]# mkdir server
[root@minecraft01 ~]# cd server/
[root@minecraft01 server]# wget https://s3.amazonaws.com/Minecraft.Download/versions/1.12.2/minecraft_server.1.12.2.jar
```

起動

```
java -Xmx1024M -Xms1024M -jar minecraft_server.1.12.2.jar nogui
```

初回起動時はeulaの同意が必要です。(eula.txtをeula=trueに変更)
変更後もう一度起動コマンドを実行します（この時にserver.propertiesファイルが生成されます）。

`stop`コマンドでminecraftサーバーを停止して設定に進みます。

### 設定
motdの値を書き換えて、どちらのサーバーに接続しているかわかりやすくします。
**minecraftマシンのみ**
firewall設定をします。

```
[root@minecraft01 server]# firewall-cmd --add-port=25565/tcp --zone=public --permanent
success
[root@minecraft01 server]# firewall-cmd --reload
success
```

server.propertiesを編集して`motd`の値を`minecraft machine`に変更します。

**nginxマシンのみ**
minecraftサーバーのポートを25566番ポートに変更します。
server.propertiesを編集して`server-port`の値を`25566`に設定します。

server.propertiesを編集して`motd`の値を`nginx machine`に変更します。

## ホストOSから接続してみる
ここまでで図で示したものが出来上がっているはずです。
minecraftクライアントから接続してみましょう。

↓minecraftサーバー、nginxを起動した状態でminecraftクライアントから`localhost`に接続します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/d36987ef-baf8-414d-6fdc-9d41c4f4775d.png)

↓minecraftマシン上のminecraftサーバーが起動した状態だとサーバーの選択画面で`minecraft machine`の文字が見えるはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/15d1ef38-bed9-9ad1-2512-ee1ff80bba02.png)

↓minecraftマシン上のminecraftサーバーが停止した状態だとサーバーの選択画面で`nginx machine`の文字が見えるはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/a58630bc-8bb5-adcc-5645-8c5393042b59.png)

実際に中身のワールドも切り替わっています。

このような構成にすることで、**外から見ると**minecraftサーバーを停止せず（詳しくは説明しませんが、pingパケットがちゃんと処理されるということです）にメンテナンスをすることができます。
また、バックアップサーバーのほうに看板等でメッセージを残すこともできます。

# まとめ
nginxをTCPリバースプロキシとして使用する一例としてminecraftサーバーを構築してみました。
個人的に自宅サーバーを立てるときにリバースプロキシは有用だと思っているのでまとめてみました。
参考にしてもらえたら幸いです。