【mod_proxy】Dynmapをhttpsで見る

# やりたいこと
タイトル通り、Dynmapをhttpsで見たい。

# 前提
DynmapはMinecraftのサーバプラグインで、ゲームの内部状況をブラウザから確認できるようになる、人気のプラグインです。

― [【Dynmap】Overview](https://dev.bukkit.org/projects/dynmap)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267005/ff4525c3-9ce6-f9c2-195d-edb8b01902e2.png)




普通に実装するなら、ポート周りはこんな感じになります。
<img width=600 alt="server-image.png" src="https://qiita-image-store.s3.amazonaws.com/0/267005/0eb3f36c-0a54-adcb-a882-51f871b4692c.png">


| ソフトウェア | ポート番号 | 通信プロトコル |
|:-:|:-:|:-:|
| Apache | 80, 443 | http, https |
| Minecraft server | 25565 | 独自プロトコル |
| Dynmap | 8123 | http |

大雑把な図ですが、おおよそこのような関係になります。ポート番号はデフォルト値です。

問題はDynmapです。Dynmapは内部にスタンドアローンなWebサーバを内蔵していますが、Apacheとの競合を防ぐため、8123番ポートを利用します。そのため、サーバは80, 443, 25565の他に**8123番ポートも開放**しなければなりません。

他のポートは開放する他ありませんが、Dynmapのポートは開放したくありません。
もっといえば、httpsで通信したいです。

さて、どうしましょう。

# リバースプロキシ（Reverse Proxy）を使う
リバースプロキシを使うことで解決できます。
リバースプロキシとは、外部ネットワークの接続に対し、何らかの処理を行い、内部サーバに引き渡す、いわば中継機能を果たす機器のことです。

とても分かりやすい図があったので、お借りしました。

>
![image.png](https://qiita-image-store.s3.amazonaws.com/0/267005/52b1f176-9525-bdf3-235e-7185bbde261d.png)
― [【ITSakura】プロキシサーバとリバースプロキシサーバの違い](https://itsakura.com/network-proxy)

リバースプロキシが外部の受付役となっていることが分かりますね。今回、ハード的にサーバは1つですが、やることは同じです。
Apacheに対するリクエストはApacheに、Dynmapに対するリクエストはDynmapにそれぞれ内部で振り分けることで、開放ポートを減らすことができます。

# リバースプロキシ適用
リバースプロキシを使った図が以下になります。（8123番ポートはイメージです）
<img width=600 alt="reverse-proxy" src="https://qiita-image-store.s3.amazonaws.com/0/267005/5b40307e-a9cd-f21a-56ae-737efd903fc1.png">

リバースプロキシはApacheに備わっている機能なので、リバースプロキシはApache内部にあります。
リバースプロキシが受け取ったリクエストのうち、Dynmapに関するものだけ8123番ポートに向けて、リクエストを送ります。
Dynmapの応答はリバースプロキシを通じて、外部に送信されます。

このように、リバースプロキシを使うと内部サーバを隠蔽し、無用なポート開放を防ぐことができます。

## リバースプロキシの実装
Apacheであれば、mod_proxyを使って簡単に実装できます。
今回はhttpsの通信に対して実装するので、`VirtualHost`を使います。（https化はすでにされているものとします）

```title:ssl.conf
...
# スラッシュ補完（後述）
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_URI} /dynmap$
RewriteRule /* %{REQUEST_URI}/ [R=301,L]
</IfModule>

# リバースプロキシ
<VirtualHost *:443>
ServerName sample.com:443
<IfModule mod_proxy.c>
  ProxyRequests Off
  ProxyPass /dynmap http://localhost:8123
</IfModule>
...
</VirtualHost>
```

`ProxyRequests`はフォワードプロキシをオンにするかどうかの記述なので、今回はoffにします。

`ProxyPass`の使い方は、
>ProxyPass <外部からのリクエスト> <内部サーバ先>

です。非常にシンプルですね。
`sample.com:443/dynmap`へのリクエストは内部の`localhost:8123`に投げるようにしています。
これで、`https://sample.com/dynmap`にアクセスすると、Dynmapの画面が表示されます。

これでポートを開放をせず、外部からhttpsを使ってDynmapにアクセスすることができました。

## 補足：スラッシュ補完
リバースプロキシの前にmod_rewriteモジュールを使ってスラッシュ補完を行っています。
つまり、
`https://sample.com/dynmap` を`https://sample.com/dynmap/`
に書き換えるということです。

スラッシュ補完をしないと、真っ白な画面が表示されてしまいます。
一方、`https://sample.com/dynmap/`や`https://sample.com/dynmap/#`であれば、正しく表示されます。

ではなぜ、スラッシュ`/`を保管しなければならないのか。
これは推測なのですが、DynmapのWebサーバの**ルートディレクトリを明示する必要**があるのでは？と考えています。

通常、ドメイン名のみでアクセスするとき、ブラウザはスラッシュを補完してサーバにリクエストを投げます。
URLバーが`https://sample.com`であっても実際は`https:/sample.com/`としてリクエストを送っているということです。
最後のスラッシュがルートを示しています。

ですが、今回の場合リバースプロキシを経由しているので、スラッシュ補完が行われません。
ディレクトリを一切指定されなかったWebサーバは、困って真っ白な画面を返したのではないでしょうか。

何か詳細あれば、教えていただきたく思います。

# Minecraftサーバのポートもリバースプロキシ経由にできないのか？
ごもっともだと思います。
ですが、Minecraftが扱っているプロトコルはゲーム独自のものです。
mod_proxyモジュールでは対応できないプロトコルなので、今回の例のようにはいきません。

ですので、Dynmapのみリバースプロキシを適用することにしました。

# まとめ
どうでしたか？
他のマルチサーバをのぞくと、`http://xxxxxx:8123`でアクセスしているものがいくつかあったので、記事にしてみました。

ポートを閉じるためにも、httpsで通信するためにも、リバースプロキシを使ってみてはいかがでしょうか。

# 参考リンク
- [【ITSakura】プロキシサーバとリバースプロキシサーバの違い](https://itsakura.com/network-proxy)
- [【Dynmap】Overview](https://dev.bukkit.org/projects/dynmap)
- [【エディケーション】リバースプロキシ](https://f5.com/jp/education/glossary/glossary020-21502)
- [【Apache】Apacheモジュール mod_proxy](https://httpd.apache.org/docs/2.4/ja/mod/mod_proxy.html#proxyrequests)![undefined]()
- [【海外SEO情報ブログ】URLの最後に付ける「トレイリング スラッシュ」ありなしの違いはSEOに影響するのか？](https://www.suzukikenichi.com/blog/differences-between-trailing-slash-and-non-trailing-slash/)