nftablesとfirewalldのNAPT設定

# AlmaLinux9のNAPTの設定方法

AlmaLinux9で、NAPT（IPマスカレード）を設定する方法について、です。ここまでの2記事の続きです。
- 1記事目：環境を作り、疎通を確認。踏み台は単にNIC間で転送するだけ。サーバ側に応答先を設定。
    - https://qiita.com/nonbiri-yama/items/bca242a9d40d132590ed
- 2記事目：踏み台にNAPTを設定。設定はiptablesで行った。
    - https://qiita.com/nonbiri-yama/items/a7a4090bdea470f49e6f

この記事は、踏み台のNAPTの設定を、nftablesと、firewalldで、それぞれ、設定してみたものです。

# テスト環境

### (1) テスト環境のおさらい
テスト環境は、この記事で作った環境です。

https://qiita.com/nonbiri-yama/items/bca242a9d40d132590ed

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468232/527dcdf3-8e3a-4ee8-8e7c-2fc80a0c4ba3.png)

- 登場するホスト：
    - 踏み台（nettest1）
    - サーバ（rhel8host）
    - クライアント（nettest2）
- 関係するネットワーク：
    - パブリックネットワーク（192.168.11.0/24、図中のオレンジ線）
    - プライベートネットワーク（192.168.51.0/24、図中のブルー線）

踏み台にNAPTの設定を行い、クライアントからサーバにリクエストを送って、期待通りに動作するか確認します。


# 1. nftablesによるNAPT設定

RHEL8以降、LinuxカーネルのNetfilterに対する設定（filter、nat、route）は、iptablesではなく、nftablesに置き換わっています。iptablesコマンドは使えますが、これは、内部的にはnftablesが動いており、後方互換のため、iptablesコマンドでも要求／設定参照できるためです。nftablesのCLIはnftコマンドです。

また、RHEL7以降のファイアウォールといえば、firewalldです。このfirewalldとnftablesの関係性としては、
- LinuxカーネルのNetfilterに対する設定 = nftables
- OSのファイアウォールの設定 = firewalld
ですね。

nftablesだけでも、上手く設定すれば、OSのファイアウォールを設定することができますし、細かく設定調整ができますが、例えば、eth0にsshだけ許可し他は拒否したい、といった標準的なファイアウォールで運用したいなら、firewalldを使うと楽に実現できます。firewalldに対して設定を行うと、内部的にはnftablesの設定が作られて実現されます。（nft list table inet firewalldコマンドで確認できる）

そういった情報は、ここにあります。

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/8/html/securing_networks/assembly_migrating-from-iptables-to-nftables_getting-started-with-nftables#when-to-use-firewalld-nftables-or-iptables_assembly_migrating-from-iptables-to-nftables


まずは、nftablesサービスを有効化します。
```bash
systemctl enable --now nftables
```

nftablesのデフォルトの設定ファイルが、/etc/nftables/に置いてあります。となりに、こういう、NAPTのための設定ファイルを作ります。

```bash:/etc/nftables/masq.nft
table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        oifname "eth0" masquerade
    }
}

table ip filter {
    chain forward {
        type filter hook forward priority 0; policy drop;
        ct state related,established accept
        iifname "eth1" oifname "eth0" accept
        iifname "eth0" oifname "eth1" accept
    }
}
```

また、main.nftにて、作成したmasq.nftがインクルードされるよう、「include "/etc/nftables/masq.nft"」の記述を末尾に追加します。

```bash:/etc/nftables/main.nft
# Sample configuration for nftables service.
# Load this by calling 'nft -f /etc/nftables/main.nft'.

# Note about base chain priorities:

～

# Uncomment the following line to enable masquerading of
# forwarded traffic. May be used with or without router.nft.
#include "/etc/nftables/nat.nft"

include "/etc/nftables/masq.nft"
```

変更した設定ファイルを、反映します。
```bash
nft -f /etc/nftables/main.nft
```

これで、「ip nat」と「ip filter」のテーブルが追加されました。
```bash
[root@nettest1 ~]# nft list tables
table inet firewalld
table inet nftables_svc
table ip nat
table ip filter
[root@nettest1 ~]#
```

これで、nettest2からのcurlとpingは、rhel8hostは応答を返しました。
また、ルータ（192.168.11.1）も、pingに応答しました。


# 2. firewalldによるNAPT設定

RHEL7以降では、通常、OSをインストールするとfirewalldサービスが有効です。firewalldサービスとnftablesサービスは排他的なので、前述の、nftablesサービスを有効化して設定する方法は、firewalldサービスを無効にする必要があり、ちょっと使いにくいです。

ということで、firewalldサービスでNAPT設定する方法です。
eth1-to-eth0-napt、というポリシーを作成します。そして、internalゾーンをingress-zone、publicゾーンをegress-zoneに設定します。
（この設定は、「マスカレーディング」と呼ぶそうです。）

```bash
[root@nettest1 ~]# firewall-cmd --permanent --new-policy eth1-to-eth0-napt
success
[root@nettest1 ~]#
[root@nettest1 ~]# firewall-cmd --permanent --policy eth1-to-eth0-napt --add-ingress-zone internal
success
[root@nettest1 ~]#
[root@nettest1 ~]# firewall-cmd --permanent --policy eth1-to-eth0-napt --add-egress-zone public
success
[root@nettest1 ~]#
```

publicゾーン側に、masqueradeを設定します。
```bash
[root@nettest1 ~]# firewall-cmd --permanent --zone=public --add-masquerade
success
[root@nettest1 ~]#
```

反映します。
```
[root@nettest1 ~]# firewall-cmd --reload
success
[root@nettest1 ~]#
```

行った設定は、ここには表示されません。

```bash
[root@nettest1 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth1
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  protocols:
  forward: yes
  masquerade: yes
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
[root@nettest1 ~]#
[root@nettest1 ~]# firewall-cmd --list-all --zone=internal
internal (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client mdns samba-client ssh
  ports:
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
[root@nettest1 ~]#
```

ポリシーは、以下で確認できます。

```bash
[root@nettest1 ~]# firewall-cmd --info-policy eth1-to-eth0-napt
eth1-to-eth0-napt (active)
  priority: -1
  target: REJECT
  ingress-zones: internal
  egress-zones: public
  services: http
  ports:
  protocols: icmp
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
[root@nettest1 ~]#
```

ポリシーに、masqueradeを設定します。（要らなかったかも。邪魔はしないけど）

```bash
[root@nettest1 ~]#
[root@nettest1 ~]# firewall-cmd --permanent --policy eth1-to-eth0-napt --add-masquerade
success
[root@nettest1 ~]#
```

あ、ゾーンとNICの対応がずれていました。（eth1=internal、eth0=publicにしたかった）

```bash
[root@nettest1 ~]#  firewall-cmd --get-active-zones
internal
  interfaces: eth0
public
  interfaces: eth1
[root@nettest1 ~]#
```

直します。

```bash
[root@nettest1 ~]# firewall-cmd --zone=internal --change-interface=eth1 --permanent
The interface is under control of NetworkManager, setting zone to 'internal'.
success
[root@nettest1 ~]#
[root@nettest1 ~]# firewall-cmd --zone=public --change-interface=eth0 --permanent
The interface is under control of NetworkManager, setting zone to 'public'.
success
[root@nettest1 ~]#
[root@nettest1 ~]#  firewall-cmd --get-active-zones
internal
  interfaces: eth1
public
  interfaces: eth0
[root@nettest1 ~]#
[root@nettest1 ~]# systemctl restart firewalld
[root@nettest1 ~]#
```

ポリシー設定です。

```bash
[root@nettest1 ~]# firewall-cmd --info-policy eth1-to-eth0-napt
eth1-to-eth0-napt (active)
  priority: -1
  target: ACCEPT
  ingress-zones: internal
  egress-zones: public
  services: http
  ports:
  protocols: icmp
  masquerade: yes
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
[root@nettest1 ~]#
```

これで、pingが通ります。

```
[root@nettest2 ~]# ping -c3 192.168.11.201
PING 192.168.11.201 (192.168.11.201) 56(84) bytes of data.
64 bytes from 192.168.11.201: icmp_seq=1 ttl=63 time=1.27 ms
64 bytes from 192.168.11.201: icmp_seq=2 ttl=63 time=1.77 ms
64 bytes from 192.168.11.201: icmp_seq=3 ttl=63 time=1.93 ms

--- 192.168.11.201 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 1.265/1.655/1.931/0.283 ms
[root@nettest2 ~]#
[root@nettest2 ~]# curl http://192.168.11.201
<html><body>Node: fd90d330e6d9(10.88.0.2), Pod: my-httpd(10.88.0.2)</body></html>
[root@nettest2 ~]#
```

# まとめ

nftablesとfirewalldでNAPTの設定を試しました。