🛣️

CloudFlare Tunnelで自宅サーバーを公開する(FreeプランOK)

2022/02/04に公開約4,700字

まえがき

前提

  • 自宅サーバーでいくつかのサービスを動かしていて、それをインターネットに公開したい
  • IPoE (DS-Lite or MAP-E) を使っているのでパブリックIPv4アドレスを持っていない[1]

制限事項

  • Cloudflareのネームサーバーを使っているドメインでしかこの方法は使えない[2]
  • ドメインを移管する必要はない[3]が、DNSはCloudflareに向けておく必要がある

メリット

  • SSL証明書はcloudflareが自動的に発行してくれる。発行元がcloudflareになる。
  • SSL証明書の更新作業は不要。cloudflare側で行われる。
  • IPv6に自動的に対応。トンネルがIPv4であってもIPv6のIPアドレスも自動的に割り当てられる。逆にいえばIPv6のトラフィックが流れてきてしまう(設定でIPv6はオフにもできる)
  • ngrokのように、ローカルのアプリケーションをかんたんに一時的に公開するのにも使える(本記事のスコープ外)

デメリット

  • ネームサーバーをcloudflareに向けていないと使えない

手順

前提として、cloudflareにサイト(ドメイン)が登録されている状態であること
難しそうな話がでてくるが、普通に Free プランで使える。

Zero Trust の設定画面を起動する

なんというか…すごい難しそうなことが書かれているが、通常のダッシュボードと別のところにあるからリンクをたどってください。という話。
初回は、Teamの名前を決めろみたいな質問が表示されるので適当に名前を入れる。[4]

Tunnelの設定

左側メニューの Access -> Tunnels にある。
初回は Create Tunnel のTutorialがあるのでそこに従うと楽…ですが。
複数のサイト(ここでいうサイトはホスト名の数)を公開したい場合は、設定ファイルをTutorialで表示されるものから変更する必要があります。

ちなみに、手順はLinux環境で作っています。Windows等でもおそらくほとんど同じだと思いますが、適宜読み替えてください。

手順1 cloudflared をインストール

省略。 (github)[https://github.com/cloudflare/cloudflared/releases] からバイナリを取得してインストール。 .debなら dpkg -i 、普通のバイナリなら展開して chmod + 等してください。
ちなみに、普通のバイナリを適当において実行すると自動アップデート機能があるみたいです。[5]

手順2 cloudflared でログイン

cloudflared tunnel login

ブラウザが開くが、そういう環境ではない場合は表示されたURLに別のマシンからアクセスして認証する。認証が終わるとブラウザにこのウィンドウは閉じて良い。というメッセージが表示されるので閉じる。

手順3 トンネルを作る

cloudflared tunnel create すきな名前

トンネルは再生成しないので(しても良いんだけども)本番用の名前で作って大丈夫。
とてもかんたん。ちなみにサイトが複数あってもトンネルは一つで大丈夫。
ここで表示されたTunnel ID が内部的なホスト名に使用される。
ちなみに、この時点で設定ファイルは ~/.cloudflared に作成される。

手順4 設定ファイルを作る

ここが一番書きたかったところ。Tutorialで表示される設定ファイルは1サイト用なので複数サイトの場合は書き方が異なる。 ファイル名は ~/.cloudflared/config.yaml とした

# url: http://localhost:80
tunnel: 1234567890-9876-1234-abcd-abcdef85cd7d
credentials-file: /etc/cloudflared/1234567890-9876-1234-abcd-abcdef85cd7d.json

ingress:
 - hostname: www.example.com
   service: http://localhost:80
 - hostname: blog.example.com
   service: http://localhost:80
 - hostname: storage.example.com
   service: http://localhost:80
 - service: http_status:404

1行目 url Tutorialではこの項目があるが、複数サイト使う場合はこの項目は書かない
2行目 tunnel 先ほど作成したtunnelのIDを入れる。
3行目 credentials-file ログインした再に作成されたファイルのパス。 ~/.cloudflared/tunnel-id.json にあるはず。この例は後々の手順まで終わった後なのでパスが異なっている
6行目 ingress ここの下にサイト(ホスト名+ドメイン名)とプロキシするURLを記述する
7行目 hostname ホスト名+ドメイン名。 ドメイン名はcloudflareに登録されているものなら指定できる。
8行目 service cloudflaredを実行しているサーバーからアクセスできるURL
(hostname , service を必要な数だけ繰り返す)
13行目 service: http_status:404 最後はこの行を入れる必要がある(入れないとcloudflaredが起動しない)

手順5 cloudflareのDNSにトンネルを登録する

cloudflared tunnel route dns トンネル名 ホスト名+ドメイン名(blog.example.com)

これでcloudflare内のDNSが更新される。
具体的には CNAME <tunnel-id>.cfargotunnel.com が指定される。
逆に言えば、このレコードを手動で作って上記configにそのホスト名+ドメイン名が書いてあれば[6] 自動的にトンネルされる。

手順6 サービス登録

cloudflared service install

これでsystemdのサービスが登録される。 多分enableにはなっていないので、別途
systemctl enable cloudflared は必要なはず。
なお、インストール方法によっては、 cloudflared-update.servicecloudflared-update.timer も作成されている。 cloudflared-update.timerをenableにすると自動アップデートが可能。

蛇足

接続元ポートをhttpヘッダに入れてもらう

実は書きたかったことその2。

DS-LiteやMAP-EなどのキャリアグレードNATがされている環境の場合、IPv4アドレスだけをログに出力しても接続元を特定することができません。 アクセスログには接続元ポート番号も必要です。
…が。 Cloudflareは標準でCF-Connection-IP というヘッダにIPアドレスはセットしてくれますが、ポート番号はセットしてくれません。(CF-Connection-Port とか作ってくれませんかね?)
そこで、Cloudflareの変換ルールを使って任意のヘッダにポート番号をセットしてもらうことができます。

変換ルールは、ドメインごとに設定が必要です(内容は同じでOK)
※ Zero Trustの設定画面ではなく、いつも見ている設定画面の方にあります。

変換ルールを作成ボタンHTTPヘッダーを修正 から作成します。

入力内容:

  • 名前 Add X-cloudflare-Connecting-Port (何でもOK)
  • 受信リクエストが一致する場合.. フィールド: ホスト名
  • 受信リクエストが一致する場合.. オペレータ: 次を含む
  • 受信リクエストが一致する場合.. 値: . (ピリオド一つ。すべてのリクエストにマッチさせたい)
  • 実行内容.. Set Dynamic
  • 実行内容.. ヘッダー名 X-cloudflare-Connecting-Port
  • 実行内容.. 値 cf.edge.client_port

設定した場合の画面はこちらです。

https://community.cloudflare.com/t/need-cf-connecting-port-for-french-law/319505/7

あとは、nginx等の設定で、ログに X-cloudflare-Connecting-Port を出力するようにすればOKです。

nginxの場合の例 (LTSV出力)

    log_format apm 'time:$time_iso8601\t'
                   'http_host:"$host"\t'
                   'remote_addr:$http_cf_connecting_ip\t'
                   'remote_port:$http_x_cloudflare_connecting_port\t'
		   
    access_log  /var/log/nginx/access.log apm;

別解

  • VPNを使ってレンタルサーバー等と接続すれば実現できる。
  • レンタルサーバーと言っても、Oracle CloudのAlways Free枠で十分なのでこれならお金はかからない。
  • 有料のレンタルサーバーでも Vultrとかlinodeのミニマムなプランで十分なので$5/月 くらい
  • VPNの設定も https://tailscale.com/ を使えば楽にできる
脚注
  1. 語弊がある表現だけれどもとりあえず無視して ↩︎

  2. Google Domainsのネームサーバーを使っているドメインで実験したが、応答しないようになっていた ↩︎

  3. namecheapで管理していて、DNSだけcloudflareなドメインでも正常に動いた ↩︎

  4. なお、この名前は少なくともTunnelを使うだけなら表に出ることはないと思われます。 ↩︎

  5. が。自動アップデートがかかるとその間トンネルが切断されるので良し悪し… ↩︎

  6. cloudflaredが実行中な場合、設定ファイルを書き換えたらcloudflaredの再起動も必要です ↩︎

Discussion

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