Headscale導入・設定ガイド:TailscaleのControl Serverをセルフホストして構築するZero Trust VPN

Security tutorial - IT technology blog
Security tutorial - IT technology blog

背景&なぜHeadscaleをセルフホストするのか?

AWSやHetzner、自宅のホームラボなどに分散したサーバーの管理は、多くのエンジニアにとって悩みの種です。SSHのポート22を開放してIPホワイトリストで制限する運用は、カフェなどの出先から作業する際に非常に不便です。また、従来のOpenVPNを構築する手法にも大きなデメリットがあります。すべてのトラフィックが中央サーバーを経由するため、VPSがボトルネックとなり、Ping(レイテンシ)が大幅に悪化してしまいます。

Tailscaleは、WireGuardをベースにしたMesh VPNアーキテクチャにより、この課題をスマートに解決します。ゲートウェイを中継する代わりに、2台のマシンが直接P2Pで接続されます。しかし、Tailscaleの無料SaaSプランには制限もあります。ユーザー数が3人に制限されていること、サードパーティのコントロールプレーンに依存すること、そして社内データの厳格なガバナンスが求められるプロジェクトには適さない点です。

そこで登場するのがHeadscaleです。これはTailscaleの協調サーバー(coordination server)をオープンソースで再実装したプロジェクトです。専用のVPS上で調整サーバー全体を完全にセルフホスト・管理できます。ルーティングテーブル、ACL、ノード一覧の管理権限はすべて手元に維持できます。その一方で、クライアント端末側はApp Store、Google Play、Linuxリポジトリから提供される公式のTailscaleアプリをそのまま利用可能です。

LinuxサーバーへのHeadscaleのインストール

1 vCPU・1GB RAMを搭載した安価なVPS(Ubuntu 22.04/24.04またはDebian 12)で十分動作します。100ノード未満のホームラボ規模であれば、Headscaleデーモンの実際のメモリ消費量はわずか30MB〜50MB程度です。事前に、サーバーの固定IPにAレコードを向けたサブドメイン(例:vpn.itfromzero.com)を用意しておいてください。

ステップ1:バイナリファイルのダウンロードとインストール

プロジェクトのGitHub Releasesページにアクセスし、amd64アーキテクチャ向けの最新debパッケージを取得します。

export HEADSCALE_VERSION="0.22.3"
wget --output-document=headscale.deb \
  https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb

sudo dpkg -i headscale.deb

このパッケージにより、システムユーザーheadscaleが自動作成され、/var/lib/headscaleのデータディレクトリに対する権限設定と、/etc/headscale/config.yamlへのサンプル設定ファイルの配置が行われます。

ステップ2:NginxとLet’s Encryptによるリバースプロキシの構築

HeadscaleにはACMEを介してSSL証明書を自動取得する機能が備わっています。しかし、前面にNginxを配置する構成が最も安全かつ柔軟です。Nginxを挟むことで、証明書の一元管理、スムーズなWebSocket処理が可能になり、同一VPS上で他のWebサービスと共存させる際のルーティングも容易になります。

sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx

Headscale用の新しいバーチャルホストファイルを作成します。

sudo nano /etc/nginx/sites-available/headscale

以下の設定内容を貼り付けます。

server {
    listen 80;
    server_name vpn.itfromzero.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name vpn.itfromzero.com;

    ssl_certificate /etc/letsencrypt/live/vpn.itfromzero.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpn.itfromzero.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $server_name;
        proxy_redirect http:// https://;
        proxy_buffering off;
        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;
    }
}

/etc/nginx/nginx.confを開き、http {}ブロック内にWebSocket用の設定ブロックが定義されていることを確認します。

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

バーチャルホストを有効化し、Certbotを実行してSSL証明書を発行後、サービスをリロードします。

sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo certbot --nginx -d vpn.itfromzero.com
sudo nginx -t && sudo systemctl reload nginx

Headscaleの詳細設定

config.yamlファイルの設定

Headscaleのメイン設定ファイルを開き、接続パラメータを調整します。

sudo nano /etc/headscale/config.yaml

以下の重要項目を確認し、環境に合わせて修正します。

server_url: https://vpn.itfromzero.com:443
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090

ip_prefixes:
  - 100.64.0.0/10
  - fd7a:115c:a1e0::/48

derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  paths: []
  auto_update_enabled: true
  update_frequency: 24h

database:
  type: sqlite3
  sqlite:
    path: /var/lib/headscale/db.sqlite

systemd経由でサービスを有効化し、起動します。

sudo systemctl enable --now headscale
sudo systemctl status headscale

管理者ユーザーの作成

Headscaleでは、デバイスをユーザー(User、バージョン0.23未満ではNamespace)ごとにグループ分けして管理します。社内チーム用の最初のユーザーを作成しましょう。

sudo headscale users create admin-team

クライアントをHeadscaleサーバーに登録する

クライアントマシン(別のUbuntuサーバーや個人のノートPCなど)で、公式のTailscaleエージェントをインストールします。

curl -fsSL https://tailscale.com/install.sh | sh

# セルフホストしたコントロールサーバーに接続
sudo tailscale up --login-server https://vpn.itfromzero.com

ターミナルに以下のような認証リンクが表示されます。

To authenticate, visit:
https://vpn.itfromzero.com/register/nodekey:xxxxxxxxxxxxxxxxxxxxxx

プレフィックスnodekey:(クライアントのバージョンによってはmkey:)に続くキー文字列をコピーし、Headscaleサーバーに戻って承認コマンドを実行します。

sudo headscale nodes register --user admin-team --key nodekey:xxxxxxxxxxxxxxxxxxxxxx

AnsibleやDockerのentrypointなどを用いて自動ブートストラップを行いたい場合は、有効期限付きの使い捨てPre-auth keyを事前生成しておきます。

# 24時間有効な使い捨てキーを生成
sudo headscale preauthkeys create --user admin-team --expiration 24h

これにより、クライアント側では以下のコマンドを1回実行するだけで、即座にネットワークに参加できます。

sudo tailscale up --login-server https://vpn.itfromzero.com --authkey <YOUR_PREAUTH_KEY>

VPNネットワークの動作確認と監視

接続確認とMeshパフォーマンスの検証

Headscaleサーバー上で、現在オンラインになっているデバイス一覧を確認します。

sudo headscale nodes list

結果一覧には、Node ID、Hostname、所属ユーザー、CGNATアドレス(100.64.0.0/10の範囲)、およびオンライン状態が表示されます。

次に、クライアントマシンから接続品質をテストします。

# ネットワーク内のピアと対応するIPアドレスを一覧表示
tailscale status

# 直接ルーティングの確認(Ping)
tailscale ping 100.64.0.2

pong from ... via [public-ip]:port in 14ms (direct)という応答が返ってくれば、2台のマシンがWireGuardを介してP2Pハンドシェイクに成功したことを示しています。この状態では、ISP回線の理論上限に近い帯域幅が得られます。一方、via DERP(syd)と表示される場合は、両端が厳しいNAT(Symmetric NAT)配下にあるためリレー接続となっており、Pingが80〜150ms程度まで増加します。

Prometheusによるメトリクスの収集

Headscaleには、ポート9090でPrometheusメトリクスエンドポイントが組み込まれています。localhostからcurlコマンドを実行して動作確認できます。

curl -s http://127.0.0.1:9090/metrics | grep -E '^headscale_'

特に注目すべきメトリクスはheadscale_nodes_online_countとheadscale_sqlite_queries_totalです。150ノード未満のホームラボ規模であれば、デフォルトのSQLiteファイルで極めて軽快に処理できます。200ノードを超え、頻繁なルート更新が継続的に発生する規模に拡張する場合は、データベースロックを防ぐためにdatabase.typeをpostgresに切り替えることをおすすめします。

Share: