2026年8月2日
2026年8月2日
DNAT の設定方法|Linux の iptables・nftables・ufw・firewalld でポート転送を作り、永続化して確認する
DNAT(Destination NAT)は、届いたパケットの宛先 IP やポートを書き換えて別のホストやポートへ転送する仕組みで、Linux では「宛先を書き換えるルール」「転送を許可するルール」「カーネルの転送の有効化」の3点をそろえると動きます。この記事では、Linux ゲートウェイで外側のポート 8080 を内側の Web サーバー 192.168.10.20:80 へ転送する例を使って、iptables と nftables での書き方、ufw と firewalld での設定、再起動後も残す方法、動作の確かめ方、そして戻り経路や送信元 IP の扱いといったつまずきやすい点を説明します。
DNAT が動く仕組み
DNAT のルールは、パケットがホストに入ってきた直後(PREROUTING)に評価されます。宛先が書き換わった後でルーティングの判断が行われるため、書き換え後の宛先が別ホストならパケットは「転送」の扱いになり、フィルタの FORWARD チェーンを通ります。
このため、DNAT のルールだけを書いても通信は通りません。
- NAT テーブルで宛先を書き換える(DNAT)
- フィルタで転送を許可する(FORWARD)
- カーネルの IP 転送を有効にする(
net.ipv4.ip_forward=1)
加えて、内側のサーバーからの応答が同じゲートウェイを通って戻ってくる必要があります。戻りがこのゲートウェイを通れば、変換表(conntrack)を使って送信元が自動で元に戻され、相手に届きます。
作業はリモート接続を切ってしまう可能性があるため、現在のルールを保存し、コンソール接続などの戻す手段を用意してから始めてください。本番機の前に検証環境で試すのが安全です。
sudo iptables-save > ~/iptables-before.txt
sudo nft list ruleset > ~/nft-before.txt
カーネルの転送を有効にする
一時的に有効にするだけなら sysctl -w で足りますが、再起動で戻るので設定ファイルにも書いておきます。
# 今すぐ有効にする
sudo sysctl -w net.ipv4.ip_forward=1
# 再起動後も有効にする
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/90-ip-forward.conf
sudo sysctl --system
iptables で設定する
外側のインターフェースを eth0、内側を eth1 とします。
# 1. 外側に届いた 8080 番宛てを 192.168.10.20:80 へ書き換える
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 \
-j DNAT --to-destination 192.168.10.20:80
# 2. 書き換え後のパケットの転送を許可する
sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp -d 192.168.10.20 --dport 80 \
-m conntrack --ctstate NEW -j ACCEPT
# 3. 確立済みの通信(応答を含む)の転送を許可する
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
FORWARD のルールでは、宛先ポートが書き換え後の 80 になっている点に注意してください。PREROUTING で宛先が変わった後に評価されるため、ここに 8080 と書くと一致しません。
FORWARD の既定ポリシーが DROP で、ほかに許可ルールが無い環境では、-A(末尾に追加)で足したルールが既存の拒否ルールより後ろになって効かないことがあります。sudo iptables -L FORWARD -n -v --line-numbers で順番を確認し、必要なら -I FORWARD 1 のように位置を指定して挿入します。
ゲートウェイ自身から転送先へ接続したい場合
ゲートウェイの上で curl http://localhost:8080/ を実行しても、PREROUTING を通らないため DNAT は効きません。ローカルで生成したパケットに適用したい場合は OUTPUT チェーンに同様のルールを書きますが、通常は外側の端末から試せば十分です。
nftables で設定する
nftables を使うディストリビューションでは、次のような設定を /etc/nftables.conf に書くのが分かりやすい方法です。既存のファイルがある場合は丸ごと置き換えず、テーブルを追加する形で編集してください。
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "eth0" tcp dport 8080 dnat to 192.168.10.20:80
}
}
table inet filter {
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "eth0" oifname "eth1" ip daddr 192.168.10.20 tcp dport 80 ct state new accept
}
}
ip ファミリーのテーブルでは dnat to アドレス:ポート と書けます。inet ファミリーのテーブルに NAT のルールを書く場合は dnat ip to ... のようにアドレスの種類を指定する必要があります。
構文の確認と反映は次のとおりです。-c を付けると、反映せずに文法だけを確かめられます。
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables
policy drop の forward チェーンを新しく作ると、それまで許可されていたほかの転送も止まります。既存の転送がある環境では、先に必要な許可ルールを洗い出してください。
ufw と firewalld で設定する
ufw(Ubuntu など)
ufw には DNAT 専用のコマンドが無いため、設定ファイルを直接編集します。
/etc/default/ufwのDEFAULT_FORWARD_POLICYを確認する。DROPのままなら、転送を個別に許可する/etc/ufw/sysctl.confでnet/ipv4/ip_forward=1を有効にする/etc/ufw/before.rulesの先頭(*filterより前)に NAT の記述を追加する
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.10.20:80
COMMIT
転送の許可は sudo ufw route allow in on eth0 out on eth1 to 192.168.10.20 port 80 proto tcp のように追加し、sudo ufw reload で反映します。
firewalld(RHEL 系など)
firewalld では転送ポートの指定で DNAT を作れます。別ホストへ転送する場合は、ゾーンでマスカレードを有効にする必要があるバージョンが多いので、あわせて設定します。
sudo firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.10.20 --permanent
sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-all
細かな挙動はバージョンで異なるため、使っているディストリビューションのドキュメントもあわせて確認してください(2026年10月現在)。
再起動後も残す
iptables のルールはそのままでは再起動で消えます。Debian や Ubuntu では iptables-persistent(netfilter-persistent)を入れて保存します。
sudo apt install iptables-persistent
sudo netfilter-persistent save
nftables は /etc/nftables.conf を nftables サービスが起動時に読み込みます。ufw と firewalld は、上記の手順で設定ファイルに書いた内容がそのまま残ります。
動作を確認する
確認は、モバイル回線の端末など本当に外側にある端末から行います。同じ LAN の中から自分のグローバル IP へ接続すると、ヘアピン NAT が必要になり、設定が正しくても失敗します。
# 外側の端末から
curl -I http://203.0.113.5:8080/
# ゲートウェイで、ルールに当たった件数を見る
sudo iptables -t nat -L PREROUTING -n -v
# 変換中の接続を見る(conntrack-tools が必要)
sudo conntrack -L -p tcp --dport 8080
カウンターが増えているのに応答が無い場合は、転送先のサーバーで tcpdump -ni any tcp port 80 を動かし、SYN が届いているか、応答がどのゲートウェイへ出ているかを確かめます。
つまずきやすい点
戻りの通信が別のルーターへ出ていく
転送先のサーバーのデフォルトゲートウェイが、DNAT をしているホストと違うと、応答が別の経路へ出てしまい、相手に届きません。デフォルトゲートウェイをそろえるのが基本の対処です。そろえられない場合は、ゲートウェイの POSTROUTING で送信元も書き換える(SNAT / MASQUERADE)方法があります。
送信元を書き換えるとサーバーから接続元が見えなくなる
SNAT を加えると、サーバーから見た接続元は常にゲートウェイの IP になります。WordPress のアクセスログ、ログイン試行回数の制限、コメントのスパム判定など、接続元 IP を使う機能がまとめて同じ IP として扱われます。DNAT だけなら接続元の IP は保たれるので、送信元の書き換えは戻り経路の問題を解決するために必要な場合に限るのが無難です。
Docker が動いているホスト
Docker の -p オプションによる公開も内部では DNAT で実現されており、Docker は独自のチェーンをルールに追加します。ufw で拒否したつもりのポートがコンテナ経由で開いていることがあるため、Docker のあるホストでは sudo iptables -t nat -L -n -v で Docker のチェーンも含めて確認してください。
まとめ
- DNAT は宛先の書き換え、FORWARD での許可、IP 転送の有効化の3点がそろって動く
- FORWARD のルールは書き換え後の宛先とポートで書く
- iptables は netfilter-persistent、nftables は
/etc/nftables.confで永続化する - 確認は外側の端末から行い、ルールのカウンターと conntrack で当たり方を見る
- 戻り経路をそろえることを優先し、SNAT は接続元 IP が見えなくなる影響を理解したうえで使う