Linux 上の Transmission:プロセス所有者単位で外向き通信を強制する
ピアポートだけでは足りない。ファイアウォール規則をクライアントのアカウントに絞り、両方のアドレスファミリーを覆い、トンネルを落とした状態で検証する。
Transmission の受信ポートを塞ぐことは VPN ポリシーではない。トレントクライアントはピアやトラッカーへ外向き接続を開き、名前を解決し、遠隔操作用のリスナーを露出させうる。そしてそのどれもが、意図していなかったインターフェースから出ていきうる。五つの検査を順番にこなし、境界はクライアントの設定ではなくファイアウォール規則だと考える。最後の 二つは人が飛ばしがちな検査であり、トンネルが落ちたときに保証が成り立つかを決めるのはこの 二つである。
転送を所有するプロセスを特定する
デスクトップアプリとシステムデーモンは別々のアカウントで動いており、所有者ベースの規則はプログラム名ではなくアカウントに従う。規則を書く前に、どちらが動いているのかを確かめる。専用のサービスアカウントならポリシーに正確な主体を与えられるが、デスクトップユーザーはブラウザやエディタ、セッション内のその他すべてと同一性を共有してしまう。パッケージのアカウント名がそのまま使われていると決めつけず、UID を確認する。
systemctl status transmission-daemon
ps -o user=,pid=,cmd= -C transmission-daemon
アカウントがまだ無いなら、この用途のために作り、他には何も動かさない。アカウントと UID は規則のコメントに書く。主体の無い規則は、誰も安全に編集できない規則だからだ。
待ち受け項目と経路を切り分ける
Transmission 4.x はピアの待ち受けアドレスを rpc-bind-address とは別に記録しており、この違いは重要だ。RPC の設定が制御するのは遠隔操作のリスナーだけなので、変更してもピア通信の経路は選ばれない。二つの設定を一つとして読むことが、見た目は丁寧な構成がそれでも漏れる最も多い原因である。同じ文書は、ピアの待ち受けアドレスが無効な場合のフォールバックにも触れている。つまり利用できないアドレスは文書化されたフォールバックであり、必ず拒否されるわけではない。
grep -E '"(peer-port|rpc-bind-address|bind-address-ipv4)"' /var/lib/transmission-daemon/.config/transmission-daemon/settings.json
クライアントの制御項目はその本来の用途に使い、保証はファイアウォールに置く。待ち受け設定にも価値はあるが、トラッカーとの接触、ローカル探索、委任された問い合わせがすべてトンネルから出ることを示しはしない。両者が食い違うとき、決めるのはファイアウォールである。
所有者マッチを正しいチェーンに書く
Netfilter の所有者マッチに関するマニュアルは、ローカルで生成されたパケットがソケットファイルの所有ユーザーやグループにどう帰属するかを説明している。その帰属は転送される通信には及ばず、補助グループの一致には明示的なオプションが要る。実際に使っている拡張のマニュアルを読み、たまたま見つけた最初の例に従わないこと。
iptables -A OUTPUT -m owner --uid-owner transmission -o tun0 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner transmission -j REJECT
ip6tables -A OUTPUT -m owner --uid-owner transmission -o tun0 -j ACCEPT
ip6tables -A OUTPUT -m owner --uid-owner transmission -j REJECT
順序はマッチと同じくらい重要だ。チェーンの前にある広い許可は、後ろの精密な拒否を無効にする。だから規則を足して願うのではなく、チェーン全体を見直す。ホストが IPv6 を動かすなら両方のアドレスファミリーを入れよ。v4 だけのポリシーは、誰も見ていない経路を開けたままにする。クライアントとトンネルの再起動をまたいでも規則を残しておく。
tun0 が消えたときに何が起きるかを決める
障害時の方針こそがこの演習の要点だ。トンネルが消え、同じフックが規則も外してしまうなら、クライアントは他の何かが止めるまで物理インターフェースを使える。安全な設計は規則を残し、拒否させる。これが境界をトンネルの健康状態から独立させる。規則まで消す撤去フックは、障害時方針の正反対である。
ip link show tun0
iptables -S OUTPUT
意図的に試す。トンネルを止め、捨てても安全な転送を始め、何か出ていくかを見る。そのあとトンネルを戻し、想定した経路が再び機能することを確かめる。
規則から見えない経路まで覆う
名前解決は別のリゾルバプロセスが担うことがあり、名前空間や専用ゲートウェイも、共有と起動の経路がそう設計されていればもう一つの境界になる。DNS をどのコンポーネントが所有し、同じ出口規則に従うのかを決める。次に RPC を見る。rpc-whitelist を使っているなら、承認済みの管理網は契約の一部であり、広い RFC1918 の範囲はそうではない。
ss -tunp | grep -i transmission
インターフェースのカウンタは観察の裏付けにはなるが、どのプロセスがバイトを生んだかは証明できない。捨ててよい転送を行う。まず動く経路で、次に起動時にトンネル無しで、そのうえで中断、再接続、新しく作ったインターフェースを経由する。新しい試行だけでなく、すでに開いていた接続も試すこと。規則が古いソケットに適用されていない可能性がある。日付、アカウント、結果をテストと一緒に記録する。
チェーン全体を流し消す例を貼るのは、一つの穴をいくつかと引き換えにすることになる。だから規則は消すのではなく足す。五つの検査がすべて通れば、保証は明示的だ。クライアントの通信はトンネルから出るか、まったく出ない。そしてそれを強制する規則は、トンネル自身の障害を生き延びる。