自宅を隠したまま、VPSとつなぐ
第2回で、VPSを入口に、自宅のNASを処理の本体にする形を決めました。自宅は外に直接さらさない。では、外から来たリクエストを、VPSから自宅のNASまでどうやって届けるのか。
答えはトンネルです。今回は、そのトンネルを既製のサービスに頼らず、自分で組む話です。
まず、既製品はどうやっているのか
自宅のサーバーを公開する方法として、よく使われているのがCloudflare Tunnelです。自宅のルーターに穴を開けずに、外から自宅のサーバーへアクセスできるようになります。
最初は、なぜそんなことができるのか不思議でした。仕組みはこうです。
- 自宅のサーバーで小さなプログラムを動かし、自宅の側からCloudflareへ暗号化した接続を張る。
- その接続を、切らずに張りっぱなしにしておく。
- 利用者のリクエストはまずCloudflareに届き、張ってある接続の中を通って自宅のサーバーへ流される。
ポイントは、接続を外から入れるのではなく、内側から外へ張っていることです。ルーターから見れば、家の中のパソコンが外のサイトを見ているのと同じ、外へ出ていく通信です。だから、ルーターに穴を開けなくても通ります。自宅のIPアドレスも外に出ません。
よくできた仕組みで、無料で使える範囲も広い。それでも私は、同じものを自分で組むことにしました。
なぜ、トンネルまで自分で持つのか
経路をすべて自分の管理下に置く
既製のトンネルを使うと、自分のサービスのすべての通信が、他社の設備を通ることになります。その会社の方針や利用規約が変われば、こちらの運用も変えざるを得ません。自前のインフラを目指すなら、入口から本体までの経路は、自分で持っておきたい。
仕組みを知らないものは、増やせない
この計画では、いずれ入口のVPSを増やし、拠点も増やします。そのたびに、どこと、どこを、どうつなぐかを自分で決めることになります。一度自分の手で組んでおけば、仕組みがわかり、増やすときも迷いません。
やることは、Cloudflare Tunnelと同じです。自宅のNASの側から、入口のVPSへ、暗号化した接続を張りっぱなしにする。Cloudflareの役を、自分のVPSが担うだけです。
WireGuardで組む
トンネルには、WireGuardというVPNのソフトウェアを使います。設定が短く、鍵の組を交換するだけでつながるので、個人でも扱いやすいからです。
| VPS(入口) | 自宅のNAS(本体) | |
|---|---|---|
| 接続 | 待ち受ける | こちらから張りに行く |
| トンネル内のアドレス(例) | 10.8.0.1 | 10.8.0.2 |
| 外に開けるもの | HTTPSと、WireGuardの待ち受けだけ | 何も開けない |
VPS側の設定
VPSは、自宅からの接続を待ち受けます。相手として登録するのは、自宅のNASの公開鍵だけです。
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = (VPSの秘密鍵)
[Peer]
# 自宅のNAS
PublicKey = (NASの公開鍵)
AllowedIPs = 10.8.0.2/32
自宅のNAS側の設定
NASは、VPSへ接続を張りに行きます。PersistentKeepalive は、一定の間隔で小さな通信を送り続ける設定です。これがないと、家庭用ルーターが「もう使われていない通信」と判断して、しばらくすると経路を閉じてしまいます。
[Interface]
Address = 10.8.0.2/24
PrivateKey = (NASの秘密鍵)
[Peer]
# 入口のVPS
PublicKey = (VPSの公開鍵)
Endpoint = (VPSのアドレス):51820
AllowedIPs = 10.8.0.1/32
PersistentKeepalive = 25
NAS側の AllowedIPs は、VPSのトンネル内アドレス1つだけにしています。NASがトンネル越しに話す相手は、入口のVPSだけでいいからです。
VPSで受けて、トンネルの先へ流す
最後に、VPSのリバースプロキシが受けたリクエストを、トンネルの先のNASへ流します。
server {
listen 443 ssl;
server_name example.com;
location / {
proxy_pass http://10.8.0.2:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
利用者との通信はVPSでHTTPSとして暗号化され、VPSから自宅までの区間はWireGuardのトンネルが暗号化します。
この形では、VPSはリクエストを自分で受け直してNASへ渡すだけなので、パケットをそのまま転送するルーターの設定(IP転送やNAT)は要りません。使わない機能は有効にしない。そのほうが、設定の間違いで思わぬ経路が開く心配も減ります。
設定例は考え方を示すためのものです。実際に組むときは、WireGuardとNginxの公式ドキュメントを確認してください。また、自分の環境の鍵、IPアドレス、ポート番号は、公開する記事やリポジトリに載せないようにします。
独自のプロトコルにしないのか
既製品ではなく自前で組むなら、いっそトンネルの通信の方式(プロトコル)まで独自に作ればいいのでは、と考えたこともあります。独自の方式なら、それを狙う攻撃者もそうそういないだろう、と。
ただ、ここは分けて考えます。ネットワークの形は独自に作る。暗号の方式は、実績のあるものを使う。
暗号の方式は、多くの専門家に長く調べられ、それでも破られなかったことで信頼されています。自分で作った方式は、自分で見つけられなかった穴があっても、誰も教えてくれません。WireGuardは、仕組みが小さく、よく調べられている方式です。その上に、どの拠点とどの入口をどうつなぐかという自分だけのネットワークの形を作る。独自にするのは、そこで十分です。
「狙う人がいない」ことも、守りの当てにはしません。インターネットにつながった機械は、相手を選ばない自動の攻撃に探されているからです(考察「セキュリティという参入障壁」)。
トンネルが守るのは、経路だけ
トンネルを張れば安全、というわけではありません。トンネルが守るのは、VPSから自宅までの経路です。
もし入口のVPSが乗っ取られたら、攻撃者はそのトンネルを通って自宅のNASまで来られます。トンネルの中は、いわば家の中の廊下です。だから、トンネルの先でも守りを固めます。
- NASがトンネル越しに受け付けるのは、VPSからの、決めたポートへの通信だけにする。
- NASの管理画面やSSHは、トンネル越しにも公開しない。
- VPSで外に開けるのは、HTTPSとWireGuardの待ち受けだけにする。VPSへのログインは鍵認証にする。
物理の層からアプリケーションの層まで、すべての責任が自分に来る。これが自前でインフラを持つときの一番高い壁です。この話は、考察の「セキュリティという参入障壁」で掘り下げます。
入口を増やすときも、同じ形で
この計画では、安いVPSを複数契約して入口にするつもりです。そのときも形は変わりません。NASから、それぞれのVPSへトンネルを張り、VPSごとにトンネル内のアドレスを割り振るだけです。
入口が複数あれば、どれか1台のVPSが止まっても、残りの入口で受け続けられます。1台が乗っ取られても、そのVPSのトンネルだけを切れば済みます。トンネルを自分で組んだから、入口を足すのも外すのも自分で決められる。それが今回の一番の収穫です。
この段階でやること
- VPSとNASにWireGuardを入れ、それぞれの鍵の組を作る。
- NASからVPSへトンネルを張り、つながることを確かめる。
- VPSのリバースプロキシから、トンネルの先のNASへリクエストを流す。
- NASとVPSのファイアウォールで、必要な通信だけを通す。
- トンネルが切れたときに、自動でつなぎ直されることを確かめる。
TODO:実際に組んだときの記録(NASでWireGuardをどう動かしたか、つまずいた点)をここに追記する(鍵・IPアドレス・ポート番号は公開しない)。