frp でセルフホスティング ngrok 風環境を作る

以前、ngrok というサービスを紹介した。ローカルマシン上で公開しているポートをインターネット上に公開できるというサービスだ。

ngrok はツール名であり、ウェブサービス名でもある。無料アカウントでもとりあえず HTTPS 公開ができるが、ngrok から付与される URL 文字列がランダムだったりして使い勝手が悪い。ngrok をセルフホスティングすることもできるが、ワイルドカード対応の DNS と SSL 証明書が必要で面倒臭い。

そこで、ngrok と同様の機能を提供してくれる frp というツールがあったので、コチラを使って、ローカル上のウェブアプリをインターネット公開してみる。

目次

仕組みおさらい

ngrok や frp がどういう仕組みで、何をしてくれるのか、改めておさらいしておこう。いわゆる「リバースプロキシ」の仕組みであるが、それがどういうモノかを押さえておこう。

使い方の一つは、ローカルマシンに SSH 接続するためのプロキシ的な役割で使う方法がある。

  1. 同じ LAN 内で、プライベート IP を指定すれば SSH 接続できる状態のパソコン A があるとする
    • このパソコンには Public IP が付与されておらず、直接 SSH 接続ができない
  2. パソコン A に、frp のクライアント用ソフトである frpc を導入する
  3. Public IP を持ち、インターネット上から接続できるサーバ B を用意する
  4. サーバ B に、frp のサーバ用ソフトである frps を導入する
  5. frpc と frps が通信を確立する
  6. 上2台とは全く別のネット環境から、サーバ B の Public IP を指定して SSH 接続すると、パソコン A に接続できる

こんな手順で、パソコン A には直接 SSH 接続できない場合に、サーバ B を中継して SSH 接続することができるようになる。今回はこの手順は紹介しないが、frp の公式 README で詳しく説明されている。

今回紹介するのは、手元のパソコン A で立ち上げたウェブサーバを公開する使い方だ。

  1. パソコン A 上で、開発用のウェブサーバを起動する
    • Express.js でも、Rails でも、Flask でも何でも良い。npm run dev で起動するような開発サーバであっても問題はない
    • http://localhost:8080/ などで対象のサーバにアクセスできるが、Public IP がないためインターネットからのアクセスはできない状態
  2. パソコン A に、frp のクライアント用ソフトである frpc を導入する
  3. Public IP を持ち、インターネット上から接続できるサーバ B を用意する
  4. サーバ B に、frp のサーバ用ソフトである frps を導入する
  5. frpc と frps が通信を確立する
  6. 上2台とは全く別のネット環境から、サーバ B の Public IP ないしはドメインにアクセスすると、パソコン A で起動しているウェブサーバの内容がレスポンスされる

やっていることは SSH 接続の場合とほぼ同じで、TCP 接続な点が違うぐらい。イメージ的には サーバ B がプロキシサーバ的なポジションに位置していて、サーバ B はユーザとパソコン A との通信を横流ししているだけである。

いずれも、Public IP を持っているサーバ B をフロントに立たせることで、Public IP がないマシンであってもインターネット上に露出させられるというワケだ。

インターネット上に公開されているサーバを用意する

ローカルホストをインターネット公開するには、当然ながら「インターネット上に公開されている = Public IP を持つサーバ」が必要になる。ngrok の場合は ngrok サービスが肩代わりしてくれていたのでコチラでサーバを用意する必要はなかったが、今回 frp を使うにあたっては、自分でサーバを用意する必要がある。

自分はかねてより、GCP (Google Cloud Platform) の GCE (Google Compute Engine) インスタンスや、OCI (Oracle Cloud Infrastructure) の Compute インスタンスを使っている。いずれも永久無料で利用できるクラウド VM で、Apache や nginx を導入してウェブサーバを立て、同じく無料の Freenom というサービスで独自ドメインを取得し、Let's Encrypt を使って無料の SSL 証明書を作成しているので、HTTPS 接続ができる状態にしてある。

何らかのクラウド VM や VPS を持っていない人は、以下に過去記事へのリンクを貼っておくので、これらを参考に GCE や OCI で無料のインスタンスを立てて、Public IP を取得しておこう。

OCI ではコレまで無料で2台の VM が立てられたが、2021年6月現在、 ARM ベースの Always Free インスタンスがさらに複数台立てられるようになっている。GCE では海外リージョンの低速な VM が1台しか立てられないので、今回初めてクラウド VM を立てるという人は、OCI の無料枠を利用するのが良いだろう。

今回 frp を利用するにあたっては、最低限 Public IP があれば良い。分かりやすくするために Freenom などを使って独自ドメインを取得しても良いだろう (DNS 設定は Freenom が提供する無料 DNS を利用)。VM 自体に Apache や nginx といったサーバを立てる必要はなく、HTTPS 接続も今回に関しては検証しないので、Let's Encrypt の導入も省略しても良い。

クラウドサービスごとに設定方法が異なるが、仮想ネットワークのファイアウォール設定で、インターネットからの 7000 番ポートや 7500 番ポートへのアクセスを許可しておくこと。ココで使用するポートは後で設定変更可能だ。

サーバに frps を用意する

クラウド VM や VPS を用意したら、frp のサーバ用ソフトである frps を用意しよう。

GitHub の Releases ページで、frps と frpc が両方まとめて一つのアーカイブとして配信されている。今回は v0.36.2 の Linux 用をダウンロードした。

今回検証に使ったのは、OCI で立てた Ubuntu 18.04 OS の Compute インスタンスだが、Linux 系のサーバであればほぼ同様に設定できるだろう。

# アーカイブをダウンロードする
$ wget https://github.com/fatedier/frp/releases/download/v0.36.2/frp_0.36.2_linux_386.tar.gz
# アーカイブを解凍する
$ tar xvf ./frp_0.36.2_linux_386.tar.gz
# 解凍したファイル群のうち、「frps」と「frps.ini」だけ取り出して利用する
$ cp ./frp_0.36.2_linux_386/frps ./
$ cp ./frp_0.36.2_linux_386/frps.ini ./
# frps のバージョンを確認する
$ ./frps -v
0.36.2

# 「frps.ini」設定ファイルの内容を編集する (後述)
$ vi ./frps.ini

# frps を起動する
$ ./frps -c ./frps.ini

設定ファイルである frps.ini は、以下のように記述する。

[common]
# クライアントとトークン = パスワード文字列による認証をする
token = mytoken
# このサーバが公開するポートとして 7000 番を指定する
bind_port = 7000
# ウェブサーバを公開するポートに 7000 番を利用する (上の bind_port と同じ番号で良い)
vhost_http_port = 7000
# ダッシュボードを公開する場合は以下3行を追加する
dashboard_port = 7500
dashboard_user = adminuser
dashboard_pwd = adminpass

token プロパティでパスワード文字列を設定しておく。サーバとクライアントを簡易認証するためのモノだ。bind_port は、このサーバがインターネット上に公開するポートを指定する。ココではデフォルトどおり 7000 番にしておく。

今回、ローカルマシンの開発サーバを公開しようとしているので、vhost_http_port というプロパティを指定する。接続したクライアントのポートを、どのポートで公開するか、という設定になる。今回は bind_port と同じ 7000 番にしておく。

下3行はオプションで、frps がどんなクライアントとどんな接続をしているかが Web UI で確認できる、ダッシュボードを公開するための設定。ココでは 7500 番を指定している。http://example.com:7500/ にアクセスすると BASIC 認証のプロンプトが表示されるので、この設定ファイルで指定しているユーザ名とパスワードで認証してやる。接続状況が確認できて分かりやすいが、あまりアレコレ公開したくない場合は下3行を削れば良い。

サーバ用の設定ファイルに記述するのはこのぐらい。設定ファイルを用意したら、frps を起動して待機させておこう。クライアントサイドの準備ができていなくても、構わず立ち上げっぱなしで良い。バックグラウンドで立ち上げっぱなしにしておく場合は nohup& を使うと良いだろう。アーカイブには systemd 用の設定ファイルも同梱されている。

$ nohup ./frps -c ./frps.ini &

ローカルマシンに frpc を用意する

続いて、クライアント側に frp のクライアント用ソフトである frpc を用意する。

Linux マシンで作業する場合は、上で紹介したサーバ用と同じアーカイブファイルをダウンロードすれば、中に frpc および frpc.ini が入っているので、コレを使えば良い。GitHub では同様に、Windows 版や Mac 版のアーカイブも用意されている。

今回は、MacOS で Homebrew を使って frpc を用意してみた。

# Homebrew では frps と frpc が別々に配信されている
$ brew install frps frpc
$ frps -v
0.36.2
# 今回使うのはコッチ、frpc のみ
$ frpc -v
0.36.2

# 設定ファイルを新規作成し編集する (後述)
$ touch ./frpc.ini
$ vi ./frpc.ini

# クライアントツールを起動する
$ frpc -c ./frpc.ini

設定ファイルの内容は次のようにする。

[common]
# frps.ini で指定したモノと同じトークン文字列を書いておく
token = mytoken
# 接続先サーバの Public IP かドメインを指定する
server_addr = example.com
# 接続先サーバのポートを指定する (frps.ini の bind_port プロパティと同じ値)
server_port = 7000
# ローカルマシン上でダッシュボード UI を有効にするには以下の4行を用意する
admin_addr = 127.0.0.1
admin_port = 7400
admin_user = adminuser
admin_pwd = adminpass

# ローカルの 8080 番ポートの内容を配信する
[web]
type = http
local_port = 8080
custom_domains = example.com

server_addrserver_port の2つで、接続先サーバを特定して通信するイメージだ。token プロパティによって簡易的な認証もできている、というワケ。トークン文字列が合わないと frpc を起動してもすぐに異常終了するので、問題に気が付けるはずだ。

admin_ で始まる4つの項目はオプション。frps (サーバサイド) にもあったダッシュボード UI のクライアント版で、http://localhost:7400/ にアクセスして設定内容が確認できるようになる。コチラは frps のダッシュボードと比べて確認できる内容が少ないので、あまり必要性・有用性は感じないかな。

今回、ローカルの 8080 番ポートで開発サーバを起動しているテイなので、その設定が [web] セクションに書かれている。Mac の場合は Apache が標準インストールされているので、とりあえず接続できるか試すだけであれば、$ sudo apachectl start ($ sudo httpd start) でサーバを起動し、80 番ポート (http://localhost/) で公開されたサーバを利用しても良いだろう。custom_domains は、サブドメインを利用したりするつもりがなければ、とりあえず server_addr に指定したドメインと同じで良い。

この状態で frpc を起動すると、自動的に frps との接続を確立してくれる。先に frps (サーバ側) が起動していないと、frpc は接続先が見つからず異常終了してしまうので注意。

実際にアクセスしてみる

以上の設定内容でいけば、次の URL で、ローカルサーバにアクセスできるはずだ。

めっちゃお手軽!

以上

frp はサーバサイド用の frps と、クライアントサイド用の frpc とで、実行ファイルと設定ファイルが別れているので、どのマシンがどちらの立場で使われているのかが一目瞭然だ。Go 言語製の特徴でもあるシングルバイナリ形式なので、インストールが簡単なのも嬉しい点。

設定ファイルに全ての情報が記述されるので、コマンドオプションなんかを暗記する必要がなく、設定ファイルさえ見れば状況が確認できて分かりやすい。OS ごとにバイナリファイルは異なるものの、設定ファイルの書式は同じなので迷うことが少ない。

シンプルなインターフェイスなのに機能やオプションは豊富で、1台のサーバが複数のマシンへの SSH 接続のトンネルを提供したり、複数のウェブサーバをそれぞれサブドメイン形式で公開したり、といったこともできる。Raspberry Pi + Web カメラ + MJPG-Streamer などで構築した監視カメラアプリなんかを、frpc でインターネット公開してやれば、自宅の様子を外で確認したりできるし、必要な時だけ SSH 接続できる自宅サーバを用意したりできるだろう。クライアント側の frpc の接続状況が変化しても、サーバ側の frps は再起動等が不要なので、扱いやすい。

frpFast Reverse Proxy の略らしいが、その名のとおり、簡単な設定ファイルですぐにリバースプロキシが構築できて良い。