Linux バイパスルーター上で UFW と sing-box TUN を同時に実行すると、誤診しやすい障害が発生することがあります。sing-box 自体は正常に動作しているのに、LAN 上のデバイスがバイパスルーター経由でインターネットにアクセスできない状態です。その時点で遮断されていた TCP ポートを許可したとしても、sing-box を再起動すると問題が再発することがあります。
本稿では、実際に観察された原因と、回帰検証待ちの修復方法を記録します。例はすべて文書予約アドレスを使用し、実際のネットワークには対応していません。
環境と症状
- Linux バイパスルーター。LAN インターフェースは
eth0、アドレスは192.0.2.1(例示のみ)。 - 下流端末
192.0.2.20は、IPv4 ゲートウェイとしてバイパスルーターを使用します。 - sing-box の TUN インターフェース名は
tun0で、auto_routeとauto_redirectを有効にしています。 - UFW は既定で、許可されていない着信トラフィックを拒否します。
sing-box の主要設定:
{
"type": "tun",
"interface_name": "tun0",
"auto_route": true,
"auto_redirect": true
}
障害発生中のログは次のようなものです:
[UFW BLOCK] IN=eth0 OUT= SRC=192.0.2.20 DST=192.0.2.1 PROTO=TCP DPT=24461 SYN
DPT=24461 は一度の実行で観察された動的ポートにすぎないため、UFW に固定して書き込むべきではありません。OUT= が空であることから、このパケットは FORWARD 経路を通るだけでなく、本機の INPUT 経路に入り込んでいることが示唆されます。
NAT リダイレクトの追跡
sing-box の待ち受けを確認します:
sudo ss -lntup | grep sing-box
nftables を確認します:
sudo nft -a list ruleset | grep -n -C 8 -E 'redirect|tproxy'
障害発生時のシナリオでは、次のようなルールが見られます:
meta l4proto tcp redirect to :24461
# 内部端口不允许直接连接
tcp dport 24461 ct status ! dnat reject with tcp reset
つまり、sing-box はまず宛先の TCP 接続を本機の待ち受けポートにリダイレクトし、その後、UFW の INPUT ルールが許可するかどうかを決定します。この種類のトラフィックを通常の FORWARD パケットとして許可しても、観察された INPUT での遮断は通常解消されません。
修復方針:動的ポートではなく DNAT 接続にマッチさせる
明確に信頼できる単一の下流端末については、一時的に次を試せます:
sudo iptables -I ufw-before-input 1 \
-i eth0 \
-s 192.0.2.20/32 \
-d 192.0.2.1 \
-p tcp \
-m conntrack --ctstate DNAT \
-j ACCEPT
例示の IP とインターフェースを実際の値に置き換えてください。DNAT は --ctstate の有効な仮想状態であり、--ctstatus ではありません。--ctstatus を使用すると Bad ctstatus "DNAT" というエラーが発生します。
このルールには依然として安全上の制約があります。sing-box が作成した接続だけでなく、条件を満たす DNAT TCP 接続にもマッチします。したがって、送信元と入口インターフェースを制限し、手間を省くために INPUT 全体に対する許可設定を行うべきではありません。
検証
下流デバイスで次を実行します:
curl -4 -v --connect-timeout 5 https://www.cloudflare.com/
バイパスルーター側でルールのマッチ数と遮断ログを監視します:
sudo iptables -L ufw-before-input -n -v --line-numbers
sudo journalctl -k --since '10 minutes ago' --grep='UFW BLOCK' --no-pager
カウンターが増加し、インターネット接続が復旧し、さらに sing-box を再起動して内部ポートが変化した後も正常であれば、この方法が現在の環境に適していると判断できます。
永続化
検証が成功したことを確認したら、まずバックアップを取得します:
sudo cp -a /etc/ufw/before.rules /etc/ufw/before.rules.bak
sudo nano /etc/ufw/before.rules
*filter セクションで、すべてのチェーン定義の後ろ、既存の ufw-before-input 処理ルールの前に次を追加します:
-A ufw-before-input -i eth0 -s 192.0.2.20/32 -d 192.0.2.1 -p tcp -m conntrack --ctstate DNAT -j ACCEPT
チェックしてからリロードします:
sudo iptables-restore --test < /etc/ufw/before.rules
sudo ufw reload
ロールバック時は、バックアップを使ってファイルを復元し、sudo ufw reload を実行します。一時的なテストルールも、挿入時と完全に一致するマッチングパラメータを指定して iptables -D で削除できます。
注意事項
ufw default allow routedを直接使用したり、UFW 全体を無効にするものを長期的な解決策にしないでください。- この方法は主に IPv4 TCP リダイレクト後の INPUT 遮断を対象とするものであり、すべての UDP、IPv6、WireGuard の障害に対する汎用的な修復ではありません。
- API、DNS、SOCKS など、他の明示的に設定された待ち受けポートについては、実際の用途に応じてアクセス元を引き続き制限してください。
- UFW と sing-box のどちらも nftables を変更する可能性があります。ルールのマッチカウンターが増加しても接続が失敗する場合は、他のベースチェーンのフック優先度や後続の破棄ルールも確認してください。