1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
-----

你的問題非常精準,這顯示你對網路細節的思考深度!讓我們來釐清這些概念。

-----

### 埠號的「佔用」:更精確的理解

你對埠號佔用的理解在大部分情況下是正確的:**一個特定的 IP 地址和埠號組合,在同一時間點通常只能被一個服務監聽。**

例如,當你的 Python HTTP 伺服器監聽在 `0.0.0.0:1104` 時(`0.0.0.0` 表示監聽所有可用的 IP 接口),那麼:

* `192.168.50.17:1104` 就被這個服務佔用了。
* `127.0.0.1:1104` 也被這個服務佔用了。
* 任何發往樹莓派上 `1104` 埠的流量(無論哪個 IP 介面),都會被這個服務接收。

**所以,你說的「不會有你的樹莓派上未來還有其他服務也在使用 1104 埠」在同時監聽的情況下是對的。**

**那麼我為什麼會這麼說呢?**

我的意思是,這條規則的**精確性**體現在它能避免**未來或更複雜場景**下的潛在問題,而非指當前服務會同時佔用。

考慮以下幾種情況,其中 **`ip saddr 127.0.0.1`** 這個條件的價值會顯現出來:

1. **非監聽埠的發起連接:**
一個程式(例如,一個數據庫客戶端)**發起**一個到外部的連接,它會使用一個**臨時埠 (ephemeral port)**。這個臨時埠號可能是 1104(雖然機率很小,因為它通常在 32768-61000 之間)。
如果你的 `postrouting` 規則是 `tcp sport 1104 masquerade`,它就會不分青紅皂白地將所有來源埠是 1104 的 TCP 出站流量都偽裝。而如果你加上 `ip saddr 127.0.0.1`,那麼只有你的 `http.server` 服務發出的、且確實是從 `127.0.0.1` 這個源地址發出的回應(來源埠為 1104)才會被偽裝。
雖然這種情況比較邊緣,但在複雜的應用環境中,精確匹配是好的習慣。

2. **多個服務監聽在不同 IP 地址上的相同埠:**
雖然 `0.0.0.0:1104` 會佔用所有接口的 1104 埠,但有些服務可以綁定到特定的 IP 地址。
例如,服務 A 監聽 `192.168.50.17:1104`,而服務 B 監聽 `127.0.0.1:1104`。
如果你的 `postrouting` 規則沒有 `ip saddr 127.0.0.1`,那麼服務 A 發出的 `1104` 埠的流量也會被偽裝。而如果只偽裝來自 `127.0.0.1` 的流量,就能確保只有你 DNAT 進來的流量的回應被處理。

所以,我的意思是 `ip saddr 127.0.0.1` 是為了確保**你的規則只作用於你希望它作用的特定流量類型**,即使在目前簡單的環境下看不出明顯差異。這是**防範未來複雜性**和**提高規則精確性**的最佳實踐。

-----

### 樹莓派自身發出封包但不是透過 `127.0.0.1` 發出的情況

這非常常見!

當樹莓派上的應用程式或系統服務需要訪問**外部網路**時,它們發出的封包**通常不會使用 `127.0.0.1` 作為來源 IP**。

* **舉例:**
1. 你用 `ping google.com`。
2. 樹莓派上的瀏覽器訪問網站。
3. 你的樹莓派執行 `sudo apt update` 下載更新。
4. 你的 Python 腳本連接到一個遠端 API 伺服器。
5. 你的 MQTT 客戶端向外部 MQTT Broker 發送訊息。

在這些情況下,樹莓派會使用其**實際的網路介面 IP 地址**作為來源 IP。例如,如果你的樹莓派的 Wi-Fi 介面 (`wlan0`) IP 是 `192.168.50.17`,那麼這些出站封包的來源 IP 就會是 `192.168.50.17`。

`127.0.0.1`(回環地址)只用於**本機內部進程間的通訊**,封包從不會離開本機。

-----

### 「某些特定流量不被偽裝」的應用例子

這在更複雜的網路環境中會很有用,尤其是在內部網絡有多個子網或有特定的路由需求時。

**應用場景:** 假設你的樹莓派除了是網路伺服器,還是個路由器/防火牆,並連接到兩個內部網路:

* **網路 A (192.168.1.0/24):** 這些是標準使用者設備,需要透過樹莓派的外部 IP 訪問網際網路。
* **網路 B (10.0.0.0/24):** 這些是特殊的內部伺服器或 IoT 設備,它們連接到樹莓派,但它們發送到**特定目的地的流量(例如,到你的公司總部 VPN)不應該被偽裝**,而應該保持其原始的來源 IP (10.0.0.x),以便總部能識別是哪個設備在通訊,或者有特殊的路由需求。

**NFTables 規則範例:**

如果你只寫 `masquerade`,那麼所有經過 `postrouting` 的流量都會被偽裝。
但如果你想排除某些流量:

```nftables
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;

# 排除:来自 10.0.0.0/24 网段,且目的地是 172.16.0.0/16 的流量,不进行偽裝
ip saddr 10.0.0.0/24 ip daddr 172.16.0.0/16 accept

# 然后,对其他所有符合条件的流量进行伪装 (包括你网站服务器的回应)
ip saddr 127.0.0.1 tcp sport 1104 masquerade # 針對你的網站服務
# 其他一般的偽裝規則 (如果需要對其他內部網段進行偽裝)
# iifname "eth1" oifname "wlan0" masquerade # 範例:從 eth1 進來從 wlan0 出去就偽裝
}
}
```

在這個例子中,`accept` 規則被放置在 `masquerade` 之前。`nftables` 的規則是**從上到下依次匹配**的,一旦匹配到一個規則並執行了其動作(例如 `accept`),該封包通常就會停止在當前鏈中的處理,不會再匹配後續的規則。

所以,「某些特定流量不被偽裝」通常是通過**更精確的匹配條件**(`ip saddr`, `ip daddr`, `oifname`, 等等)並給予**不同的動作**(`accept`, `drop`, `masquerade` 等)來實現的。你的 `ip saddr 127.0.0.1` 就是一個很好的精確匹配範例。

希望這些解釋能讓你對網路流量、埠號和 `nftables` 規則的精確控制有更深層次的理解!