ポート開放ができない環境でサーバを外部公開するための個人的ベストプラクティス

:::note warn
警告
※記事は以下のサイトに移動しました
:::
https://bigbell.dev/posts/linux/2023-07-07/

<!-- ## はじめに
この記事は DeNA 23 新卒 Advent Calendar 2022 の11日目の記事です．


https://qiita.com/advent-calendar/2022/dena-23-shinsotsu

こんにちは DeNA23 卒内定者の bigbell です．  
自分は[大学の研究室](https://kajilab.net/)でネットワークとサーバの運用・保守を担当しているのですが，**プライベートIPしか振られていない環境でサーバを外部公開**する必要があり，その中で得られた知見があるので，同じ悩みを抱えた人のためにも今回初めて記事を書いてみようと思います．  

説明が間違っていることもあるかも知れませんがその点はご容赦ください


## 経緯とか
私の研究室では後輩が[官公庁オークション](https://kankocho.jp/)で手に入れてきたラックマウントサーバ（ラックにマウントするとは言ってない）を使用して，入退室管理システムやファイルサーバ，研究で使用する自作ソフトウェアを動かしています．  


最近ではAWSやGCPといったクラウドサービスがあるのに「なぜ今の時代にオンプレ環境？」と突っ込みたい人もいるかも知れませんがこれには理由があります．  
実はこの環境に移る前はAWSのEC2上に無料枠でサーバを建てて，研究室内のシステムを動かしていました． 
 
しかし，無料枠の範囲内ではサーバのスペックが足りず，自作のソフトウェアを実行するとまともにSSHすら繋がらなくなってしまう有様でした．  
この問題を受けて，当初は幾らかお金が掛かるのには目をつぶって運用しようかという話にもなったのですが，「研究室内でそんなに使われていないシステムを動かすのに金銭的コストを掛けるのもどうなの？」という意見を受けて白紙になりました．  
「う〜ん...運用どうしよう」と考えていた所，ちょうど後輩が官公庁オークションで上記のラックマウントサーバを手に入れてきたので，こちらを使用したオンプレ環境に移行したという形です．


### 直面した問題
サーバを手に入れて，よし！これで問題は解決！！と言いたい所でしたが，サーバを外部公開するに当たって別の問題に直面しました．  
各研究室には大学側が設置している上流のルータを通してプライベートIPしか振られておらず，基本的に自前で用意したサーバを公開することはできないのです．  
大学によっては情シスの人に頼めば良い感じにルータを設定してくれて，外部公開できる所もあるかと思いますが私の大学ではそれは厳しそうでした．

上述の問題の解決策として，よくQiitaでは [ngrok (エングロック)](https://ngrok.com/) のサービスをした方法が紹介されています．  
このサービスは ngrok のサーバに対してトンネルを貼ることで，外部からの通信を中継してくれるというサービスです．  
簡単に言えばリバースプロキシを楽に使えるようにしたようなもので，プライベートIPしか振られていないような環境でもインターネットにさえ繋がっていればサーバを外部に公開することができます．

ちなみに公式では以下のように説明されています
> ngrokは、NATやファイアウォールに囲まれたローカルネットワークのサービスを安全なトンネルを介して公衆インターネットに公開します。ローカルウェブサイトの共有、Webhookコンシューマの構築/テストWebhook コンシューマーの構築/テスト、個人サービスのセルフホスティングなどが可能です。

**このように非常に便利なサービスですが無料枠で使用する場合は以下の制限があります**

- 独自のドメインを割り当てることができず毎回URLが変わってしまう
- 帯域幅と転送量にリミットがある

そこで，より低コストで利用できるサービスがないか探した所，[Cloudflare社が提供しているサービス](https://github.com/cloudflare/cloudflared)を見つけました．  
**このサービスを利用すると [ngrok](https://ngrok.com/) の有料プランに該当する機能をコストを掛けずに利用できます！！**
(*※ただし，無料プランでは1回にアップロードできるファイルサイズは100MB以下という制限だけ存在します*)

## Cloudflare のサービスを使ったサーバの外部公開方法
アカウントの作成とCloudflareへのドメインの登録が済んでいることを前提として進めていきます．

### 1．cloudflared のダウンロード
以下のURLを参照して各自の環境に合わせた最新のバイナリファイルを選択してください．  
https://github.com/cloudflare/cloudflared/releases/latest/

```shell-session:zsh
$ wget https://github.com/cloudflare/cloudflared/releases/download/2022.10.3/cloudflared-linux-amd64
```

:::note info
`apt` や `yum` 等を利用してインストールすることもできますが，自動更新の機能を利用したいので，ここではバイナリファイルを直接ダウンロードしてインストールを行います．
:::

### 2．バイナリファイルの名前を変更して適切なパスに配置する
```shell-session:zsh
$ mv cloudflared-linux-amd64 cloudflared
$ sudo cp ./cloudflared /usr/local/bin/
```

### 3．実行権限を与える
```shell-session:zsh
$ sudo chmod +x /usr/local/bin/cloudflared
```

### 4．Cloudflare にログインする
以下のコマンドを実行して表示されたURLを任意のブラウザで開く  
ページが開いたら紐付けたいドメイン名を選択して **`承認ボタン`** を押す

:::note warn
警告
※URLを開くときはcloudflareにログインしている状態で開く必要があります．
:::

```shell-session:zsh
$ cloudflared tunnel login
```

### 5．トンネルを作成する　(この時点では設定をするだけでまだコネクションは貼られない)
:::note info
トンネルを作成すると **`~/ホームフォルダ/.cloudflared/`** 内にトンネルID等の認証ファイルが生成される
**`abcd1234-0000-0000-0000-000000000000.json`** の形式の名前のjsonファイルです．
:::

```shell-session:zsh
$ cloudflared tunnel create <任意のTunnelの名前>
```

### 6．トンネルIDとドメイン名を紐づける
一つ前の作業で作成されたJSONファイルを確認してトンネルのIDを把握しておく  
トンネルIDが分かったら以下のコマンドで **`トンネルID`** と **`設定したいドメイン`** を紐づける

```shell-session:zsh
$ cloudflared tunnel route dns <Tunnelのid> <(紐付けたいドメイン名)>
```

### 7．公開するポートと紐づけるドメインの設定
**`cloudflared`** という名前のディレクトリを作成し，その中に  **`config.yml`** という名前のファイルを作成する．
**`config.yml`** の中身は各自の目的に合わせて下の例のように記述する．

```shell-session:zsh
$ sudo mkdir /etc/cloudflared
$ sudo nano /etc/cloudflared/config.yml
```

#### config.ymlの内容
ここでは **`SSH`** と **`VNCサーバ`** を外部に公開する設定にしています．
他のポートを公開したい場合は各自任意の情報に書き換えてください．
```yml:config.yml
tunnel: トンネルのID
credentials-file:　絶対パス/.cloudflared/作成されたjsonファイルを指定

ingress:
  - hostname: <紐付けたいドメイン名>
    service: ssh://localhost:22
  - hostname: <紐付けたいドメイン名>
    service: tcp://localhost:5900
  - service: http_status:404
```

### 8.cloudflaredをサービスとして設定する (自動で起動するようにする)
```shell-session:zsh
$ sudo cloudflared --config /etc/cloudflared/config.yml service install
```

### 9.cloudflaredを起動する
```shell-session:zsh
$ sudo systemctl start cloudflared
$ sudo systemctl enable cloudflared
```

### 10.正常に起動しているか確認する
`Active` の欄が `active (running)` になっていればOK
```shell-session:zsh
$ sudo systemctl status cloudflared
```

```shell-session:上記コマンドの結果
🟢 cloudflared.service - Cloudflare Tunnel
   Loaded: loaded (/etc/systemd/system/cloudflared.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2022-10-27 12:29:48 JST; 3 weeks 0 days ago
 Main PID: 2976033 (cloudflared)
    Tasks: 30 (limit: 202791)
   Memory: 64.3M
   CGroup: /system.slice/cloudflared.service
           └─2976033 /usr/local/bin/cloudflared --config /etc/cloudflared/config.yml tunnel run
```

## 自動アップデートの設定
下記のコマンドで **`cloudflared.service`** を開き，ExecStartの行から **`--no-autoupdate`** の記述を削除する
```shell-session:zsh
$ sudo nano /etc/systemd/system/cloudflared.service
$ sudo systemctl daemon-reload
$ sudo systemctl restart cloudflared
```


## SSH接続できるようにするための設定
**<font color='red'>※クライアント側の設定です！</font>**
サーバ側のSSHポートを公開して外部からアクセスできるようにしたい場合は，以下の設定も必要になります．
**`ProxyCommand`** の行が肝です
```text:~/.ssh/config
Host ssh.example.com
	HostName ssh.example.com
	User hoge
	ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
```

### VNC接続するときのコマンド
```shell-session:zsh
$ sudo cloudflared access tcp --hostname vnc.example.com --url localhost:1234
```
コマンドを実行した後にVNC Viewer等で　**`localhost:1234`** に接続すると繋がります


## まとめ
この記事ではプライベートIPしか振られていない環境でも，Cloudflareのサービスの利用によってサーバを外部公開する方法を紹介しました．
近年ではAWSやGCPといったクラウドサービスが普及しているため，サーバの外部公開で困ることは少ないかも知れません．
しかし，研究活動の一環での利用となると、コストやその他諸々の事情でそれらの利用が難しい場合も
あります．
この問題を解決するための方法として本記事ではCloudflareのサービスについて紹介しました．

なお，記事中ではあまり触れていませんが，Cloudflareのサービスを使い出す前は[ngrok](https://ngrok.com/) のサービスも実際に導入し利用を検討していました．
こちらのサービスはコマンドを1つ叩くだけで指定したポート番号を外部に公開できるため，短期間だけデバックやデモとして公開したい場合には非常に便利なサービスです．
URLが固定じゃなくても問題ない場合にはまずは[ngrok](https://ngrok.com/)を試してみても良いかもしれません．
同様の問題を抱えている人にこの記事が参考になれば幸いです．

明日は@a5chinさんによる「数独・ナンプレを画像から解く AI を作る」という記事が公開されます．
お楽しみに！！

https://qiita.com/advent-calendar/2022/dena-23-shinsotsu -->