1 ポイント 投稿者 GN⁺ 2024-05-13 | 1件のコメント | WhatsAppで共有
  • Wagは、WireGuard に多要素認証、ルート制限、デバイス登録を追加するプロジェクトで、MFA が必要なルートと常時アクセス可能な公開ルートを区別できる
  • 新規クライアント登録 API、高可用性、リアルタイムのユーザー更新と通知、Security Key・SSO・PAM・TOTP など複数の MFA 統合を提供する
  • サーバー運用には IP フォワーディングの有効化が必要で、手動実行時には iptableslibpam のインストール、iptables と WireGuard デバイス管理のための root 実行が必要
  • 管理は Web UI と CLI で可能で、CLI は startregistrationdevicesuserswebadmin の各サブコマンドで登録トークン、デバイスロック、MFA 初期化、Web 管理者アカウントを扱う
  • 制約として、クライアントごとに 1 つの AllowedIP のみをサポートし、主に Linux 専用で、Windows も一部の作業を行えば動作する可能性がある

Wagが追加するWireGuard機能

  • Wagは WireGuard に MFA、ルート制限、デバイス登録を追加する
  • ルートは、MFA 認証が必要な経路と、常時アクセス可能な 公開ルート に分けて定義できる
  • 新規クライアント登録のための簡単な API を提供する
  • 高可用性、リアルタイムのユーザー更新と通知をサポートする
  • MFA 統合には次の方式が含まれる
    • Security Key
    • SSO
    • PAM
    • TOTP
  • ドキュメントは Documentation で提供されている

インストールと実行条件

  • サーバーで フォワーディング を有効化する必要がある
    • IPv4 では net.ipv4.ip_forward=1 設定を使用する
    • IPv6 では net.ipv6.conf.all.forwarding=1 などの関連 sysctl 設定を使用する
  • Docker Compose の実行例では wagvpn/wag:latest イメージを使用する
    • 管理ページのポート例は 4433/tcp
    • 公開登録ページのポート例は 8081/tcp
    • WireGuard のポート例は 53230/udp
    • /dev/net/tun デバイスをコンテナに接続する
  • 手動インストールには iptableslibpam が必要
  • Wag は iptablesWireGuard デバイス を管理するため root で実行する必要がある
  • バイナリリリースには glibc 2.31+ が必要
  • ソースビルドには go1.23.1npm が必要

管理方法

  • 管理 UI を有効化したうえで Wag を設定すると、最初の管理者を作成し、パスワードを STDOUT に出力する
  • その後 Web UI にログインしてユーザーを管理できる
  • root ユーザーは CLI で Wag サーバーを管理できる
  • CLI の形式は wag subcommand [-options]
  • 対応するサブコマンドは次のとおり
    • start: Wag サーバーを起動し、デーモン化はしない
    • registration: 登録トークンの作成、削除、一覧表示を処理する
    • devices: WireGuard デバイスの一覧表示、削除、ロック、ロック解除、有効な MFA セッションの確認を処理する
    • users: ユーザー MFA 管理、ユーザー削除、アカウントロック、MFA 初期化を処理する
    • webadmin: Web UI 管理者ユーザーの追加、削除、一覧表示、アカウントのロックと解除を処理する
    • versionfirewall も対応コマンドに含まれる

登録トークンとMFAフロー

  • 新しいデバイス登録では、まず wag registration -add -username tester のようなコマンドで 登録トークン を作成する
  • 作成したトークンを公開登録エンドポイントに渡すと、WireGuard 設定のレスポンスを受け取れる
  • 返される設定には InterfacePrivateKeyAddressPeerEndpointPublicKeyAllowedIPsPersistentKeepAlive などの項目が含まれる
  • ユーザーはサーバーの VPN アドレスに接続して 2FA コード を入力する
  • セッションが期限切れになるまで継続する時間は設定ファイルで指定する

Web管理コンソール

  • 管理コンソールにログインするには Webserver.Management.Enabledtrue に設定する必要がある
  • コンソールでは sudo ./wag webadmin -add -username <your_username> -password <your-password-here> で Web 管理者アカウントを追加する
  • その後、管理リスニングアドレスにアクセスして認証情報を入力する
  • Web インターフェース自体では 管理者ユーザーの追加 はできない
  • 管理ポータルは外部に公開しないことが推奨され、ListenAddress127.0.0.1 または localhost に設定し、SSH フォワーディングで公開する方法が推奨される

主な設定項目

  • NumberProxies はクライアントの前段にある信頼できるリバースプロキシの数を指定し、Wag が X-Forward-For を反映してクライアント IP を解析できるようにする
  • Socket は Wag の制御ソケットで、変更すると同一マシン上で複数の Wag インスタンスを実行できる
  • NAT はマスカレードの有効・無効を切り替え、有効化するとすべてのトラフィックが VPN サーバーから始まったように見える
  • NATExcludeRangesNAT=true のとき NAT から除外する CIDR 範囲を指定する
  • ExposePorts は VPN サーバーのポートをクライアントに公開し、iptables ルールを追加する
  • CheckUpdates はデフォルトで無効で、有効化すると管理 UI が新しい Wag バージョンの通知を表示し、api.github.com にアクセスする
  • Acls はグループとポリシーを定義するが、初回実行時のみ 反映され、実行中は Web UI で編集する
  • Webserver には公開登録エンドポイント、トンネル MFA ポータル、管理ポータルの設定が含まれる
  • Wireguard ではデバイス名、リスニングポート、秘密鍵、VPN が担当するサブネット、MTU、DNS サーバーを設定する
  • Clustering にはクラスタ名、etcd クラスタ状態、ログレベル、witness ノード、データベースの場所、クラスタ証明書関連の設定が含まれる

ACLポリシーの動作

  • Policies は VPN がキャプチャするルートと、Wag を通過できるポート・プロトコルを定義する
  • ルール適用には サブネットのプレフィックス長 を使い、最も具体的なマッチがルートアクセスレベルを決定する
  • たとえば /16 を MFA として定義し、その中の特定の /32 を Allow として定義すると、より具体的な /32 が優先され、MFA なしでアクセス可能になる
  • この動作は v6.0.0 で変更され、それ以前は MFA ルートが常に優先されていた
  • 1 つのルートに複数のポリシーが定義されるとポリシーは合成され、MFA ルールが優先される
  • まだリリースされていないバージョンからは Deny ルールでルートアクセスを遮断できる
  • 最も具体的なルールは新しいルールの「バケット」を作るため、/32 バケットに deny しかない場合、同じ /32 の他ポートへのアクセスも許可されない可能性がある

ポートとプロトコルのルール

  • サービスアクセスはポートとプロトコルのルールで定義できる
  • サポートされるルールタイプは 3 種類
    • Any: 個別ルールがないか any キーワードを使うと、すべてのサービスとポートの組み合わせを許可する
    • Single Service: 192.168.1.1 22/tcp 53/udp のように、ホストの特定の TCP・UDP ポートを許可する
    • Ranges: 192.168.1.1 22-1024/tcp 23-53/any のように、ポート範囲を指定する
  • ポート範囲は低いポートを先に書く必要がある
  • ICMP にはポートがないため、1.1.1.1 icmp のようにポートなしで指定できる

制限事項と開発

  • Wag はクライアントごとに 1 つの AllowedIP のみをサポートする
  • この制限は、クライアントからサーバーへ接続する構成に適している
  • 主に Linux 専用 で、Windows は一部の作業を行えば動作する可能性がある
  • 開発モードでは、トンネルに入ってくるリクエストの IP をクライアント IP として設定するための環境変数を使える
  • テスト例として internal/routersudo go test -v . を実行する
  • 外部からの貢献については、機能追加やバグ修正の際、可能であればテストを書いて Pull Request を開くよう案内している

1件のコメント

 
GN⁺ 2024-05-13
Hacker News のコメント
  • 見たところ良さそうだが、いくつか気になる点がある。
    curl [http://public.server.address:8080/register_device?key=e83253...](<http://public.server.address/register_device/…;) という例と、「サービスは完全にテンプレート化されたレスポンスを返す」という説明を見ると、登録プロセスではクライアントが秘密鍵を作って公開鍵をサーバーに送るのではなく、サーバーが秘密鍵を作ってクライアントに送っているように見える。
    さらに例が HTTP なので、人々が HTTP でも問題ない選択肢だと思わないよう、少なくともそこは変えたほうがよさそうだ。
    セッションが期限切れになったとき、クライアントがそれに気づく方法があるのかも気になる。あるいは SSH セッションのようなものが単に止まるだけなのだろうか。
    Wi-Fi のキャプティブポータル検知のように動く WireGuard クライアントを時々探しているが、理想的には設定ファイルに persistentkeepalive のような 1 行を追加して URL を取得し、定期的に確認する方式だとよい。OK が返れば正常、応答がなければネットワーク問題、Location ヘッダーが返ればブラウザをその場所で開いてセッションの再認証などを行う、という形だ。
    まだそういうクライアントは見つけていない。

    • 登録 URL は任意で pubkey パラメータも受け取れるので、サーバーが秘密鍵を生成する方式に依存しなくてもよい。ドキュメントが不足していて紛らわしいのは確かだ。
      最後の質問に答えると、自分が使っている eBPF XDP では PASSDROPREDIRECT しかできない。そのため最も簡単な結果である PASS/DROP で処理しており、接続は単に止まる。
      ただしキャプティブポータル検知ページを wag の MFA リストに追加すれば、検知自体は構成でき、その後はブラウザが処理してくれるはずだ。
      wag でインターセプトやプロキシのように動作する機能を実装するつもりはない。それをやれば認証の期限切れやログアウト処理は多少簡単になるだろうが、目指す方向ではない。
    • そういう機能は本当に素晴らしそうなので、このプロジェクトの作者が検討してくれるとよいと思う。
    • 似たようなサーバーを作ったことがある。デバイスごとのクライアント証明書が必要で、それを使ってログインページに mTLS で接続し、その後 OIDC でユーザーを認証してトンネルを有効化する構成だったが、難しい部分はクライアントだった。
      Mac 向けに Go クライアントを1つ書き、Brew のコマンドライン wg を使って鍵生成も処理したが、粗削りで sudo が必要だった。
      ネットワーク権限を使うきちんとしたネイティブアプリがあればよいのだが、自分の手に余る。
  • セッション管理の問題をすでに扱っているのか、あるいは扱う予定があるのか気になる。
    本質的に WireGuard の鍵は永続的なセッションキーのようなものだ。
    WireGuard のトランスポート層を実装するソフトウェアがきちんとした VPN サーバーソリューションであるなら、セッション管理も実装すべきだと思う。つまりサーバーとの第2チャネルを通じて、セッションキーを定期的にローテーションし、セッションを終了し、IP アドレスを変更し、新しい経路を設定し、必要なら認証を繰り返すべきだ。

    • そういう用途なら Firezone を使うと思う。ユーザーにプラットフォームへ定期的にログインさせるオプションがあり、OIDC で外部 ID プロバイダーと組み合わせれば、セッション管理に対して非常に堅牢でシンプルな解決策になる。
    • wag の文脈で「永続的なセッションキー」が正確に何を意味するのかはよく分からない。
      WireGuard の鍵は wag サーバーと通信できるようにするものだが、実際のセッションはユーザーが認証済みかどうかを保持する eBPF マップで管理される。
      そのため、誰かが秘密鍵の材料を盗んだとしても、MFA で制限された経路にはアクセスできない。
    • GlobalProtect のような VPN クライアントを WireGuard で作るなら、クライアントごとの永続的な認証鍵を持たせ、それで VPN コントローラーまで初期トンネルを張り、その中で認証を行って別のセッションキーを受け取らせると思う。最初のトンネルは、認証を終えて実際のセッションキーを受け取ったらすぐ切断する構成だ。
    • 定期的なセッションキーのローテーション、セッション終了、IP アドレス変更、新しい経路設定、再認証のための第2チャネルというなら、実質的に IPsec の IKE プロトコルではないのか。普通に IPsec を使えばよいのでは?
  • TOTP コードの総当たりを防いでいるのか気になる。たとえばレート制限や再試行回数制限のようなものだ。
    コードをざっと見たが、そうした処理は見つけられなかった。
    想定しているシナリオは、誰かがブラウザで TOTP 入力 UI を開き、開発者ツールを立ち上げて、可能なすべての TOTP コードを繰り返し試すケースだ。

    • TOTP コードの総当たり対策はある。各認証にはユーザーが試行できる回数制限があり、それを超えるとアカウントがロックされ、管理者がロックを解除する必要がある。
      特に、なぜデバイスが認証を強制的に試みようとしているのかをユーザーに考えさせる意図もある。そうした状況はエンドポイント侵害を示している可能性があるためだ。
    • おそらくここだと思う: https://github.com/NHAS/wag/blob/cdbdbec3393fa86bf6c823117c8...
    • この実装の詳細は知らないが、普通は TOTP の段階まで進めるログイン情報、つまりユーザー名とパスワードをすでに持っているなら、そのユーザーはすでに侵害されている状態だ。
  • Headscale や Tailscale と非常によく似ているように聞こえる。WireGuard ネットワークを管理する代替手段が見えるのは良いことだ。
    機能がどこまで重なり、何が追加され、何が異なり、今後も実装しないものは何なのかを理解できる比較資料があるのか気になる。

    • WireGuard を使うという点では確かに似ている。
      ドキュメントに直接の比較は入れていないが、今のところ自分が向かいたい方向ではない。このプロジェクトは自分の必要に合っていて、かなり楽しい。
      Wag は、すべてが互いに到達でき、ルールがオーバーレイを定義する Tailscale 式のメッシュというより、明確な境界を望むハブアンドスポーク構成に向いている。
      wag と Tailscale はどちらも SSO 連携と、ユーザー保護のための事実上の 2FA を追加する。
      どちらにも登録方法と管理のための Web UI があるが、自分は Web 開発が好きではない個人開発者なので、Tailscale のほうがずっと洗練されているはずだ。
      確実に実装しないのは、セッションログアウト後にユーザーをリダイレクトするためのインターセプトや TLS プロキシの方面だ。主な理由は、今すぐそれを eBPF でやるのは自分には少し手に余り、動かすにはおそらく書く必要がある DNAT/SNAT コンポーネントを使いたくないからだ。
  • IPv4 専用というのは、WireGuard を選ぶサイトならもっとモダンな構成を持ち、自前のサービス用 ULA を多用していてもよさそうだ。

    • 近いうちに IPv6 サポートを追加する予定で、ユーザーの実際のローカルネットワークと衝突するリスクを減らすために、人々の IPv4 アドレスをプライベートな IPv6 空間にマッピングすることも考えている。
      ULA に言及したとき、特に念頭に置いていた点があったのか気になる。
    • ここで考えている ULA の利点が何なのか気になる。