Linuxでnftablesを使ったVPN Kill Switchの構築:IPとDNSの漏洩を完全に防ぐ方法

Network tutorial - IT technology blog
Network tutorial - IT technology blog

VPN接続が突然切断されると、Linux OSはパケットを物理ネットワークインターフェースへ自動的にフォールバック(経路変更)してしまいます。その結果、気付かないうちに本来のIPアドレスやDNSクエリがインターネット上へ丸見えになってしまいます。この現象は一瞬で発生するため、リモートワークやプライベートなブラウジング中の身元漏洩につながるリスクがあります。

Kill Switchはこの問題を解決するために設計されました。安全回路(ブレーカー)のように機能し、VPN接続が切断された瞬間に外部への通信を即座に完全遮断します。

LinuxでVPN Kill Switchを実装する手法

現在、この仕組みを導入するには主に4つのアプローチがあります。

  • VPNクライアント付属の機能: 商用アプリ内のKill Switchスイッチや、設定ファイル内のup/downスクリプトフックを利用する。
  • UFW(Uncomplicated Firewall): 簡潔な構文で扱いやすいが、実態はiptablesのルールを生成している。
  • 従来のiptables: 古くからLinuxで使われてきた実績ある方式で、パケットフィルタリングテーブルを直接操作する。
  • 最新のnftables: パケットフィルタリングの最新フレームワーク。Linuxカーネル3.13以降でiptablesを完全に置き換え、Debian 10やUbuntu 20.04以降ではデフォルトとなっている。

各ソリューションの実用的な比較

運用環境へ導入するにあたり、それぞれトレードオフが存在します。

1. VPNクライアント内蔵のKill Switch

  • メリット: ワンクリックまたは簡単なコマンド1つですぐに有効化できる。
  • リスク: VPNプロセスがクラッシュしたり、メモリ不足(OOM killer)で強制終了された場合に脆弱になる。プロセスが異常終了するとクリーンアップスクリプトが実行されないため、OSは通常のWi-Fiや有線LAN経由でデータを送信し続けてしまう。

2. UFWファイアウォール

  • メリット: 直感的な構文で、Linux初心者でも理解しやすい。
  • リスク: 仮想インターフェース(wg0、tun0など)が頻繁にオン・オフされる環境では柔軟性に欠ける。DHCPでIPアドレスが再割り当てされた際、UFWの静的ルールによってローカルネットワーク接続が途切れてしまうトラブルが起きやすい。

3. 従来のiptables

  • メリット: 普及率が高く、CentOS 7などの古いディストリビューションでもドキュメントが豊富。
  • リスク: IPv4用(iptables)とIPv6用(ip6tables)で2つの別々のファイルを作成・管理する必要がある。1行変更するだけでもカーネルがルールテーブル全体をリロードするため、CPUリソースの無駄が生じ、既存の接続が切断される可能性がある。

4. 最新のnftables

  • メリット: IPv4とIPv6を単一のテーブル(family inet)に統合可能。アトミックトランザクション方式を採用しており、実行中の接続を切断することなく数マイクロ秒で即時反映される。カーネル内で直接動作するバイトコードエンジンにより、パケットフィルタリングのパフォーマンスが非常に優れている。
  • デメリット: ブロック構造の構文が独特なため、1行完結のiptablesコマンドに慣れている場合は習得に少し時間がかかる。

なぜnftablesが最も信頼できる防御壁なのか?

ネットワークセキュリティの基本原則は、「防御壁はエンドポイントのアプリケーションから独立していなければならない」という点です。VPNソフトウェアに不具合が発生しても、カーネル層の防壁はそのまま有効であり続ける必要があります。

カフェのWi-Fiや不安定なモバイル回線を利用していると、VPN接続が切断されてから1〜3秒で自動再接続することがよくあります。このわずか数秒の隙間に、システムは通常のネットワークカードを経由して何十ものDNSクエリやTCPパケットを密かに送信してしまいます。nftablesを使えば、「VPNサーバーへの接続ポートを除き、すべての外部トラフィックをブロックする」というデフォルトポリシーを定義できます。これにより、データ漏洩のリスクを根本から排除できます。

nftablesによるKill Switchの構築手順

ステップ1:ネットワーク情報の確認

ターミナルを開き、既存のネットワークインターフェース一覧を確認します:

ip -brief link
# enp3s0   UP      52:54:00:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
# wg0      UNKNOWN 

環境に合わせて以下のパラメータを事前に把握しておきます:

  • インターネット接続用の物理インターフェース:enp3s0(Wi-Fiの場合はwlan0など)
  • 仮想VPNインターフェース:wg0(WireGuard)またはtun0(OpenVPN)
  • VPNサーバーのIPアドレス:203.0.113.50
  • VPNポートおよびプロトコル:51820 UDP(WireGuard)または1194 UDP(OpenVPN)
  • ローカルLANのサブネット:192.168.1.0/24(プリンター、NAS、ルーターへのアクセス用)

ステップ2:nftablesのインストール

Debian、Ubuntu、Linux Mintの場合は、次のコマンドを実行します:

sudo apt update && sudo apt install -y nftables
sudo systemctl enable nftables

ステップ3:ブロッキングルールの作成

nftablesの設定ファイルを開きます:

sudo nano /etc/nftables.conf

ファイルの内容を以下の標準ルールセットに置き換えます:

#!/usr/sbin/nft -f

flush ruleset

table inet vpn_killswitch {

    chain output {
        # デフォルトですべてのアウトバウンドトラフィックを破棄
        type filter hook output priority filter; policy drop;

        # 1. ローカルループバックを許可
        oifname "lo" accept

        # 2. 確立済みの正常な接続を維持
        ct state established,related accept

        # 3. DHCPによる内部IP割り当て要求を許可
        udp dport { 67, 68 } oifname "enp3s0" accept

        # 4. ローカルネットワークリソース(プリンター、ルーター、NAS)へのアクセスを許可
        ip daddr 192.168.1.0/24 oifname "enp3s0" accept

        # 5. 物理ポート経由のVPNサーバーとのハンドシェイク接続のみを許可
        ip daddr 203.0.113.50 udp dport 51820 oifname "enp3s0" accept

        # 6. VPNトンネル経由のすべての通信を許可
        oifname "wg0" accept
    }

    chain input {
        # 不正なインバウンドトラフィックを破棄
        type filter hook input priority filter; policy drop;

        # ループバックおよび確立済み接続の応答を許可
        iifname "lo" accept
        ct state established,related accept

        # ローカルルーターからのDHCP応答を許可
        udp sport { 67, 68 } iifname "enp3s0" accept

        # LANサブネットからのパケットを許可
        ip saddr 192.168.1.0/24 iifname "enp3s0" accept
    }
}

ステップ4:設定の反映と有効化

システムを再起動することなく、ルールセットを即座に適用します:

sudo nft -f /etc/nftables.conf

カーネル上で現在有効なルールセットを確認します:

sudo nft list ruleset

ステップ5:漏洩防止の実機テスト

設定しただけで安心せず、必ずテストを行いましょう。以下の3ステップで切断状態をシミュレートします:

  1. VPNが正常に動作している状態での確認:
    curl -4 ifconfig.me
    # VPNサーバーのIPアドレス(203.0.113.50)が返ってくることを確認
  2. VPN切断障害のシミュレーション:
    sudo wg-quick down wg0
    curl --connect-timeout 5 https://ifconfig.me

    5秒後にターミナルにConnection timed outまたはCould not resolve hostと表示されれば、Kill Switchは正常に機能しています。enp3s0を経由する外部宛てパケットは、カーネルによって完全に遮断されています。

  3. IPv6漏洩対策の確認:

    多くのプロバイダではIPv4と同時にIPv6も自動割り当てされます。この設定ではtable inetでpolicy dropを適用しているため、VPNを経由しないIPv6による名前解決やデータ通信もすべて遮断され、IPv6からの身元特定リスクも防ぐことができます。

設定ファイル1つだけで、カーネル層での堅牢な保護機構を実現できます。これにより、安全なVPNトンネルが稼働している間だけデータが外部へ送信されるようになります。

Share: