iptables で VPN 専用ホスト: tun+ ではなく tun デバイスを名指しする
tun+ はマシン上のあらゆるトンネルに一致します。デバイス名を一つに固定し、endpoint の例外を先に置き、ポリシーを試す間は復元経路を残します。
-o tun+ と書くのは正確な指示に見えますが、実際にはカーネルが持つすべてのトンネルに一致するワイルドカードです。二台目の VPN クライアント、コンテナブリッジ、クラッシュしたセッションが残したインターフェースのいずれもこれに一致し、ホストはポリシーが防ぐはずだった通信を許可してしまいます。以下の手順はデバイス名を一つに固定し、トンネルが実際に確立できるよう例外を順序立て、ルールがまずく入ったときの戻り道を残します。各ステップは単独で取り消せる程度に短くしてあります。
ステップ 1: 今あるルールを記録する
デフォルトポリシーを変える前に、現在の状態を保存して、ロールバックが記憶ではなくコマンドになるようにします。iptables-save > /root/iptables.pre はすべてのテーブルを復元できるファイルに書き出します。これから設定するポリシーがそれを運ぶ接続を落とすのですから、root セッションを開いた二つ目の端末は過剰警戒ではありません。遠隔から作業する場合は、デッドマンスイッチとして flush も予約しておきます。
次にインターフェースを一覧します。思い込んだ名前は本来読むべきだった名前です: ip -br link は今存在するものを表示し、トンネルは起動するまで現れません。読んだデバイス名はそのまま書き留めます。
ステップ 2: トンネルに固有の名前を与える
OpenVPN はクライアント設定で dev tun_work を受け付け、Linux は tun0 を割り当てる代わりに、まさにその名前のインターフェースを作ります。ほかに名乗るものがなければ、名前は再接続をまたいで安定し、デバイスを名指しするルールが意味を持つのはこのおかげです。
名前を制御できない場合は、代わりにトンネルのアドレスやパケットマークで一致させます。どちらも機能し、どちらも読みにくく、トンネルが番号を変えるとどちらもより静かに失敗します。いずれにせよ、一致させる対象は一つに絞り、どれなのかを明示します。
ステップ 3: endpoint の例外を先に置いてポリシーを書く
VPN 専用ホストとは、短い例外リストを持つデフォルト拒否のホストであり、最も重要な例外はトンネルの存在を許すものです。endpoint のルールをトンネルデバイスのルールより前に置いて、この順序で組み立てれば、ハンドシェイクの行き先が確保できます:
# allow the tunnel to be built, then everything the tunnel carries
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -d 198.51.100.7 -p udp --dport 1194 -j ACCEPT
iptables -A OUTPUT -o tun_work -j ACCEPT
iptables -A INPUT -i tun_work -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTendpoint のリテラルなアドレスとポートはこのページではなく、あなたのプロバイダーのものです。endpoint がホスト名なら一度だけ解決して結果を固定してください。ルールは名前を引けませんし、次に DNS が変われば回転する答えがポリシーを静かに壊します。ポートも確認してください。TCP 443 に移すプロバイダーなら、誰も書いていないルールが別途必要になります。単一プロトコルでしか動かないトンネルは、多くの場合ファイアウォールの不具合ではなく、この抜けです。
ルーターでは 後ろのクライアントのために同じ形を FORWARD チェーンに入れ、ルーター自身の通信には独自のルールが要ります。転送パケットだけを覆うルールセットでは SSH と DNS が古い経路に残り、トンネルが上がった瞬間にルーターが到達不能になるのはこのためです。
ステップ 4: 存在だけでなく順序を確認する
ルールは順に評価されるので、先に来る ACCEPT は後から足した REJECT をただ隠します。established と related の一致があるからこそ、一般的な受信経路を開かずに自分の外向き要求への応答が戻ります。トンネルの受け入れ後に置けば、落ちたトンネルでも自分のインターフェース上で戻り通信は通ります。
ルール数は一画面で読める程度に抑えます。監査できないルールセットは、例外を先頭に足して場当たり的に直すことになり、デフォルト拒否のポリシーが徐々にデフォルト許可の縁を持つのはそういう経路です。変更の前後で ACCEPT を数えます。
ステップ 5: 起動とアドレスファミリーを越えて生き残らせる
手で入れたルールは再起動で消えるので、ディストリビューションのファイアウォールサービスか、トンネルインターフェースができた後に走るユニットから読み込みます。OpenVPN の起動前に走るスクリプトで tun_work を参照するのは、落ちた通信の起動ループを作るよくある方法です。
次に IPv6 を別に確認します。IPv4 の良いデフォルト拒否ポリシーはもう一方のファミリーについては何も語りません。ip -6 route は、IPv4 しか扱わないトンネルを迂回して通信を運びうる経路がホストにあるかを教えてくれますし、ip6tables や nftables には同等のルールが別に要ります。ルールセットの対称性を信じるのではなく、IPv6-only の宛先で試してください。
ステップ 6: 検証し、復元できるようにしておく
トンネルを上げ、意図したサービスが動くことを確かめてから、ポリシーが拒否すべきものを試します。答えが意外なら iptables-restore < /root/iptables.pre で復元し、それから初めてルールの編集を始めます。永続化と試した復元経路がそろって初めて、これは二度できる変更になります。