☁️

Cloudflaredでおうちサーバを公開する

2022/12/19に公開約4,000字

はじめに

自宅にあるサーバを外部に公開するとき、ルータでポート開放してポートフォワーディングして… といった手間だったり、可変IPでDNSの設定がしづらかったり、また自宅のIPアドレスが丸見えになってしまって格好悪かったり、様々な問題がある。

しかし、それらを一挙に解決してくれるサービスをCloudflareが提供しているという噂を聞いたので、試してみることにした。

しかも無料アカウントでもOK

前提条件

  • Cloudflareのアカウントを持っていること
  • 独自ドメインを持っていて、ネームサーバをCloudflareに向けられること
  • 公開したいサービスが動いていること

検証環境

  • Raspberry Pi 4B with Raspberry Pi OS Lite 64bit
    Linux misskey 5.15.61-v8+ #1579 SMP PREEMPT Fri Aug 26 11:16:44 BST 2022 aarch64 GNU/Linux
  • Misskey (分散型SNS、ポート8080/TCPで待ち受け中)
  • Nginx(80/TCPを127.0.0.1:8080/TCPへフォワード)

Misskeyの構築手順については割愛する。

Cloudflaredの導入

Cloudflaredというデーモンが直接CloudflareのCDNに繋がるので、間にルータ等があったとしても難なく突破して外向きサーバを立てることができる。

ざっくり構成図

ざっくり構成図

Cloudflaredのインストール

Cloudflaredの導入には複数の方法があるが、今回はパッケージリポジトリを導入し、パッケージマネージャからインストールする手法を取ることにした。

Cloudflared Packages より、自分の環境に適したリポジトリを選択し、導入する。
(親切にコマンド例も載っているので、その通りにすると導入までストレートに終わる)

今回使用している Raspberry Pi OS は Debian 11 (bullseye) ベースのため、Bullseye用を用いた。

Cloudflareとの接続(認証、証明書の取得)

続いて、手元のCloudflaredとCloudflareのアカウントを紐づける操作を行う。

シンプルに cloudflared login を実行する。

$ cloudflared login
Please open the following URL and log in with your Cloudflare account:

https://dash.cloudflare.com/argotunnel?callback=https%3A%2F%2Flogin.cloudflareaccess.org%2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%3D

Leave cloudflared running to download the cert automatically.

2022-12-18T11:49:21Z INF Waiting for login...

表示されたURLにアクセスすると、Cloudflareへのログイン操作の後「どのアカウント(ドメイン)に接続しますか?」と聞かれるので、目的のドメインを選択する。

すると、自動的に認証から証明書のダウンロードまで行われる。

You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
/home/user/.cloudflared/cert.pem

$ 

サーバの作成

続いて、Cloudflare側のサーバを作成(登録?)する。

$ cloudflared tunnel create server
Tunnel credentials written to /home/user/.cloudflared/UUID.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.

Created tunnel server with id UUID

ここでUUIDが発行されるため、控えておく。この後何度も使用する。

設定ファイルの作成

Cloudflaredで用いる設定ファイルを作成する。

便宜上、jsonファイルも/etc/cloudflaredディレクトリへコピっている。

# ディレクトリがない場合は作る
$ sudo mkdir -p /etc/cloudflared

$ sudo cp ~/.cloudflared/UUID.json /etc/cloudflared
$ sudo vim /etc/cloudflared/config.yml
config.yml
tunnel: UUID
credentials-file: /etc/cloudflared/UUID.json

ingress:
  - hostname: misskey.objektiv2.net
    service: http://localhost:80
  - service: http_status:404
  • misskey.objektiv2.net宛に来た通信はhttp://localhost:80に流す
  • それ以外は404を返す

この時点でお気づきかもしれないが、名前ベースのリバースプロキシのような動きをするため
1台のCloudflareゲートウェイで、ローカルの複数サービス/ホストへフォワードできてしまう。

自動的にサブドメインを作成し、CNAMEを設定する

$ cloudflared tunnel route dns UUID misskey.objektiv2.net

この操作をすると、Cloudflare上で自動的にCNAMEレコードが作成される。

自動的にCNAMEが作成される

Cloudflaredをサービスとして動かす

パッケージマネージャからインストールすると、自動的にsystemdに組み込まれるようだ。

$ sudo systemctl enable cloudflared
$ sudo systemctl restart cloudflared

これで終わりです

設定したドメインにブラウザからアクセスしてみましょう。

GLHF!

Cloudflaredの罠

プロキシさせる場合、フォワード先のポート番号が限られる

HTTPやHTTPSでフォワードできるポートに制限があり、それ以外のポートを指定しても弾かれてしまう。

Cloudflare Docs - Network ports

よくある3000/TCPはリストにないためデフォルトでフォワードしてくれない。
リストにないポートを使用するには、Cloudflareによるプロキシを解除するか、別途Nginx等でリバースプロキシを立てる必要がある。

正確なアクセスログが取れない

間にNginx等のプロキシサーバーを噛ました場合、すべてのアクセスがローカルのCloudflaredからという扱いになるため
ログに記録されるアクセス元IPアドレスがが全て ::1127.0.0.1 になる。

解決方法

Cloudflareを通した通信のオリジンIPアドレスはCF-Connecting-IPヘッダに記録されているため、それをアクセス元のリアルIPアドレスとして使う設定が必要。

Nginxの場合、nginx.confhttpチャンクに、下記を追記する

/etc/nginx/nginx.conf
set_real_ip_from ::1/64;
set_real_ip_from 127.0.0.1/32;
real_ip_header CF-Connecting-IP;

Discussion

ログインするとコメントできます