はじめに
現在、私は AWS EC2で自作アプリを公開・運用すること を目標に学習しています。
しかし、いきなりクラウド環境へデプロイするのではなく、まずはサーバーの構築やデプロイの流れを理解したい...。
そこで今回は、自宅のUbuntu PCを簡易サーバーとして利用し、Dockerで作成したアプリケーションをデプロイして動作確認できる環境を構築していきます。
構成としては、以下のようなイメージです。
開発PC
│
Docker Imageを作成
│
自宅Ubuntuサーバー
│
Dockerで起動
しかし、ここで一つ問題がありました。
外出先から自宅UbuntuへSSH接続したかったのですが、
- ポート開放はしたくない(セキュリティ怖い)
- 無料で利用したい
- 設定が簡単な方法がいい
という条件が個人的にありました。
そこで見つけたのが Tailscale です!
実際に使ってみると驚くほど簡単に接続できたのですが、
- なぜポート開放なしで通信できるの?
-
100.x.x.xのIPアドレスって何? - サブネットマスクが
255.255.255.255なのに通信できるのはなぜ? - 通信相手はどうやって見つけているの?
など、多くの疑問があったので、
今回はそれらを調べながら Tailscaleの通信の仕組み をまとめてみます。
なお、Tailscaleの導入については、以下の記事を参考にさせていただきました。
https://utakamo.com/article/network/vpn/tailscale/index.html#vpn-p2p-ssh
最初に困ったこと
最初は、自宅のWindows PCとUbuntu PCを同じWi-Fiへ接続してSSH接続していました。
Windows
│
家庭用ルーター
│
Ubuntu
この構成であれば問題なくSSH接続できます。
しかし、外出すると通信できなくなりました。
Windows(外出先)
│
インターネット
×
家庭内LAN
│
Ubuntu
これはUbuntuに割り当てられている
192.168.x.x
というIPアドレスが、家庭内ネットワーク(LAN)の中だけで利用できるプライベートIPアドレスだからです。
通常、外部からアクセスするには
- グローバルIPアドレス
- ポート開放
- DDNS
などの設定が必要になります。
しかし今回は、「できるだけ簡単に、安全に、しかも無料で実現したい」 という条件があったため、別の方法を探しました。
LANとWANとは
Tailscaleを理解する前に、まずはLANとWANについて簡単に整理します。
LANとは
LAN(Local Area Network)は、自宅や会社など限られた範囲のネットワークのことです。
例えば、自宅では次のような構成になります。
Windows
│
Ubuntu
│
スマートフォン
│
家庭用ルーター
このように同じルーターへ接続された端末同士は、お互いに直接通信できます。
各端末には、
192.168.x.x
のようなプライベートIPアドレスが割り当てられます。
WANとは
WAN(Wide Area Network)は、世界中のネットワークをつなぐ広域ネットワークです。
私たちが普段利用しているインターネットもWANに含まれます。
自宅LAN
│
インターネット(WAN)
│
会社
LAN同士は、そのままでは通信できません。
そのため、安全に通信するための技術としてVPNがあります。
VPNとは
VPN(Virtual Private Network)は、
インターネット上に仮想的な専用ネットワークを構築する技術です。
VPNを利用すると、離れた場所にある端末同士でも、まるで同じLANに接続されているように通信できます。
また、通信内容は暗号化されるため、安全にデータを送受信できます。
イメージとしては、インターネット上に専用トンネルを作るようなものです。
Windows
│
====== VPN ======
│
Ubuntu
インターネットVPNとIP-VPN
VPNには大きく分けて2種類あります。
インターネットVPN
既存のインターネット回線を利用してVPNを構築する方式です。
PC
↓
インターネット
↓
PC
特徴
- 導入コストが低い
- 個人でも利用しやすい
- Tailscaleはこちらに分類される
IP-VPN
通信事業者が管理する閉域網を利用するVPNです。
PC
↓
閉域網
↓
PC
特徴
- インターネットを経由しない
- 高い信頼性
- 主に企業向け
P2P通信とは
今回利用したTailscaleは、P2P(Peer-to-Peer)通信を基本としています。
P2Pとは、端末同士が直接通信する方式です。
一般的なVPNではVPNサーバーを経由します。
Windows
↓
VPNサーバー
↓
Ubuntu
一方、Tailscaleでは可能な限り端末同士が直接通信します。
Windows
↓
Ubuntu
これにより、
- 通信経路が短くなる
- 通信速度が低下しにくい
- VPNサーバーに負荷が集中しない
というメリットがあります。
補足
ネットワーク環境によっては、NATやファイアウォールの影響で直接通信できない場合があります。
その場合は、Tailscaleが提供するDERPサーバーを中継して通信します。そのため、「必ず直接通信する」というわけではありません。
Tailscaleを選んだ理由
今回Tailscaleを選んだ理由は、無料プランでも十分な機能を利用できたためです。
無料プランでは、以下のような機能が利用できます。
- 最大100台のデバイスを登録可能
- 最大3人のユーザーで利用可能
- デバイス同士のP2P通信
- MagicDNS
- ACL(アクセス制御)
- ユーザー承認
- シングルサインオン(SSO)
今回私が利用したのは、主に次の2つです。
- 登録デバイス同士のP2P通信
- MagicDNS
これだけでも、自宅サーバーの検証環境としては十分でした。
Tailnetとは
Tailnetとは、Tailscaleによって構築される仮想ネットワークのことです。
Tailscaleへログインした端末は、同じTailnetへ参加します。
各端末には
100.x.x.x
という専用IPアドレスが割り当てられます。
例えば、
Windows
100.x.x.x
Ubuntu
100.x.x.x
Mac
100.x.x.x
のように、同じTailnet内の端末は、お互いを専用IPアドレスで識別できます。
MagicDNSとは
通常、SSH接続ではIPアドレスを指定します。
ssh user@100.xxx.xxx.xxx
しかしMagicDNSを利用すると、
ssh user@ubuntu
のようにホスト名だけで接続できます。
IPアドレスを覚える必要がなくなるため、複数台の端末を管理する場合でも非常に便利です。
Tailscaleの通信の仕組み
ここまでで、
- VPNとは何か
- Tailscaleとはどのようなサービスなのか
- Tailnetとは何か
について整理しました。
ここからは、実際にTailscaleがどのような仕組みで通信を行っているのかを見ていきます。
Tailscaleとは
Tailscaleは、WireGuardを利用して、安全な仮想プライベートネットワーク(VPN)を構築するソフトウェアです。
例えば、自宅のWindows PCとUbuntuサーバーがあったとします。
通常であれば、
Windows
│
家庭用ルーター
│
インターネット
│
家庭用ルーター
│
Ubuntu
外部からUbuntuへ接続するには、
- ポート開放
- グローバルIPアドレス
- DDNS
などの設定が必要になることがあります。
しかし、Tailscaleを利用すると、
Windows
│
Tailscale VPN
│
Ubuntu
のように、世界中にある端末同士が、同じLANへ接続されているかのように通信できます。
Tailscaleをインストールすると何が起こるのか
Tailscaleをインストールすると、OSには仮想ネットワークアダプターが追加されます。
Windowsでは ipconfig を実行すると、次のような表示になります。
不明なアダプター Tailscale
IPv4 Address .........100.x.x.x
Subnet Mask ..........255.255.255.255
Default Gateway ......
ここで注目したいのは、
100.x.x.x255.255.255.255- デフォルトゲートウェイが空欄
の3点です。
Tailscale IPアドレス(100.x.x.x)とは
通常、自宅のWi-Fiへ接続すると、
192.168.x.x
のようなプライベートIPアドレスが割り当てられます。
一方、Tailscaleへログインすると、
100.x.x.x
というIPアドレスが割り当てられます。
これはTailnet内だけで利用される専用IPアドレスです。
例えば、
Windows
100.88.10.5
Ubuntu
100.95.40.7
Mac
100.102.12.3
このように、Tailnetへ参加しているすべての端末に専用IPが割り当てられます。
このIPアドレスを利用することで、世界中にある端末同士でも、お互いを識別できるようになります。
サブネットマスクが255.255.255.255なのはなぜ?
通常のLANでは、
IPアドレス
192.168.1.10
サブネットマスク
255.255.255.0
となっています。
この場合、
192.168.1.x
が同じネットワークになります。
しかしTailscaleでは、
100.x.x.x
255.255.255.255
となっています。
このサブネットマスクは /32 と呼ばれ、
「このIPアドレスは自分自身だけ」
という意味になります。
つまり普通のLANの考え方では、「他の端末とは通信できない」設定になっています。では、なぜ通信できるのでしょうか。
答えは、
通信経路をサブネットマスクではなく、ルーティングテーブルで管理しているからです。
通信先を決めているのはサブネットマスクではない
一般的なLANでは、
宛先IP
↓
サブネットマスクを確認
↓
同じネットワークなら直接通信
違えばルーターへ送る
という流れになります。
一方、Tailscaleでは違います。
宛先IP
↓
Windows Routing Table
↓
Tailscale Adapter
↓
WireGuard
つまり、
通信先はサブネットマスクではなく、OSが持っているルーティングテーブルによって決められています。
ルーティングテーブルとは
Windowsには、
route print
というコマンドがあります。
このコマンドを実行すると、
OSが保持している通信経路(ルート)を確認できます。
例えば、
100.64.0.0/10
↓
Tailscale Adapter
というルートが登録されています。
これは、
100.x.x.x宛ての通信は、Wi-FiではなくTailscaleアダプターへ送る
という意味です。
つまり、Windowsは、サブネットマスクではなく、ルーティングテーブルを見て通信先を決定しています。
Tailscaleは通信相手をどう見つけるのか
「100.x.x.xというIPだけで、どうやって相手を見つけているの?」
その答えが コントロールプレーン(Control Plane) です。
Tailscaleへログインすると、各端末はTailnetへ登録されます。
登録される情報には、
- Tailscale IP
- 公開鍵
- 接続情報
- デバイス名
などがあります。
例えば、
100.95.40.7
というIPを見ると、
Tailscaleは
「これはUbuntuサーバー」
という情報を持っています。
つまり、
IPアドレスだけで通信相手を識別できるようになっています。
コントロールプレーン(Control Plane)の役割
コントロールプレーンは、Tailnetに参加している端末の情報を管理しています。
例えば、
- Tailscale IPアドレス
- デバイス名
- 公開鍵
- 接続先の情報
などを管理し、各端末へ配布しています。
一方で、SSHやファイル転送などの実際の通信データはコントロールプレーンを経由しません。
つまり、
Control Plane
・ログイン認証
・端末情報の管理
・公開鍵の配布
・接続情報の共有
を担当するだけであり、実際の通信は端末同士で直接行われます。
WireGuardによる暗号化
通信相手が分かると、
Tailscaleは内部で利用している WireGuard によって通信を暗号化します。
通信の流れは次のようになります。
SSH
↓
Tailscale
↓
WireGuardで暗号化
↓
インターネット
インターネット上を流れる通信はすべて暗号化されるため、
第三者に通信内容を読み取られる心配はほとんどありません。
ポート開放なしで通信できるのはなぜ?
通常、自宅PCへ外部からアクセスする場合は、
外部PC
↓
インターネット
↓
家庭用ルーター
↓
Ubuntu
という流れになります。
しかし、家庭用ルーターは外部から突然送られてきた通信を基本的には受け付けません。
そのため、
- ポート開放
- DDNS
- グローバルIPアドレス
などの設定が必要になります。
一方、Tailscaleでは両方の端末が先に外向きの通信を開始します。
これは、Tailscaleのコントロールプレーンが各端末へ接続先の情報(公開鍵や現在の接続情報など)を共有し、それぞれの端末が相手に向かって通信を開始するためです。
この仕組みにより、多くの家庭用ルーター(NAT)の環境では、UDPホールパンチング(UDP Hole Punching) と呼ばれる技術を利用して、ポート開放を行わなくても端末同士が直接(P2P)通信できるようになります。
① 接続情報を共有
┌─────────────────────┐
│ Tailscale │
│ Control Plane │
└─────────────────────┘
▲ ▲
│ │
Windows │ │ Ubuntu
│ │
「相手の接続先情報」を取得
│ │
▼ ▼
② お互いにUDP通信を開始
Windows Ubuntu
│ │
├────────────▶ ◀────────────┤
│ UDPパケット送信 │
▼ ▼
NAT NAT
③ NATに一時的な通信経路(穴)が作られる
Windows ◀──────── P2P通信 ───────▶ Ubuntu
では、なぜこの方法でポート開放なしに通信できるのでしょうか。
通常、家庭用ルーター(NAT)は、外部から突然送られてきた通信を遮断します。
インターネット
│
▼
家庭用ルーター
│
✕
Ubuntu
これは、ルーターが
「この通信はどの端末宛てなのか分からない」
と判断し、セキュリティのために通信を破棄するからです。
一方で、自宅PCからインターネットへ通信を開始すると、ルーターは
「この通信は内側から始まった通信だから、返答は通してよい」
と判断し、一時的に通信経路(NATテーブル)を作成します。
Tailscaleでは、各端末が起動するとコントロールプレーンへ接続し、
- Tailscale IP(100.x.x.x)
- 公開鍵
- 現在の接続先情報(グローバルIPアドレスやUDPポート番号など)
を登録します。
その後、SSHなどで通信を開始すると、コントロールプレーンから事前に共有されている接続情報をもとに、WindowsとUbuntuのTailscaleクライアントがお互いのエンドポイントへほぼ同時にUDPパケットを送信します。
このとき重要なのは、相手から通信が来るのを待って返信するのではなく、両方の端末が自分から通信を開始するという点です。
Windows Ubuntu
│ │
│ UDP送信 ─────────────▶ │
│◀───────────── UDP送信 │
両方のルーターは、それぞれ
「この通信は内側から始まった通信だ」
と認識するため、一時的に通信を許可する状態になります。
この状態になると、お互いのルーターに通信経路(Hole)が作られ、その経路を利用してWindowsとUbuntuが直接(P2P)通信できるようになります。
これがUDPホールパンチングです。
P2P接続が確立すると、その後の通信はWireGuardによって暗号化され、SSHやファイル転送などのデータは、安全な状態で直接やり取りされます。
補足
UDPホールパンチングは、多くの家庭用ルーターで利用できますが、すべての環境で成功するわけではありません。
企業ネットワークや一部の厳しいNAT・ファイアウォール環境ではP2P接続を確立できないことがあります。その場合は、Tailscaleが提供する DERPサーバー が中継役となり、暗号化された通信を転送します。
DERPを経由した場合でも通信はWireGuardで暗号化されており、通信内容が平文になることはありません。
相手PCでは何が起きている?
Ubuntu側にもTailscaleがインストールされています。
Ubuntuでは、
インターネット
↓
WireGuard
↓
復号
↓
SSHサーバー
という流れになります。
暗号化された通信を受け取り、WireGuardが復号したあと、SSHサーバーへ通信を渡しています。
SSHサーバーからすると、通常のSSH接続とほとんど変わりません。
デフォルトゲートウェイが空欄なのはなぜ?
通常のLANでは、
192.168.x.1
のようなルーターがデフォルトゲートウェイとして設定されています。
しかし、
Tailscaleアダプターにはデフォルトゲートウェイが設定されていません。
これは、インターネット全体の通信をTailscaleへ流すわけではないからです。
Windowsは、
100.x.x.x
宛ての通信だけをTailscaleアダプターへ送ります。
それ以外の通信は、従来どおりWi-Fiや有線LANからインターネットへ送信されます。つまり、Tailscaleは必要な通信だけを仮想ネットワークへ流しているため、専用のデフォルトゲートウェイは不要なのです。
Tailscale全体の通信の流れ
WindowsからUbuntuへSSH接続する場合、
実際には次のような流れになります。
SSH
↓
100.x.x.x
↓
Windows Routing Table
↓
Tailscale Adapter
↓
WireGuardで暗号化
↓
Wi-Fi
↓
家庭用ルーター
↓
インターネット
↓
Ubuntu
↓
WireGuardで復号
↓
SSHサーバー
このように、
Windowsのルーティングテーブルが通信をTailscaleへ渡し、
TailscaleがWireGuardで暗号化して相手PCへ届けています。
通常のLANとの違い
| 通常のLAN | Tailscale |
|---|---|
| サブネットマスクで通信先を判断する | ルーティングテーブルで通信先を判断する |
| デフォルトゲートウェイが必要 | 基本的に不要 |
| LAN内で直接通信する | 仮想ネットワーク(Tailnet)経由で通信する |
| 通信は暗号化されないことが多い | WireGuardで常に暗号化される |
| 同じネットワーク内でしか通信できない | インターネット経由でも同じLANのように通信できる |
まとめ
最初は、
100.x.x.x
255.255.255.255
という設定を見て、「これで本当に通信できるの?」と疑問に思っていました。
しかし調べてみると、
- 100.x.x.x宛ての通信はルーティングテーブルによってTailscaleアダプターへ送られる
- コントロールプレーンが接続先の情報を管理する
- WireGuardが通信を暗号化する
- 可能な限りP2P通信で接続する
- Ubuntu側で復号され、SSHサーバーへ渡される
という流れで通信が行われていることが分かりました。
また、TailscaleはVPNサーバーへ通信を集約するのではなく、
- OSのルーティングテーブル
- Tailnetによる端末管理
- WireGuardによる暗号化
- P2P通信
これらを組み合わせることで、安全かつシンプルな仮想ネットワークを実現しています。
おわりに
今回の記事では、自分自身が
- 「100.x.x.xって何?」
- 「なぜポート開放なしで通信できるの?」
- 「サブネットマスクが /32 なのに通信できる理由は?」
と疑問に思ったことを調べながら整理してみました。
私自身、インフラやネットワークについてはまだ学習途中のため、理解が足りていない部分もあるかもしれません。
もし記事の内容で間違っている点や、「こういう仕組みも知っておくといいよ」というものがあれば、ぜひコメントで教えていただけるとうれしいです!
参考
今回はこちらの記事を参考にさせていただきました。
また、Tailscaleの公式ドキュメントも非常に分かりやすくまとまっています。