トンネルを止めずに一台のOpenVPNクライアントをLANから遮断する
クライアントのトンネルアドレスを証明書に固定し、FORWARDの先頭でLAN向け通信を落とし、IPv6・追跡済みセッション・再起動後の維持を確認します。
OpenVPNサーバーの一クライアントだけLANアクセスを失わせ、他のトンネルはすべて維持したい。これを検索すると、たいてい「デーモンにはできない」という答えが上位に出るが、それは半分しか正しくない。OpenVPNにクライアント単位のファイアウォールはないが、トンネルを終端するホストがすでにパケットをフィルタしている。重要な判断は二つ、ブロックする身元とルールの位置だ。この二つを正せば、変更はテストして削除できる一行になる。
ルールを書く前にクライアントを固定する
OpenVPNはプールからトンネルアドレスを貸し出すので、10.8.0.6は人ではなく席である。セッションが終われば貸与は戻り、次の接続に渡ることがある。つまりそのアドレスに対するルールは、ユーザーではなく席を追いかける。だからまず、本当に何を止めるのかを決める。短い実験のためのアドレスか、締め出したい相手の証明書か。
アドレスを証明書に結びつけるには、client-config-dirで指定したディレクトリにファイルを作り、証明書のcommon nameをそのままファイル名にする。中に指示を一つ、ifconfig-push 10.8.0.6 255.255.255.0と書けば、そのクライアントは再接続しても同じトンネルアドレスを保つ。有効にするには二つの設定が要る。client-config-dirをサーバー設定で宣言すること、そして配布するアドレスがサーバーの経路にあるサブネット内であること。どちらかが欠けるとファイルはエラーも出さず無視され、次のファイアウォール試験が誤解を招く。
一時的な遮断ならプールのアドレスでもよいが、その時点でどのセッションが保持していたかをチケットに記録する。
順序がドロップの実行を決める
トンネルとLANの間を転送するLinuxゲートウェイでは、パケットはFORWARDチェーンを通る。下のコマンドを文として読む。FORWARDの先頭に挿入し、tun0から入る、その一台だけの、LANサブネット宛ての通信に一致させて落とす。
iptables -I FORWARD -i tun0 -s 10.8.0.6/32 -d 192.168.1.0/24 -j DROPルールを動かすのは-Iである。-Aで追加すると、トンネルサブネット向けの広い許可の下に落ちてパケットを一切見ない。位置はiptables -vnL FORWARD --line-numbersで確認し、カウンターも読む。先頭のドロップが何も数えていないなら、通信は別の経路で来ている。
ゲートウェイ自身のLANアドレスは別経路である。箱そのもの宛てのパケットはINPUTへ行き、FORWARDは通らない。だからクライアントはLANホストを失ってもルーターの管理画面を開ける。それが問題になるなら対応するINPUTルールを足す。
フィルタが見えないもの
フィルタはパケット評価時に行われるので、確立済みのTCPセッションは閉じるかリセットされるまで続く。遮断を即座に効かせたいなら、ルールを入れた後にconntrack -D -s 10.8.0.6でその送信元の追跡フローを消す。数秒の重なりが許容できるなら、暗黙にせず選択として記録する。いずれにせよ、次に触る人は、このルールが進行中の通信を止めるのか新規接続だけなのかを知る必要がある。
ピア間の通信はホストのファイアウォールに一切触れない。client-to-clientが有効だと、OpenVPNはそのパケットをデーモン内部で転送するので、FORWARDもINPUTも見ない。直せるのはclient-to-clientを切るか、ピア同士が届くことを受け入れるかだけだ。iptablesのルールはその通信を見ない。
IPv6、ファームウェア、再起動後の維持
IPv4のルールはIPv6について何も語らない。トンネルとLANが両方のファミリーを運ぶなら、同等のルールがip6tablesに、あるいはそのホストのポリシーを持つnftablesルールセットに必要だ。ルーターのファームウェアはnetfilterを独自の抽象の裏に隠すことが多く、フォーラムからコピーした構文やテーブル名が通用しない場合がある。
OpenWrtでは文書化された道はファイアウォール設定の項目であり、手書きのiptables行ではない。手書きの行はファイアウォールの再読み込みで消えることがある。他のディストリビューションはnetfilter-persistent、起動ユニット、ベンダーの保存領域でルールを復元するが、これらは互換ではない。選んだ方法は再起動で証明すること。再読み込みは別の経路を試すだけだ。
経路ごとに確認し、その後きれいに戻す
経路は別々に試すこと。一つ成功しても他を隠してしまう。遮断されたクライアントはトンネル経由でインターネットには届き、LANホストには届かないはずだ。二台目のクライアントは同じホストに届くはずで、ゲートウェイアドレスとピア経路はそれぞれ独自の観察が要る。結果はルールの隣に書き、次の変更の基準にする。下の表はチケットに残す価値のある集合だ。
| 経路 | ルールありで期待されること | 失敗するとき |
|---|---|---|
| 遮断されたクライアントからLANホストへ | 応答なし | ルールが許可の下にある、または送信元が一致しない |
| 遮断されたクライアントからインターネットへ | tun0経由で動く | ルールが広すぎ、宛先一致がない |
| 二台目のクライアントから同じLANホストへ | 動く | ルールが意図したホストより多くに一致した |
| 遮断されたクライアントからゲートウェイのLANアドレスへ | 対応するINPUTルールがある場合だけ遮断 | INPUTルールがない |
| 遮断されたクライアントからピアのトンネルアドレスへ | それでも動く | client-to-clientが有効でファイアウォールの範囲外 |
戻すのは仕様による削除であり、フラッシュではない。iptables -D FORWARD -i tun0 -s 10.8.0.6/32 -d 192.168.1.0/24 -j DROPはその項目だけを消し、チェーンの残りはそのまま残す。起動時にルールを復元するファイルからも同じ行を消し、再読み込みして、クライアントが再びLANホストに届くことを確認する。FORWARDのフラッシュは書いていないルールまで戻すので、切り戻しが二件目の障害になる。
チケットを閉じる前に三つ確認する。固定したアドレスが証明書と一致していること、ルールが許可より上にありカウンターが非ゼロであること、永続化ファイルにテストしたのと同じルールがあること。どれか欠ければ、その遮断はシェルセッション内の幻想だ。