Cloudflare Tunnelで自宅サーバーを公開する(FreeプランOK)
2022/12/15 追記
手順の変更
以前の記事との違いは、
- Argo Tunnelという名前がなくなった
- cloudflaredの設定にトンネル先を入れていたが、それはCfのWebで入力したものをリアルタイムに反映するようになった。ようするに設定値はトークンだけ。
Tunnelの作成
これで自宅内LANとCfの間のVPN(のようなもの)を確立させる。
ここでインストールする cloudflared
が動作していないと公開できないので注意
- Cloudflare(以下Cf) のダッシュボードにログイン
- 左側メニューから
Zero Trust
をクリック - 左側メニューの
Access -> Tunnels
をクリック - 右上、
Create a tunnel
をクリック - トンネルの名前(何でもよい)を入れて次へ
-
cloudflared
を動かすための情報が表示されるのでどれかを実行。一番楽なのは docker -
Save
ボタンを押してTunnelの作成を一度完了させる
Public HostnameとLAN内エンドポイントの紐付け
ようするに、 myapp.example.comにアクセスされたら http://192.168.1.1 を返す。みたいな設定を入れていく。
- 左側メニューの
Access -> Tunnels
をクリック - 先程作ったTunnel名の右側にある
Configure
をクリック -
Public Hostname
をクリック -
Add a public hostname
ボタンをクリック - (1行目 Public hostname と書かれた行の話。インターネットから見える名前の設定)
-
Subdomain
に好きな名前 (例文の場合だと myapp) を入れる -
Domain
はCfに登録されているドメインが列挙されるのでその中から選択する - (ここで
Warning: No DNS record found for this domain. The policy may not execute as expected.
と表示されるが無視してよい) -
Path
はあれば入力する(myapp.example.com/app -> 192.168.1.1/app になると思われる) - (2行目 Service と書かれた行の話。自宅LAN内のどこにトンネルするかの設定)
-
Type
はWebならHTTPになるはず。選択肢から適切なのを選ぶ -
URL
にLAN内でのURLのhttp:// を除外した部分を入れる。 (例: 192.168.1.1:3000) -
Save Hostname
ボタンを押す
これで、myapp.example.com にアクセスすると、LAN内のWebアプリが見えるはず。
ちなみに、自動的にCfの証明書が発行され、httpsアクセスになる。
以前の手順より遥かに簡単になった。すごい。
蛇足
インターネットからアクセスしたいけど自分だけ使いたい
上記の設定に加えて以下を行うことで可能。(FreeプランOK)
先程作成した、 myapp.example.com として説明する。
- Cloudflare(以下Cf) のダッシュボードにログイン
- 左側メニューから
Zero Trust
をクリック - 左側メニューの
Access -> Applications
をクリック -
Add an application
ボタンをクリック -
Self Hosted
をクリック - (1行目)
-
Application name
は好きな名前を入れる - (2行目 Application domain)は先程作成済みのものを入力する
-
Subdomain
は myapp を入力 -
Domain
は example.com を入力 -
Path
は入力していれば入力(今回の例であれば空欄) - 右上の
Next
ボタンを押す - (誰にアクセスを許可するかの設定)
-
Policy name
に適当な名前をいれる。 onlyme とか - 下の方にある
Create additional rules
に あるSelector
をEmails
にする -
Value
欄にあなたのメールアドレスを入力 - 右上の
Next
ボタンを押す - 右上の
Add Application
ボタンを押す
再度、 myapp.example.com にアクセスすると以下のような画面でログインを要求される。
ここにメールアドレスを入力すると認証コードが送られてきて(メール内にリンクもある)ログインするとLAN内のWebアプリが表示できる。
このポリシー周りの設定は色々できるようなので研究のしがいがありそう。
この手順では適当にメールアドレスだけで認証しているけれども、ここでYubicoとかが使える…と思われる。
ちなみに、毎度毎度認証が必要になるわけではなく、普通のセッションと同じように一定時間内に使い続けていれば再認証は不要なようだ(デフォルトでは24時間以内)
追記の蛇足
- Type欄、SMBとかSSHとかがあるので色々とすごいことができそう
まえがき
前提
- 自宅サーバーでいくつかのサービスを動かしていて、それをインターネットに公開したい
- IPoE (DS-Lite or MAP-E) を使っているのでパブリックIPv4アドレスを持っていない[1]
制限事項
メリット
- 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のcloudflaredリポジトリ からバイナリを取得してインストール。 .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.service
と cloudflared-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
設定した場合の画面はこちらです。
あとは、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/ を使えば楽にできる
Discussion