自宅を隠したまま、VPSとつなぐ

第2回で、VPSを入口に、自宅のNASを処理の本体にする形を決めました。自宅は外に直接さらさない。では、外から来たリクエストを、VPSから自宅のNASまでどうやって届けるのか。

答えはトンネルです。今回は、そのトンネルを既製のサービスに頼らず、自分で組む話です。

まず、既製品はどうやっているのか

自宅のサーバーを公開する方法として、よく使われているのがCloudflare Tunnelです。自宅のルーターに穴を開けずに、外から自宅のサーバーへアクセスできるようになります。

最初は、なぜそんなことができるのか不思議でした。仕組みはこうです。

  1. 自宅のサーバーで小さなプログラムを動かし、自宅の側からCloudflareへ暗号化した接続を張る。
  2. その接続を、切らずに張りっぱなしにしておく。
  3. 利用者のリクエストはまずCloudflareに届き、張ってある接続の中を通って自宅のサーバーへ流される。

ポイントは、接続を外から入れるのではなく、内側から外へ張っていることです。ルーターから見れば、家の中のパソコンが外のサイトを見ているのと同じ、外へ出ていく通信です。だから、ルーターに穴を開けなくても通ります。自宅のIPアドレスも外に出ません。

よくできた仕組みで、無料で使える範囲も広い。それでも私は、同じものを自分で組むことにしました。

なぜ、トンネルまで自分で持つのか

経路をすべて自分の管理下に置く

既製のトンネルを使うと、自分のサービスのすべての通信が、他社の設備を通ることになります。その会社の方針や利用規約が変われば、こちらの運用も変えざるを得ません。自前のインフラを目指すなら、入口から本体までの経路は、自分で持っておきたい。

仕組みを知らないものは、増やせない

この計画では、いずれ入口のVPSを増やし、拠点も増やします。そのたびに、どこと、どこを、どうつなぐかを自分で決めることになります。一度自分の手で組んでおけば、仕組みがわかり、増やすときも迷いません。

やることは、Cloudflare Tunnelと同じです。自宅のNASの側から、入口のVPSへ、暗号化した接続を張りっぱなしにする。Cloudflareの役を、自分のVPSが担うだけです。

WireGuardで組む

トンネルには、WireGuardというVPNのソフトウェアを使います。設定が短く、鍵の組を交換するだけでつながるので、個人でも扱いやすいからです。

トンネルの両端の役割
 VPS(入口)自宅のNAS(本体)
接続待ち受けるこちらから張りに行く
トンネル内のアドレス(例)10.8.0.110.8.0.2
外に開けるものHTTPSと、WireGuardの待ち受けだけ何も開けない

VPS側の設定

VPSは、自宅からの接続を待ち受けます。相手として登録するのは、自宅のNASの公開鍵だけです。

VPSの設定例(/etc/wireguard/wg0.conf)
[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 は、一定の間隔で小さな通信を送り続ける設定です。これがないと、家庭用ルーターが「もう使われていない通信」と判断して、しばらくすると経路を閉じてしまいます。

NASの設定例(/etc/wireguard/wg0.conf)
[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へ流します。

VPSのNginxの設定例(証明書まわりは省略)
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のトンネルだけを切れば済みます。トンネルを自分で組んだから、入口を足すのも外すのも自分で決められる。それが今回の一番の収穫です。

この段階でやること

  1. VPSとNASにWireGuardを入れ、それぞれの鍵の組を作る。
  2. NASからVPSへトンネルを張り、つながることを確かめる。
  3. VPSのリバースプロキシから、トンネルの先のNASへリクエストを流す。
  4. NASとVPSのファイアウォールで、必要な通信だけを通す。
  5. トンネルが切れたときに、自動でつなぎ直されることを確かめる。

TODO:実際に組んだときの記録(NASでWireGuardをどう動かしたか、つまずいた点)をここに追記する(鍵・IPアドレス・ポート番号は公開しない)。