# `iptables` 基本介紹
> 以下紀錄自己學習 `iptables` 的筆記~
資料透過網路傳輸時,會被切割成一個一個的封包,封包裡面除了含有要傳送的 data,也含有這個封包的資訊,像是 souce address、protocol 等等資訊。
而說到 Linux 的防火牆,就會提到 `iptables`。當封包進到 `iptables` 裡面,`iptables` 就會查看這個封包的資訊,並依據我們設定的防火牆規則對這個封包做處理。
以下簡單介紹 `iptables` 和它的指令~
## Linux Firewall

`netfilter` 是運行在 kernel space 的,所以使用者沒辦法直接對他做操作;而 `iptables` 則是運行在 user space 的工具,使用者可以透過 `iptables` 對 `netfilter` 做設定。
從上面那張圖可以看到 `iptables` 其實是比較舊版的,且 `iptables` 是專門負責 IPv4 的 packet filtering 和 network address translation(NAT);IPv6 的部分則由 `ip6tables` 負責,另外還有 `ebtables` 和 `arptables` 分別負責 ethernet bridge frame table 和 address resolution protocol table。
而 Linux kernel 自 3.13 版開始,就改成使用 `nftables`。一個 `nftables` 的功能就涵蓋了舊版的四個工具的功能。
而 OpenWRT 則是自 22.03 版以後,預設上改成使用 `nftables`。
## `iptables` 組成
### Table、Chain、以及 Rule

上圖是一個很簡單的示意圖,`iptables` 裡面會有一個一個的 tables,每個 table 則會由多個 chains 所組成,而這些 chains 則由多個 rules 所構成。
- 依據對封包做的操作的不同,會有不同的 table,像是負責 network address translation 的 nat table、或負責封包過濾的 filter table
- Chain 則是依據封包處理流程的不同階段所命名,像是在做 routing 之前(`PREROUTING` chain)、或者是處理完 routing 之後(`POSTROUTING` chain)。
- Rule 就是我們所設定的防火牆規則。
- 每個 rule 都會有 target,target 就是要對這個封包做什麼「操作」。
- 當封包進入一個 chain,++就會開始從該 chain 的第一條 rule 開始依序比對++,假如該封包符合了第一個 rule,那就會對這個封包執行第一個 rule 所指定的「操作」,然後可能就會到下一個 chain 或被丟棄。
++假如前面的 rule 都沒有符合,那就會去比對下一條 rule++,依此類推。
- 最後,可以看到 chain 裡面不一定要有 rule,但一定要有 policy。Policy 跟 rule 一樣都會有 taget。前面有提到,封包一旦進入一個 chain,就會開始依序從第一個 rule 做比對,若有符合某個 rule,那就執行該 rule 的 target。但萬一全部的 rule 都比對過了,但都沒符合,或者是該 chain 裡面沒有半條 rule,那該怎麼處理這封包呢?這時候就是看該 chain 的 policy 的 target 為何,就會對封包執行 policy 的 target。
以 NAT table 為例,這個 table 的功能是對封包做 network address translation,那這個 table 有 `PREROUTING` chain 和 `POSTROUTING` chain,所以封包在 `PREROUTING` 和 `POSTROUTING` 階段會進入 NAT table 的 `PREROUTING` chain 和 `POSTROUTING` chain,並且依序比對這些 chain 裡面的 rule,

上圖是個簡單的示意圖說明若一個封包進到 `iptables` 的處理流程。
封包的處理流程主要依據不同的階段劃分,這裡簡單的用 Stage I、Stage II、以及 Stage III 作說明(後面會提到確切的流程)。
前面有提到,chain 的命名是依據封包處理的階段所命名,所以 stage I 會由某些 table 的 chain I 所組成,而 stage II 則由另一些 table 的 chain II 所組成,依此類推。
封包在每個 stage 會依序去執行不同 table 的 chain。一旦符合該 chain 的某個 rule,就執行該 rule 的 target。上圖列出了兩個最常見的 target:`ACCEPT` 和 `DROP`。`ACCEPT` 就是讓這封包通過,`DROP` 則是丟掉這個封包。
所以在上面的示意圖可看到,一旦封包符合了 rule,且該 rule 的 target 是 `ACCEPT`,那這個封包就會到下一個 chain,==**而不會繼續比對同一個 chain 後面的 rule**==。==所以 rule 的順序在 `iptables` 是很重要的一件事==!倘若 target 是 `DROP`,就會丟棄該封包、不再對它做處理。
### Rule 的順序很重要!

前面有提到,封包一旦進入一個 chain,就會依序去比對該 chain 的 rule,倘若符合某個 rule,就會執行該 rule 指定的 target。
所以再提醒一次~rule 的順序在 `iptables` 是很重要的~若順序錯誤,可能就達不到自己想要的效果!
此外,依據封包執行的結果,大致可將 target 分成兩類:terminating target 和 non-terminating target。
- Terminating target:
- 大部分的 target 都屬於這類,像是最常見的 `ACCEPT` 和 `DROP`。執行了這類的 target 後,封包就不會繼續比對同一個 chain 後續的 rule。例如,若是 `ACCEPT`,那封包就會到下一個 chain。
- Non-terminating target:
- 這類型的 target 執行後,還是會繼續比對後面的 rule,像是 `LOG`,顧名思義就是會記錄到 log。
### `iptables` 的 Tables 和 Chains

目前 `iptables` 內有這五個 tables,但可能會因 kernel 的 configuration 不同而有些許差異:
- `filter` table
- 最主要負責防火牆功能的 table,也是預設的 table,負責封包過濾的功能,像是 `ACCEPT`(接受)封包,或 `DROP`(丟掉)封包。
- `nat` table
- 主要負責處理 network address translation(NAT),像是更改封包的 source address(SNAT)、或是更改封包的 destination address(DNAT)。
- `mangle` table
- 此 table 的功能主要是修改封包的 header,像是修改 Time-To-Live(TTL)欄位、Quality of Service(QoS)欄位。此外,也能藉由 `MARK` target 對封包做標記。
- `raw` table
- `raw` table 的主要功能是將不需要做 connection tracking(連線追蹤)的封包做 `NOTRACK`,這樣這些封包就不會被 connection tracking,而可以減少 `netfilter` 的負擔。
- 由於若要將封包設定為 `NOTRACK`,就必須要在封包進到 `iptables` 時盡早對其做處理,所以在封包的 `PREROUTING` 和 `OUTPUT` 階段第一個經過的 table 就是 `raw` table(後面會有張整個流程的示意圖)。
- `security` table
- 此 table 是用在 Mandatory Access Control(MAC)networking rules。通常是 kernel 有啟用 Linux Security Modules(LSM),這個 table 才會有作用。有用到 LSM 的包括 Security Enhanced Linux(SELinux)。
- MAC 會透過對封包標示 Security Label 來控制是否可存取系統資源,所以此 table 主要的 target 就是對封包做 `SECMARK` 或 `CONNSECMARK` 標示,後續再由像是 SELinux 決定是否允許此封包存取系統。
- 當 `filter` table 的 chain 比對完之後,就會馬上比對 `security` table 的 chain。
:::info

如果是使用 OpenWRT 的話,OpenWRT 預設使用的 table 是 filter table、nat table、以及 mangle table。
若想要使用 raw table 的話,記得在編譯 kernel 前就要做好設定~
像是用 `make menuconfig`:
- `Kernel modules`
- `Netfilter Extensions`
- `<*> kmod-ipt-raw`
:::

內建的 chain 總共有五個,分別代表封包處理的五個階段:
- `PREROUTING`
- 封包從 network interface 進入,並且在做 routing decision 之前的階段。
- 做完 routing decision 之後會決定這個封包其目的地是本機、還是只是經過本機。
- 若封包的目的地是本機,那就會進入 `INPUT` chain。
- 若封包只是經過本機、目的地不是本機,那就會進入 `FORWARD` chain。
- `INPUT`
- 當封包通過 `PREROUTING` chain,且路由決策判斷其目的地是本機,就會進入 `INPUT` chain,可以在這裡決定本機是否要接受此封包。
- `FORWARD`
- 只是經過本機的封包、目的地不是本機。
- 通過 `FORWARD` chain 後會進入 `POSTROUTING`。
- `OUTPUT`
- 由本機發出的封包,進入 `iptables` 的第一個階段就是 `OUTPUT` chain。
- 通過 `OUTPUT` chain 後會進入 `POSTROUTING` chain。
- `POSTROUTING`
- 若只是經過本機而目的地非本機的封包、以及由本機發出的封包,在離開 network interface 之前的最後一個階段就是 `POSTROUTING` chain。
- 可以在這個階段修改封包的 source address(`SNAT` 或 `MASQUERADE`)。

每個 table 所內建的 chain。
<!--
依上面幾張 slides 所示,`iptables` 的 table 有 filter table、nat table、mangle table、以及 raw table。
- Filter table
- 最主要負責防火牆功能的 table,也就是負責封包過濾的功能,像是要 `ACCEPT` 某封包,或 `DROP` 某封包。
- 此 table 含有的 chain 的名稱:`INPUT`、`FORWARD`、`OUTPUT`
- Nat table
- 此 table 主要負責處理 network address translation(NAT),像是更改封包的 source address(SNAT)、或是更改封包的 destination address(DNAT)。
- 此 table 含有的 chain 的名稱:`PREROUTING`、`OUTPUT`、`POSTROUTING`
- Mangle table
- 此 table 含有的 chain 的名稱:`PREROUTING`、`INPUT`、`FORWARD`、`OUTPUT`、`POSTROUTING`
- Raw table
- 此 table 含有的 chain 的名稱:`PREROUTING`、`OUTPUT` -->
### 封包處理流程
<!--  -->

上圖是 `iptables` 的封包處理流程的簡單示意圖~
++假如封包從某個 network interface 進來(incoming packet),且目的地是本機(local processing),那這個封包就會經過++:
1. raw table 的 `PREROUTING` chain
- 決定此封包是否要做 connection tracking
2. mangle table 的 `PREROUTING` chain
3. nat table 的 `PREROUTING` chain
- `PREROUTING` 的 nat table 有可能會做 destination NAT、以變更目的地 address
4. 做 routing decision 判斷此封包目的地是本機(送往 `INPUT` chain)、或者是要被轉發到某個 network interface(送往 `FORWARD` chain)
5. mangle table 的 `INPUT` chain
6. filter table 的 `INPUT` chain
- 決定哪些封包可以進入本機
7. security table 的 `INPUT` chain
8. 送到本機(local procession)
++假如某個封包從 network interface 進來(incoming packet),但他的目的地不是本機、只是經過,最終會由某個 interface 出去(outgoing packet),那這個封包就會經過++:
1. raw table 的 `PREROUTING` chain
2. mangle table 的 `PREROUTING` chain
3. nat table 的 `PREROUTING` chain
- `PREROUTING` 的 nat table 有可能會做 destination NAT、以變更目的地 address
4. 做 routing decision 判斷此封包目的地是本機(送往 `INPUT` chain)、或者是要被轉發到某個 network interface(送往 `FORWARD` chain)
6. mangle table 的 `FORWARD` chain
7. filter table 的 `FORWARD` chain
8. security table 的 `FORWARD` chain
9. mangle table 的 `POSTROUTING` chain
10. nat table 的 `POSTROUTING` chain
- 在 `POSTROUTING` chain 的 nat table 可能會做 `SNAT` 或 `MASQUERADE` 以變更封包的 source address
- `SNAT` 是將封包的 source address 修改成固定的 IP address。
- `MASQUERADE` 則是將封包的 source address 修改成他要出去的 network interface 的 IP address。
11. 由 network interface 出去(outgoing packet)
++假如某個封包是由本機產生(locally generated packet),然後經由 network interface 出去(outgoing packet),那這個封包就會經過++:
1. 本機產生的封包會先進行 routing decision,以決定走哪條路由,之後才進入 `OUTPUT` chain
2. raw table 的 `OUTPUT` chain
- 決定本機發出的封包是否要做 connection tracking
3. mangle table 的 `OUTPUT` chain
4. nat table 的 `OUTPUT` chain
- `OUTPUT` chain 的 nat table 有可能會做 destination nat,更改封包的目的地。
5. filter table 的 `OUTPUT` chain
6. security table 的 `OUTPUT` chain
7. 在經過 `OUTPUT` chain,準備進入 `POSTROUTING` chain 之前,會先進行 re-route check,再重新計算一次路由
- 由於 `OUTPUT` chain 的 nat table 有可能會更改 destination address,所以才會需要再重新計算一次路由
:::info
PS. 若封包是由 `FORWARD` chain 到 `POSTROUTING` chain,那就不會做 re-route check。
因為這樣子的封包,其流程是 `PREROUTING` -> routing decision -> `FORWARD` -> `POSTROUTING`。
這樣的封包只有可能在 `PREROUTING` chain 的 nat table 才有可能做 destination nat 更改目的地 address,當他結束 `PREROUTING` chain 後就會做 routing decision,而 `FORWARD` chain 裡面並沒有 nat table,不可能更改目的地 address,所以在進入 `POSTROUTING` chain 前就不用做 re-route check。
:::
8. mangle table 的 `POSTROUTING` chain
9. nat table 的 `POSTROUTING` chain
- 在 `POSTROUTING` chain 的 nat table 可能會做 `SNAT` 或 `MASQUERADE` 以變更封包的 source address
- `SNAT` 是將封包的 source address 修改成固定的 IP address。
- `MASQUERADE` 則是將封包的 source address 修改成他要出去的 network interface 的 IP address。
10. 由 network interface 出去(outgoing packet)
## 一些常用的 `iptables` 指令

接下來簡單介紹一些比較常見的 `iptables` 指令~若要看更詳細的內容可以用 `man iptables` 指令查看。
上圖中第一類的指令是在指定的 table 中指定的 chain 做添加(`A` ppend)、刪除(`D` elete)、插入(`I` nsert)規則的指令。
- `-A`:新添加的規則會放在指定的 table 的指定的 chain 的==最後面==。
- `-D`:刪除某個規則,
- `-I`:將新的規則插入指定的 table 的指定的 chain 的指定的位子,
第二類的指令則可以新增(`-N`)或刪除(`-X`)使用者自訂義的 chain。
第三個指令則是可以設定指定的 table 的指定的 chain 的 policy。
最後一個則是可以列出指定的 table 的規則。
:::info
在設定 `iptables` 的時候都要有 root user 的權限,所以若是一般使用者的話記得指令前面都要加個 `sudo`。
:::
:::info
以上幾個指令,若沒有指定 table 的話,預設都會是對 filter table 做設定喔~
:::

這張 slide 簡單介紹怎麼設定一個規則(後面會介紹幾個我自己有用過的規則~看例子應該會更好懂~)
首先~我們可以指定這條規則的參數,像是:
- `-p [!]protocol_name`:指定某個 protocol 的封包。若有加 `!` 則會變成指定「非」某某 protocol 的封包。
- `-s [!]address[/mask]`:source address 是某某 address 的封包。
- `-d [!]address[/mask]`:destination address 是某某 address 的封包。
- `-j target`:符合這條規則的封包,要對他做的操作。
- `-g chain`:符合這條規則的封包,跳到指定的 chain。
- `-i [!]interface_name`:從某個 interface 進來的封包。
- `-o [!]interface_name`:要從某個 interface 出去的封包。
我們也要設定若某個封包符合規則,那要對這個封包做什麼操作(Target),像是:
- `ACCEPT`:讓這個封包通過
- `DROP`:把這個封包丟掉,不處理它
- `REJECT`:類似 `DROP`,但還會送封包回去說這個封包被拒絕
- `MASQUERADE`:將封包的 source address 改成他要出去的 interface 的 IP address
- `SNAT`:跟 `MASQUERADE` 一樣都會修改封包的 souce address,但 `SNAT` 是設定固定的 IP,`MASQUERADE` 則是依據指定的 network interface 的 IP 做設定。
接下來介紹幾個自己有設定過的 `iptables` 指令~

這個指令的功能是:
- 添加一個規則到 `nat` table 的 `POSTROUTING` chain 的最後一個位子
- 這個規則是說,只要是從 `enp1s0` 這個 network interface 出去的封包,都對他們做 `MASQUERADE`,也就將他們的 source address 改成 `enp1s0` network interface 的 IP address

這個指令的功能是:
- 添加一個規則到 `filter` table 的 `INPUT` chain 的最後一個位子
- PS. 若沒有指定 table 的話~都是對 `filter` table 做操作喔
- 這個規則是說,只要是 ICMP protocol 的封包,都讓他們通過

這個指令的功能是:
- 添加一個規則到 `filter` table 的 `FORWARD` chain 的最後一個位子
- 這個規則是說,只要是從 `enx256e` 這個 network interface 進來、且會從 `enp1s0` 這個 network interface 出去的封包,都讓他們通過

這個指令的功能是:
- 添加一個規則到 `filter` table 的 `FORWARD` chain 的最後一個位子
- 這個規則是說,只要是從 `enp1s0` 這個 network interface 進來、且會從 `enx256e` 這個 network interface 出去的封包,且若這些封包的狀態(`state`)是已建立(`ESTABLISHED`)連線的狀態,就讓這些封包通過

前面用 `iptables` 指令所設定的規則,在每次重開機後都不會存在,若希望能保留每次設定的規則,可以用 `iptables-persistent` 這個工具。
安裝 `iptables-persistent`:
```shell
$ sudo apt install iptables-persistent
```
每次設定完規則後就用以下指令儲存規則:
```shell
$ sudo netfilter-persistent save
```
這樣的話規則就會被儲存在以下檔案,並且在每次開機時都將其載入:
```terminal
/etc/iptables/rules.v4
/etc/iptables/rules.v6
```
此外,順便介紹另一個與 `iptables -L -n` 類似的指令,會將 `iptables` 的規則依序印出來:
```shell
$ sudo iptables-save
```
## 用 `iptables` 設定接受/拒絕 ssh 連線
若想簡單練習 `iptables` 的話,可以練習設定是否要接受/拒絕 ssh 連線~
若是 Ubuntu Desktop,需要先安裝 ssh server 這樣才能讓別人用 ssh 連進來,在 Ubuntu 的話主要都是安裝 `openssh-server`:
```shell
$ sudo apt update
$ sudo apt install openssh-server
```
用以下指令確認確認 `openssh-server` 是 `active (running)`:
```shell
$ systemctl status ssh
```

:::info
如果 Ubuntu 是虛擬機,且是 VirtualBox 虛擬機的話,記得要先對Ubuntu 虛擬機設定 `Bridged Adaptor`,且 `Name` 選擇 MacBook 連到 Wi-Fi 的 interface `en0`,這樣我的 Ubuntu 虛擬機就會跟我的 MacBook 在同一個 LAN 內:

:::
首先,我的 MacBook 透過 Wi-Fi 介面被分配到的 IP 是 192.168.50.180:

Ubuntu 虛擬機被分配到的則是 192.168.50.241:

### 設定 `iptables` 允許/拒絕 ssh 連線
由於 `ssh` 通常都是用 Transmission Control Protocol (TCP),且 ssh server 是監聽 port 22。所以若要允許/拒絕別人用 ssh 連過來,就要對 `iptables` 的 `filter` table 的 `INPUT` chain 設定說,若有封包是用 TCP protocol、且該封包的 destination port 是 22,我們就接受/拒絕。
如果是要接受所有 ssh 連線,就做以下設定:
```iptables
iptables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT
```
- `-t filter -A INPUT`:在 filter table 的 `INPUT` chain 添加(`A` ppend)新的規則
- `-p tcp --dport 22 -j ACCEPT`:對於使用 `tcp` protocol 且 destination port 是 22 的封包,允許這樣的封包通過
若要拒絕所有 ssh 連線,可以做以下設定:
```iptables
sudo iptables -t filter -A INPUT -p tcp --dport 22 -j DROP
```
或
```iptables
sudo iptables -t filter -A INPUT -p tcp --dport 22 -j REJECT
```
- `DROP` 就是丟掉這封包不理他,發送 ssh 連線請求的 client 端會過一陣子得到 timed out 的訊息
- `REJECT` 則會發送一個拒絕的封包,讓發出 ssh 連線請求的 client 知道被拒絕了
### Test 1 - `DROP` ssh 連線
若用 `iptables` 將 ssh 連線請求的封包設定為 `DROP`,並且用 `tcpdump` 查看是否有封包進來:

可以看到 MacBook 嘗試連過去時,並沒有收到被拒絕的訊息,而是過一段時間後出現 `Operation timed out` 的訊息,因為 Ubuntu 虛擬機的防火牆把這個封包丟掉而不處理它:

### Test 2 - `REJECT` ssh 連線
若用 `iptables` 將 ssh 連線請求的封包設定為 `REJECT`:

可以看到 MacBook 發送 ssh 連線請求後,會收到 `Connection refused` 的訊息:

### Test 3 - 只允許特定 IP 的 ssh 連線
接下來,在 LAN 裡面多加一個新的裝置,也就是我的 Marvell ESPRESSObin v7 開發板~他被分配到的 IP 是 192.168.50.141:

假設要設定只允許我的開發板的 IP 可以 ssh 連到 Ubuntu 虛擬機,不允許其他的 IP 來進來,就需要依序對 Ubuntu 虛擬機做以下設定:
1. 允許 192.168.50.141 的 ssh 連線請求:
```iptables
iptables -t filter -A INPUT -p tcp --dport 22 -s 192.168.50.141 -j ACCEPT
```
2. 其他的 ssh 連線請求全部都拒絕:
```iptables
iptables -t filter -A INPUT -p tcp --dport 22 -j REJECT
```
這時候 rule 的順序就很重要!如果這兩條規則設定的順序反了,那封包一進到 `INPUT` chain 的 filter table 就會先比對到「拒絕所有 ssh 連線請求的規則」,所以會連來自 192.168.50.141 的連線請求都一併拒絕喔~
以下是測試的結果~左上角的視窗是對 Ubuntu 虛擬機做 `iptables` 的設定,右下角的視窗則是開發板可以 ssh 連過去:

我的 MacBook 被拒絕,無法 ssh 連過去~

若將這兩條規則的順序設反了,那就會連開發板都沒辦法連過去喔:


## 其他 `iptables` 相關設定
- [WAN Ping 基本介紹與設定](https://hackmd.io/@cpt/wan_ping)
- [DMZ Host 基本介紹與設定](https://hackmd.io/@cpt/dmz_host)
## References
- https://linux.die.net/man/8/iptables
- https://linux.vbird.org/linux_server/rocky9/0180firewall.php
- https://stuffphilwrites.com/2014/09/iptables-processing-flowchart/
- https://homes.di.unimi.it/sisop/qemu/iptables-tutorial.pdf
- https://lewestech.com/mirrors/www.iptables.info/en/structure-of-iptables.html
- https://gist.github.com/nerdalert/a1687ae4da1cc44a437d
- https://man7.org/linux/man-pages/man8/iptables.8.html
- http://www.faqs.org/docs/iptables/mangletable.html
- https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/security_guide/sect-security_guide-iptables#sect-Security_Guide-IPTables
- https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
- https://en.wikipedia.org/wiki/Secure_Shell