VPNサービスTailscaleを使ってAWS VPCプライベートサブネット内のリソースと通信してみた

2021.11.24

Tailscale はデバイス・アプリケーション同士をP2Pのメッシュネットワークで通信させるVPNサービスです。 Linuxカーネルにも組み込まれているWireGuardをベースに実装されています。

Windows/Linux/スマートフォンなどにクライアントプログラムをインストールすると、ごく僅かな設定だけで利用できます。

AWS VPC展開する場合、、たくさんのインスタンスへのインストールが発生する上、RDSのようなマネージドサービスにはユーザープログラムはインストールできません。

そこで、VPC内にゲートウェイとなるEC2インスタンス(Tailscaleドキュメントで言う所の「サブネットルーター」)を1台だけセットアップし、VPC内の各種リソースと通信する方法を紹介します。

  • 左側がTailscaleクライアントをインストールしたTailscaleネットワーク
  • 右側がAWS VPC
  • 中央にいるのがVPC内に構築したサブネットルーターノード

※図は「Subnet routers and traffic relay nodes · Tailscalehttps://tailscale.com/kb/1019/subnets/」から

Tailscaleネットワーク内のノードからはAWS VPCのプライベートなリソースへの通信、例えば、EC2インスタンスへのSSH、RDSへのデータベース接続などができます。

やってみた

AWS VPC内にサブネットルーターをセットアップし、クライアントからVPCのプライベートサブネットにあるEC2にSSHできることを確認します。

環境

  • Tailscale : 1.18.1
  • AWS VPC
  • EC2 : Amazon Linux 2(x86_64)

Tailscale のアカウント開設

まずは次の URL から Tailscale のアカウントを開設します。

https://tailscale.com/start

Google/Microsoft/GitHubのアカウントでSSOしてアカウントが必要です。

次に、実行環境向けのTailscaleクライアントをダウンロードします。

管理画面のマシン一覧には、インストールしたデバイスを確認できます。

各デバイスには、キャリアグレードNAT(CGNAT)用に予約されている 100.64.0.0/10 のIPアドレスが割り振られます。

サインアップの詳細は次のドキュメントをご確認ください。

Getting Started with Tailscale · Tailscale

AWS リソースの作成

次にAWSに

  • 新規VPC(Tailscale Subnet Router for AWS VPC Demo)
  • サブネットルーター用EC2(Tailscale Relay Server)
  • 接続確認用EC2(Tailscale Target Server)

を構築します。

次のドキュメントと同じ構成です。

Connect to an AWS VPC using subnet routes · Tailscale

次のGitHubのレポジトリにあるCloudFormationのテンプレートを流すだけです。

quiver/tailscale-aws-vpc-subnet-route-cfn: AWS CloudFormation template for Tailscale VPC Subnet Router Demo

ポイントは以下の3点です。

サブネットルーターをNATゲートウェイ配下のプライベートサブネットに配置することも可能ですが、 パフォーマンスの観点から、ドキュメント通り、パブリックサブネットに設置しています。

デバイス同士がP2Pで通信して、レイテンシーを軽減できるよう、サブネットルーターのセキュリティグループのインバウンドルールでは、UDPの41641ポートを許可しています。

サブネットルーター経由でVPC内のリソースにアクセスできるよう、これらリソースのセキュリティグループのインバウンドルールでは サブネットルーターにアタッチしたセキュリティグループをソースとする通信を許可しています。

TailScaleの初期設定

CloudFormationでは、EC2のユーザーデータでサブネットルーターインスタンスへのTailScaleのインストールIPフォワードの有効化までを行っています。

ルーターインスタンスに接続し、手動でノードをTailscaleネットワークに参加させます。

Systems Manager Session Manager でシェルアクセスします(そのため、公式ドキュメントにあるようなSSH通信は許可していません)。

tailscale がインストールされていることを確認します。

$ tailscale version
1.18.1
  tailscale commit: 0a43340482a86c3c227aa133c8f1ee9e36360b4b
  other commit: b05dc05d46c4a3c7e634ef0695221985be99274d
  go version: go1.17.2-ts7037d3ea51

次のコマンドで tailscale を起動します。

$ sudo tailscale up --advertise-routes=10.0.0.0/16 --hostname=vpc-subnet-router

To authenticate, visit:

        https://login.tailscale.com/a/abcde

リレーする CIDR(=AWS VPC)を --advertise-routes で指定し、--hostname でこのインスタンスのノード名を指定します。

認証用のRULをクリックし、Tailscale サイトにログインして認証します。

$ tailscale status コマンドから、EC2インスタンスが Tailscaleネットワークに参加したことがわかります。

$ tailscale status
100.91.173.90   vpc-subnet-router    foo@      linux   -
100.126.250.25  mac-office           foo@      macOS   idle, tx 18184 rx 20840

Tailscale 用の NIC(tailscale0) が追加されています。

$ tailscale ip -4
100.91.173.90

$ ip addr show tailscale0
3: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none
    inet 100.91.173.90/32 scope global tailscale0
       valid_lft forever preferred_lft forever
    inet6 fd7a:115c:a1e0:ab12:4843:cd96:625b:ad5a/128 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::df00:75ec:4275:f77c/64 scope link stable-privacy
       valid_lft forever preferred_lft forever

クライアントからサブネットルーターにアクセス

次に、クライアントからAWS VPCにあるサブネットルーター用EC2インスタンスにSSH接続してみます。

IPアドレスとして、100 で始まる TailscaleネットワークのIPアドレスを指定します。

$ ssh -i /path/to.pem ec2-user@100.91.173.90

...

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-0-0-18 ~]$

クライアントからVPC 内のリソースにアクセス

ここからが本題です。 サブネットルーターをいかした通信を試します。

サブネットルーター配下にある、TailscaleがインストールされていないAWS VPCのリソースにアクセスします。

準備として、Tailscale管理コンソールで以下の操作を行います。

  • アドバタイズするサブネットを承認
  • 鍵の有効期限を無効化

アドバタイズするサブネットを承認

サブネットルーター内で tailscale を起動する際、 --advertise-routes=10.0.0.0/16 でアドバタイズするサブネットを指定しました。

追加するノードの承認を管理コンソールで行ったように、このサブネットの承認もコンソールで行います。

マシーン一覧のアクションメニューから Review route settings を選択し、アドバタイズするサブネットを Enable します。

鍵の有効期限を無効化

同様に、サブネットルーターデバイス(vpc-subnet-router)を選択し、メニューから Disable key expiry を指定して、鍵の有効期限を無効化します。

デフォルトでは 180 日ごとにノードの再認証が必要です。

SSH の接続テスト

上記設定を済ませ、VPC プライベートサブネットにある EC2 に対して traceroute してみます。

$ traceroute 10.0.2.146
traceroute to 10.0.2.146 (10.0.2.146), 64 hops max, 52 byte packets
 1  100.121.197.86 (100.121.197.86)  26.016 ms  19.954 ms  19.784 ms
 2  10.0.2.146 (10.0.2.146)  19.091 ms  19.979 ms  19.222 ms

パケットはサブネットルーター(100.121.197.86/10.0.0.18)を経由して届きます。

SSH 接続を試します。

$ ssh -i /path/to.pem ec2-user@10.0.2.146

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-0-2-146 ~]$

クライアント(Mac)のルーティングテーブルを確認します。

$ netstat -rn

Routing tables

Internet:
Destination        Gateway            Flags        Netif Expire
default            link#17            UCSI         utun2
10/16              link#17            UCS          utun2
100.91.173.90/32   link#17            UCS          utun2
100.100.100.100/32 link#17            UCS          utun2
100.126.250.25     100.126.250.25     UH           utun2
224.0.0/4          link#17            UmCSI        utun2
255.255.255.255/32 link#17            UCSI         utun2

...

※ Tailscale 向けのものを抜粋

100.91.173.90 はAWS VPC内のサブネットルーター、100.126.250.25 はクライアント(Mac)のIPアドレスです。

RDS の接続テスト

パブリックIPアドレスを割り振っていない RDS の場合、VPC外でもホスト名はプライベートIPアドレスで名前解決されます。

同様にして、RDSに接続することも可能です。

$ dig +short database-1.xxx.eu-central-1.rds.amazonaws.com
10.0.3.176

$ mysqlsh --uri=admin@database-1.xxx.eu-central-1.rds.amazonaws.com:3306
...
 MySQL  database-1.xxx.eu-central-1.rds.amazonaws.com:3306 ssl  JS >

利用費

小規模な個人用途であれば、Tailscaleは無料、その他のケースでは有料です。

詳細は Pricing ページをご確認ください。

Pricing · Tailscale

最後に

WireGuardをベースにしたVPNサービスTailscaleを用い、AWS VPC内のリソースにプライベート接続する方法を紹介しました。

Tailscaleを使いこなしたい場合、マニュアルの他、次の技術ブログの一読をおすすめします。

また、WireGuard については、プロトコル考案者によるBlack Hat 2018での発表がおすすめです。

それでは。

参考