incusのコンテナがネットワークに接続できない問題の調査と解決方法: NAT設定の手順とトラブルシューティング

## はじめに

incusの勉強中にincusのコンテナがネットワークに接続できない問題が発生した。今後同じ問題に遭遇した場合を考えて、ネットワーク接続問題の解決方法をメモしておく。

## 追記

公式FAQにネットワーク接続問題についての言及があった。Dockerを利用していると問題が発生するようだ。
- [なぜ私のインスタンスはネットワークアクセスがないのですか？](
https://incus-ja.readthedocs.io/ja/latest/howto/network_bridge_firewalld/#network-incus-docker)

## まとめ

#### 問題

incusのコンテナからインターネットに接続できなかった。コンテナとホストPC間ではパケットが到達していた。

#### 原因

今回のネットワーク接続問題の原因は２つ
- コンテナに対するNATルールが作成されていなかった
- FORWARDチェーンのデフォルトポリシーがDROPに設定されていた


#### 修正方法

##### NATルールの追加
コンテナから外部へのパケットは`eth0`のIPアドレスに変換して通信する

```bash
sudo iptables -t nat -A POSTROUTING -s 10.107.73.0/24 ! -d 10.107.73.0/24 -o eth0 -j MASQUERADE
```

| **コマンド**          | **意味**  |
|-----------------------|--------------------------------|
| iptables           | パケットフィルタリングとNATを設定するためのコマンド                   |
| -t nat             | NAT設定のためのテーブルを指定                  |
| -A POSTROUTING      | `POSTROUTING`チェーンにルールを追加。パケット送信元アドレスを変更       |
| -s 10.107.73.0/24   | 送信元アドレスが`10.107.73.0/24`のパケットに適用           |
| ! -d 10.107.73.0/24 | 宛先が`10.107.73.0/24`以外のパケットに適用                 |
| -o eth0            | 出力インターフェース`eth0`を介するパケットに適用            |
| -j MASQUERADE      | 送信元IPを`eth0`のIPアドレスに変換して通信                    |

##### FORWARDチェーンのデフォルトポリシーを変更

FORWARDチェーンのポリシーをACCEPTに変更します。これにより、コンテナからインターネットへのトラフィックが許可されるようになる。

```bash
sudo iptables -P FORWARD ACCEPT
```


## ネットワークが繋がらない問題の調査方法



#### コンテナの状況を確認

IPv4のIPアドレスは`10.107.73.0/24`だと確認できる

```bash
incus list
```

```bash: incus listの出力
+----------------+---------+----------------------+---------------------------------------------+-----------+-----------+
|      NAME      |  STATE  |         IPV4         |                    IPV6                     |   TYPE    | SNAPSHOTS |
+----------------+---------+----------------------+---------------------------------------------+-----------+-----------+
| first          | RUNNING | 10.107.73.58 (eth0)  | fd42:8159:b94:458:216:3eff:fea5:9726 (eth0) | CONTAINER | 0         |
+----------------+---------+----------------------+---------------------------------------------+-----------+-----------+
| noble          | RUNNING | 10.107.73.227 (eth0) | fd42:8159:b94:458:216:3eff:feb9:d5c4 (eth0) | CONTAINER | 0         |
+----------------+---------+----------------------+---------------------------------------------+-----------+-----------+
| remote-desktop | STOPPED |                      |                                             | CONTAINER | 0         |
+----------------+---------+----------------------+---------------------------------------------+-----------+-----------+
```

#### パケットの到達状況を確認

`ping`コマンドを使った下記の結果からホストPCとコンテナ間はパケットが届いているが、コンテナから外部ネットワークへパケットが届いていないことが確認できた。

- ホストPCからコンテナ`noble`にパケットが届くかを確認する [OK] 
- ホストPCから`8.8.8.8`にパケットが届くかを確認する [OK] 
- コンテナ`noble`からホストPCにパケットが届くかを確認する [OK]
- コンテナ`noble`から`8.8.8.8`にパケットが届くかを確認する [**NG**] 


```bash
ping 10.107.73.227
ping 8.8.8.8
incus exec noble -- ping 192.168.1.113
incus exec noble -- ping 8.8.8.8
```

:::note info
NAT (Network Address Translation) 設定やIPフォワーディングに関連していると推察される。
:::


#### NATの設定を確認する

`iptables`を使って、ホストがコンテナからのトラフィックをインターネットに向けてNAT（アドレス変換）しているか確認する。

```bash
sudo iptables -t nat -L -n -v
```

| **オプション** | **意味**     |
|----------------|-----------|
| -t nat| NATテーブルを対象にする |
| -L| 現在のルールをリスト形式で表示する|
| -n| 数値形式で出力する |
| -v| 詳細モードで表示する|

```bash: sudo iptables -t nat -L -n -v　の出力
Chain PREROUTING (policy ACCEPT 8485 packets, 1168K bytes)
 pkts bytes target     prot opt in     out     source               destination
 4010  265K DOCKER     0    --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 3901 packets, 971K bytes)
 pkts bytes target     prot opt in     out     source               destination
    1   278 DOCKER     0    --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT 3896 packets, 970K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 MASQUERADE  0    --  *      !docker0  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     0    --  docker0 *       0.0.0.0/0            0.0.0.0/0
```

現在のiptablesの設定を見ると、POSTROUTINGチェーンでMASQUERADEルールが存在するが、このルールはDockerのネットワーク (172.17.0.0/16) に対して適用されている。Incusのコンテナは別のサブネット (10.107.73.0/24) を使用しているため、これが適用されていないと考えられる。

#### NATルールの追加

コンテナが属するネットワーク (10.107.73.0/24) に対してもNATのMASQUERADEルールを追加する。

```bash
sudo iptables -t nat -A POSTROUTING -s 10.107.73.0/24 ! -d 10.107.73.0/24 -o eth0 -j MASQUERADE
```

`iptables`コマンドでNATルールを追加した結果を確認する。

```bash
sudo iptables -t nat -L -n -v
```


```bash: sudo iptables -t nat -L -n -v　の出力
...
Chain POSTROUTING (policy ACCEPT 3897 packets, 971K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 MASQUERADE  0    --  *      !docker0  172.17.0.0/16        0.0.0.0/0
    0     0 MASQUERADE  0    --  *      eth0    10.107.73.0/24      !10.107.73.0/24
...
```

設定したルールが適用されていることが確認できた。

:::note warn
残念ながらNATのルールを追加しただけでは ping が通らなかった。そこでポリシールールを確認する。
:::


#### `iptables`のポリシールールの確認

```bash
sudo iptables -L -v
```

| **オプション** | **意味**   |
|----------------|-----------------------------|
| -L | 現在のルールをリスト表示する |
| -v | 詳細モード   |


```bash: sudo iptables -L -vの出力
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy DROP 900 packets, 56584 bytes)
 pkts bytes target     prot opt in     out     source               destination
  900 56584 DOCKER-USER  all  --  any    any     anywhere             anywhere
  900 56584 DOCKER-ISOLATION-STAGE-1  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     all  --  any    docker0  anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 DOCKER     all  --  any    docker0  anywhere             anywhere
    0     0 ACCEPT     all  --  docker0 !docker0  anywhere             anywhere
    0     0 ACCEPT     all  --  docker0 docker0  anywhere             anywhere

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain DOCKER (1 references)
 pkts bytes target     prot opt in     out     source               destination

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DOCKER-ISOLATION-STAGE-2  all  --  docker0 !docker0  anywhere             anywhere
  900 56584 RETURN     all  --  any    any     anywhere             anywhere

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       all  --  any    docker0  anywhere             anywhere
    0     0 RETURN     all  --  any    any     anywhere             anywhere

Chain DOCKER-USER (1 references)
 pkts bytes target     prot opt in     out     source               destination
  900 56584 RETURN     all  --  any    any     anywhere             anywhere
```


iptablesの出力を見ると、`Chain FORWARD (policy DROP 900 packets, 56584 bytes)`のようにFORWARDチェーンのデフォルトポリシーがDROPに設定されている。これが原因でIncusコンテナからのトラフィックがインターネットに到達しない可能性が高い。コンテナからインターネットに出て行くパケットがこのFORWARDチェーンでドロップされているため、pingが通らない状態だと考えられる。

#### FORWARDチェーンのポリシーを変更

FORWARDチェーンのポリシーをACCEPTに変更する

```bash
sudo iptables -P FORWARD ACCEPT
```

ポリシーの変更を確認する

```bash
sudo iptables -L -v
```
```bash: sudo iptables -L -v の出力
...
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  900 56584 DOCKER-USER  all  --  any    any     anywhere             anywhere
  900 56584 DOCKER-ISOLATION-STAGE-1  all  --  any    any     anywhere             
...
```

FORWARDのpolicayがACCEPTに修正された


#### パケットの到達を確認する

コンテナからインターネットへのパケット到達を確認する

```bash
incus exec noble -- ping -c 4 8.8.8.8
```
```bash: incus exec noble -- ping -c 4 8.8.8.8 の出力
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=4.20 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=4.47 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=4.25 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=57 time=4.24 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 4.198/4.289/4.466/0.104 ms
```