🌐

Tailscale Subnet RouterでAWS Client VPNを代替する

開発や運用などでVPC内リソースへアクセスするためにAWS Client VPNを構築することは大変一般的だと思います。
しかしながら、開発人数が1~数名などの小・中規模プロジェクトにおいて、AWS Client VPN Endpointの時間課金はどうしてもコスパが悪くなります。(ユーザー数が少なくても多くてもベース料金は変わらないため)

そこで今回はAWS Client VPNよりもコスパ良く、かつ簡便にセットアップが可能なTailscaleと呼ばれるVPNサービスを利用して、AWS VPCにVPN接続可能にする手順をコード付きで解説します。

ちなみにTailscaleには以下のような特徴があります。

  • VPNトンネルにWireGuardプロトコルを利用し、速度が高速。
  • ユーザー認証にOAuth2.0、Open ID Connect、SAMLによるSSOが利用可能。
    • WireGuardのように面倒なユーザー作成とconfの書き換えや配布が要らないです。
      • 実際の現場では複数の会社が絡むのが当たり前で、これが理由でWireGuardの利用が憚られていましたが、これをTailscaleは克服した形です。
  • クライアント間通信が可能。
    • Tailnet(後述)に参加しているクライアント間は相互に内部IPを通じて通信可能です。

構成イメージ

ざっくり手順

以下だけです。

Subnet Routerインスタンス構築の手順

1.Tailscaleでアカウントを作成。
2.Authkeyを発行。
3.EC2インスタンスに接続し、tailscaledをセットアップ。

VPNクライアント(Windows、Macなど)側の手順

  1. Tailscaleアカウントを作成する or 招待してもらう。
  2. TailscaleクライアントをInstallする。
  3. Tailscaleクライアントを起動し、Tailscaleアカウントで認証を済ませてVPN接続を開始する。

こまかく手順

前提

  • 例では VPC CIDR = 172.31.0.0/16 を想定。(各々のVPC CIDRに適宜読み替えをお願いします。)
  • Amazon Linux 2023 のEC2を1台用意(t4g.nanoでOK/パブリックでもプライベートでも可)。
  • セキュリティグループは Outbound許可(ALL)、InboundはSSH(22)だけで十分。
    ※Tailscaleは基本 Outbound UDP:41641(+TCP:443) だけ出られれば動きます。

1. Tailscale側の準備

  1. Tailnet作成/ログイン(Google/Microsoft/OktaなどのSSOでOK)。

    初回ログインを行うと、Tailnetと呼ばれるものが作成されます。

    このTailnetにこの後の手順でEC2インスタンスやクライアント(Windows、Macなど)を追加することになります。

  2. 管理画面 → Settings → Keys → Generate auth key。

    • Reusable:OFF
    • Ephemeral:OFF(永続)
    • Preauthorized:ON(非対話で接続)
      → 表示された Auth Key ( tskey-auth-XXXXX )を控える。(この後の「Subnet Routerをセットアップ」で使います。)
  3. (任意・おすすめ)自動Approve を有効化:
    管理画面 → Access controls で ACL を編集し、以下を追加。
    これで今後、どんなサブネットを広告しても自動承認される。

    {
      "autoApprovers": {
        "routes": {
          "172.31.0.0/16": ["<TailscaleのAdminアカウントのメールアドレス>"]
        }
      }
    }
    

2. Subnet Routerをセットアップ

Session Manager、Instance Connect、SSH、何でもよいですが、EC2に接続してカーネルパラメータの変更やtailscaledのインストール、起動を行います。
以下にユーザーデータの例を記載します。

UserData

#!/bin/bash
#cloud-config
set -euxo pipefail

# ルーティング有効化(サブネットルーターに必須)
cat <<EOF > /etc/sysctl.d/99-tailscale.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sysctl -p /etc/sysctl.d/99-tailscale.conf

# Tailscale インストール & 起動
curl -fsSL https://tailscale.com/install.sh | bash
systemctl enable --now tailscaled

# Tailnet に非対話で参加+Tailnetにルート広告を行う
TAILSCALE_AUTH_KEY="YOUR_AUTH_KEY" # 実際のものに差し替えてください
ADVERTISE_ROUTES="172.31.0.0/16" # 適宜差し替えてください

tailscale up \
  --auth-key=${TAILSCALE_AUTH_KEY} \
  --advertise-routes=${ADVERTISE_ROUTES}

# 状態確認
tailscale status || true

3. ルートの承認(自動承認を入れてない場合のみ)

Subnet Routerのセットアップが正常に完了していれば、管理画面→MachinesにEC2インスタンスがMachineの1つに表示されていると思います。

後は下記画像のように右側の三点リーダーを押すとEdit Route Settingsが表示されるのでそこをクリックするとApproveが可能です。

ACLで自動Approveルールを入れている場合、こちらの処理は不要です。

4. クライアント側(Mac / Windows / Linux)

1.ユーザーをTailnetに参加させる
同じTailnetに参加するために、Tailnetアカウントの作成、もしくはTailnetに参加済みのユーザーから招待を受けておく必要があります。(VPN接続開始時にTailnetアカウントで認証をする必要があるため。)
Tailnetへのユーザーの追加、招待は管理画面→Users→Invite usersから実行可能です。

Tailnet作成者のドメインと参加したいユーザーのドメインが異なる場合は、Outside my orgを選択すると招待リンクの作成が可能です。

2. Tailscaleクライアントをインストール
管理画面→Machinesの下記画像の「Client Device」という箇所をクリックすると

このように各OS用のクライアントダウンロードページを確認可能です。(ダウンロードリンクをコピーできる他、指定したメールアドレスにLinkを送付できるようです。関係者にインストール手順を連携するのに使えそうですね)

ダウンロード後の詳しい操作は各OSの手順に従ってください。(VPN接続開始の許可などの操作が必要になるかと思います。)

Tailscaleを起動すると、クライアントのルートテーブルに自動でVPC CIDR(Subnet Routerがtailscale upをするときにadvertise-routesオプションで指定したCIDR)へのルートが追加されます!
無事ルートが追加されていれば、これで晴れてVPC内へ通信が可能になります。

以上。

料金比較(AWS Client VPN vs Tailscale)

詳細な料金は Tailscale公式サイト に記載されていますが、ここでは簡単に「AWS Client VPNをVPCに1か月アタッチした場合」と「TailscaleのStarterプラン」を比較してみます。

AWS Client VPN の場合(東京リージョン)

  • VPCアタッチ料金(Association):$0.15/時間

  • 1か月(720時間)あたり:

    → $0.15 × 720 = 約 $108/月

(参考:AWS VPN料金ページ)

Tailscale Starter プランの場合

  • 料金:$6 / ユーザー / 月
  • $108 ÷ $6 = 18ユーザー分

つまり、利用ユーザーが18人以下であれば、Tailscaleの方が安いという計算になります。

まとめ

少人数(数人程度)のVPN利用であれば、Tailscaleを使うことで大幅なコスト削減が見込めます。

ただ、Subnet Routerの役割をするEC2インスタンスを管理しないといけないこともあり、
最終的には、運用コストと管理負担のバランスを見てどちらを採用するか検討するのがよいでしょう。

参考

https://tailscale.com/blog/how-tailscale-works

Discussion