Cloudflare Tunnelを利用したMinecraftサーバーの公開

:::note warn
この記事を書いたのはネットワーク初学者です。
ミスを見つけたら教えてください。
:::

自宅サーバーを公開する際には、基本的にルータのポートフォワーディング機能を利用して外部にポートを公開する必要があると思う。
しかし、集合住宅の共有回線を利用している場合など、自由にグローバルIPアドレスを使えなかったり、ルータの設定を変更できない環境もある。
この対策として**Cloudflare Tunnel**を利用し、Minecraftサーバーの公開を試す。

なお、Cloudflareに関しては無料サービスのみを利用してネットワークを構成する。

# 構成
ネットワーク構成としては下図のようになる模様。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/21c830bf-95b4-6250-1572-e8e837229804.png)

集合住宅内でポートフォワーディングが難しいため、図の下部のように**トンネルを用いてサーバー・Cloudflare間の通信を確立している点**が今回の趣旨。
トンネルの確立はcloudflaredというデーモンアプリケーションにより行われる。

また、クライアント側ではcloudflaredによりトンネル通信に加えてユーザ認証を行う。
この際、トンネル内部では暗号化通信が行われるため、認証情報の盗聴を防ぐことができる。
注意点として、ユーザはcloudflaredをインストールし、コマンド実行する必要がある。
（公式ドキュメントでは、コマンドのデスクトップショートカットをユーザに配布することを提案している。cloudflaredを使う以外にも接続のバリエーションがあるが、クライアント側のアプリケーションインストールが求められないのは有料のEnterprise版のみらしい。）

## 実験環境
- Client OS：Windows 11
- Server OS：Ubuntu 22.04.4 LTS
- Minecraft Server：Spigot 1.20.6

# 事前準備
## Minecraftサーバーの構築

Minecraftサーバーの構築方法に関しては情報が豊富であり、この記事の趣旨とは外れるため割愛する。
筆者はこちらの記事を参考に構築を行った：
https://qiita.com/Nether_2046/items/8b3ad9208ab1e700ef07

サーバーを構築したらLAN内で接続テストを行い、サーバーが正常に機能していることを確かめておく。

## ドメインの取得
Cloudflareを利用する際に必要となるため、ドメインを持っていない場合は取得する。
私は可能な限りコストを抑えたかったので、1年使うだけならタダ同然＆whois情報公開代行が無料であるXServer Domainを利用した。

# Cloudflare
## Cloudflareとドメインの紐付け
Cloudflare上でドメインを利用するため、取得したドメインをCloudflareに紐づける。
Cloudflareにログイン後、サイドバーの一番上にある「Webサイト」をクリックすると以下のページに遷移する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/3f5be509-f1f7-519e-2bff-7ad2534a84ae.png)

以降、画面の指示に従いドメインを紐づける。
ドメインの紐付けが完了すると、「Webサイト」画面からドメイン管理ページを開くことが可能になる。

## DNSサーバーの登録
Cloudflare上で名前解決を行うため、DNSサーバーの登録を行う。
ドメイン管理ページのサイドバーにある「DNS」をクリックすると、CloudflareのDNSサーバーを確認できる。

![image.p
ng](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/608a2c0c-8b4c-8784-1c4f-46a963f08d88.png)

これらをドメインを取得したドメインサービスに登録する。
XServerの場合、管理画面の「ネームサーバー設定」から以下のような形で登録することができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/93aa6f43-b113-a677-48d0-4442e88e6db5.png)

これでCloudflareにおいて名前解決を行うことが可能となる。

## サーバー・Cloudflare間のトンネル作成
Cloudflareのダッシュボードから「Zero Trust」>「Networks」>「Tunnels」と遷移すると、以下のトンネルの作成画面に辿り着く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/bd724351-f10c-7695-7d3f-eea35944d614.png)

connectorにはCloudflaredを選択する。
次の画面でトンネルに名前をつけるとトンネルが作成される。
ちなみにトンネルは1つのサーバーにつき1つなので、そのサーバーを示すような名前だと分かりやすいかもしれない。

### cloudflaredのインストールとトンネル接続
トンネルを作成すると、このような画面に遷移する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/e15ece16-63ee-667c-c528-ef45ba1babfa.png)

該当する環境を選択するとコマンドが表示されるため、これをコピーしてサーバー上で実行する。
成功すればConnectorsに接続情報が表示される。

![スクリーンショット 2024-05-11 174403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/97d6b3e7-8b3a-2092-aea3-3388a38ebf49.png)

### ホスト名とサービスを設定
設定項目は以下の通り。

- Subdomain, Path
自由に設定
- Domain
先ほどcloudflareに紐付けたドメイン
- Type, URL
プロトコルとプライベートIPアドレス、ポート番号を指定。
Minecraftの場合、デフォルトではTCPの25565ポートを利用する。

![スクリーンショット 2024-05-11 174647.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/bfc53e8e-1f7d-7784-9b37-546b326b2e41.png)

これでトンネルの作成が完了し、以下のように作成したトンネルが動作していることが確認できる。 

![スクリーンショット 2024-05-11 180343.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/103519c0-e178-46a1-ee2e-e169f68928da.png)

またトンネルの作成と同時に、登録したサブドメインからトンネルに解決するDNSレコードが自動的に作成される。
こちらは先程DNSサーバーを確認したページ（ドメイン管理ページから「DNS」＞「レコード」）で確認できる。

![スクリーンショット 2024-05-11 180950.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/52d9b915-bb9e-950c-24de-8d3902f4bdc8.png)

[^dns-record]: CloudflareのプロキシはHTTPとHTTPS以外のTCP通信を弾いてしまう、という情報がネット上で散見されたが、今回はプロキシを解除せずに通信することが可能だった。これは、今回の設定においてはクライアントがcloudflaredを利用した通信を行うことにより、データがHTTPヘッダでカプセル化されていたため？ 参考：https://developers.cloudflare.com/fundamentals/reference/network-ports/

## Access アプリケーションの作成
許可したユーザのみがサーバーにアクセスできるようにするため、Cloudflare Accessを利用する。[^access]
Cloudflare Accessを使うには、Zero Trust > Access > Applications > Add an appllication からAccess アプリケーションを作成する。

[^access]: 試した結果、Accessアプリケーションを設定せずに通信することは不可能だった。しかし、WebサーバーであればAccessアプリケーションがなくとも公開できることが確認できた。HTTP & HTTPSを除いた通信には認証が必要らしい。

### アプリケーション設定
- Application name
自由に
- Application domain
トンネル作成時に指定したホスト名を入力

![スクリーンショット 2024-05-11 183012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/ded044a5-6f96-60f8-fd1c-4d4c14e9e825.png)

他の項目にこだわりがなければ次へ。

### ポリシー設定
サーバーに接続できるユーザを制限する。
例えばEmailを入力することで、Emailアドレスを所持するユーザがログインできるようになる。
なおユーザが一度ログインすると、Session durationに指定した時間が経過するまで再認証せずにログインすることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/a9c2ca95-a2b2-2772-d248-ab9add31b31d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/d18eff10-fe08-144f-439e-a5ed3bdaf02f.png)

次のセットアップページに関しては追加設定なので、気になる項目がなければ変更せず完了。
すると、以下のようにアプリケーションが追加されたことを確認できる。

![スクリーンショット 2024-05-11 190208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/1cf6c49e-070f-ab88-3387-537c20ad9b3a.png)

# クライアント側の設定
MinecraftのようにHTTP以外の通信を行う場合、クライアント端末にもcloudflaredを利用する必要がある。[^client]
cloudflaredのインストールは以下のページから行える。
https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/

[^client]: HTTPの場合はCloudflareのTLS証明書で暗号化されるため、おそらくトンネルで暗号化を行う必要がない。https://developers.cloudflare.com/cloudflare-one/applications/non-http/arbitrary-tcp


## cloudflaredの起動
サーバーに接続する前に、クライアントはターミナル上で以下のコマンドを実行しておく。
なお、urlに含まれるポート番号は自由に設定してよい。
```
cloudflared access tcp --hostname subd.example.com --url localhost:20100
```
これによりクライアントは自身の20100ポートを介してsubd.example.com（minecraftサーバー）と通信ができるようになる。

## サーバーへの接続
いよいよMinecraftクライアントからサーバーに接続する。
「マルチプレイ」＞「サーバを追加」から以下のように設定する。
なお、サーバー名は任意、サーバーアドレスはcloudflared起動時にurlとして指定したものとなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1635006/8a797d45-92bb-1a0d-7979-cca106b1974d.png)

次に、設定したサーバーへの接続を試みると、ブラウザで認証画面が表示される。
表示されない場合はターミナルにURLが出力されているため、これを辿って認証を行う。
認証が完了したらサーバーへの接続が可能となる。

# 参考
https://qiita.com/KeioCF/items/1cf77ddd77dc143c2a47#%E6%93%8D%E4%BD%9C
https://qiita.com/harutiro/items/885c0df9662a0c6e6355