---
# System prepended metadata

title: Network / Firewall
tags: [linux, OS, firewall, command, network, OS / Ubuntu / Network, Ubuntu, firewalld]

---

Network / Firewall
===
###### tags: `OS / Ubuntu / Network`
###### tags: `OS`, `Ubuntu`, `linux`, `command`, `network`, `firewall`, `firewalld`

<br>


| cmd | package |
|-----|---------|
| `firewalld` | `apt install firewalld` |
| `nft`       | `apt install nftables` |
| `ufw`       | `apt install ufw` |

<br>



## Ubuntu 防火牆架構圖

### nftables（底層門神）× firewalld（上層總管）
> 視覺化層級與資訊流：上是控制面（設定規則），下是資料面（封包實際流動）。

![](https://hackmd.io/_uploads/ByD6dn4Ygl.png)

:::spoiler 圖片繪製原始碼
```
export default function FirewallStackDiagram() {
  // Simple helper to draw an SVG box with centered label
  const Box = ({ x, y, w, h, title, subtitle }: { x: number; y: number; w: number; h: number; title: string; subtitle?: string; }) => (
    <g>
      <rect x={x} y={y} width={w} height={h} rx={14} ry={14} fill="white" stroke="black" />
      <text x={x + w / 2} y={y + 28} textAnchor="middle" fontSize={16} fontWeight={700}>{title}</text>
      {subtitle && (
        <text x={x + w / 2} y={y + 52} textAnchor="middle" fontSize={13}>{subtitle}</text>
      )}
    </g>
  );

  // Arrow helper (straight)
  const Arrow = ({ x1, y1, x2, y2, label, dashed }: { x1: number; y1: number; x2: number; y2: number; label?: string; dashed?: boolean; }) => (
    <g>
      <line x1={x1} y1={y1} x2={x2} y2={y2} stroke="black" markerEnd="url(#arrow)" strokeDasharray={dashed ? "6,6" : undefined} />
      {label && (
        <text x={(x1 + x2) / 2} y={(y1 + y2) / 2 - 8} textAnchor="middle" fontSize={12}>{label}</text>
      )}
    </g>
  );

  // Polyline arrow for bent routes
  const BendArrow = ({ points, label, dashed }: { points: [number, number][]; label?: string; dashed?: boolean; }) => {
    const d = points.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(" ");
    // Arrow head at the last segment
    const [x1, y1] = points[points.length - 2];
    const [x2, y2] = points[points.length - 1];
    return (
      <g>
        <path d={d} fill="none" stroke="black" strokeDasharray={dashed ? "6,6" : undefined} markerEnd="url(#arrow)" />
        {label && (
          <text x={(x1 + x2) / 2} y={(y1 + y2) / 2 - 8} textAnchor="middle" fontSize={12}>{label}</text>
        )}
      </g>
    );
  };

  return (
    <div className="w-full h-full flex items-center justify-center p-6 bg-neutral-50">
      <div className="w-full max-w-[1100px] bg-white rounded-2xl shadow p-4">
        <h1 className="text-2xl font-bold mb-2">nftables（底層門神）× firewalld（上層總管）</h1>
        <p className="text-sm text-neutral-700 mb-4">視覺化層級與資訊流：上是控制面（設定規則），下是資料面（封包實際流動）。</p>

        <svg viewBox="0 0 1100 760" className="w-full">
          {/* Arrow marker definition */}
          <defs>
            <marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
              <path d="M0,0 L0,6 L9,3 z" fill="black" />
            </marker>
          </defs>

          {/* Background swimlanes */}
          <g>
            <rect x={20} y={40} width={1060} height={290} rx={12} ry={12} fill="#f6f6f6" stroke="#e5e5e5" />
            <text x={40} y={65} fontSize={14} fontWeight={700}>控制面（設定規則）</text>
            <rect x={20} y={500} width={1060} height={220} rx={12} ry={12} fill="#f6f6f6" stroke="#e5e5e5" />
            <text x={40} y={525} fontSize={14} fontWeight={700}>資料面（封包實際流動）</text>
          </g>

          {/* Top row: CLI tools */}
          <Box x={110} y={90} w={190} h={70} title="firewall-cmd" subtitle="firewalld 的 CLI" />
          <Box x={350} y={90} w={150} h={70} title="nft" subtitle="nftables 低階指令" />
          <Box x={550} y={90} w={150} h={70} title="ufw" subtitle="UFW 的 CLI" />
          <Box x={750} y={90} w={190} h={70} title="iptables" subtitle="相容層/legacy" />

          {/* Middle row: managers */}
          <Box x={150} y={210} w={260} h={90} title="firewalld" subtitle="上層總管（zones/rich rules）" />
          <Box x={560} y={210} w={240} h={90} title="UFW" subtitle="上層總管（Ubuntu 常見）" />

          {/* Engine */}
          <Box x={390} y={360} w={320} h={100} title="nftables" subtitle="底層門神（Kernel 規則）」" />

          {/* Control-plane arrows */}
          <Arrow x1={205} y1={160} x2={280} y2={210} label="管理" />
          <Arrow x1={625} y1={160} x2={680} y2={210} label="管理" />
          <Arrow x1={480} y1={160} x2={540} y2={360} label="直接下規則" />
          <Arrow x1={835} y1={160} x2={640} y2={360} label="相容層" dashed />
          <Arrow x1={280} y1={300} x2={470} y2={360} label="產生/維護規則" />
          <Arrow x1={680} y1={300} x2={630} y2={360} label="產生/維護規則" />

          {/* Data-plane objects */}
          <Box x={70} y={590} w={200} h={70} title="網卡/封包" subtitle="ens10f0 / …" />
          <Box x={820} y={590} w={220} h={70} title="應用程式" subtitle="python http.server 等" />

          {/* Data-plane arrows (bent to pass through nftables) */}
          <BendArrow points={[[170, 590], [170, 520], [550, 460]]} label="封包進入內核" />
          <BendArrow points={[[710, 460], [930, 520], [930, 590]]} label="判決後交付/丟棄" />

          {/* Legend */}
          <g>
            <rect x={20} y={450} width={320} height={34} fill="white" stroke="#e5e5e5" rx={8} ry={8} />
            <text x={35} y={472} fontSize={12}>—  實線箭頭：一般控制面流程（設定→規則）</text>
            <text x={35} y={488} fontSize={12}>╌╌ 虛線箭頭：iptables 相容層路徑</text>
          </g>
        </svg>
      </div>
    </div>
  );
}
```
:::

- ### 重點
    * **nftables** = 底層門神（真正擋/放封包）。`nft` 是它的低階操作工具。
    * **firewalld / UFW** = 上層總管（把你的策略翻成底層規則）。`firewall-cmd`/`ufw` 是它們的前端指令。
    * 用 `nft` 直接改規則會**生效**，但被上層總管（例如 firewalld）**reload 後可能覆蓋**。
    * **不要同時啟用 firewalld 與 UFW**，避免互踩規則。

 - ### 資訊流關係（控制面 vs. 資料面）
    ```
    【控制面（設定規則）】
    你 → firewall-cmd → firewalld ─┐
    你 → ufw         → UFW       ──┼─► nftables（或 iptables 相容層）→ 寫入/更新內核規則
    你 → nft（直上） ──────────────┘

    【資料面（封包實際流動）】
    網卡(ens10f0/…) → Netfilter hooks → nftables 規則判決 → ACCEPT/DROP/REJECT → 應用程式/棄置
             （這條路與你用哪個上層總管無關；只看內核現有規則）
    ```

- ### 小抄（判斷誰在當家）
    * 看 firewalld 是否在跑：`sudo firewall-cmd --state`（running = 由 firewalld 管）
    * 看 nftables 現況：`sudo nft list ruleset`（**最終真相**）
    * firewalld 後端：`grep -i ^FirewallBackend /etc/firewalld/firewalld.conf`（多半是 `nftables`）
    * iptables 是否走 nft 相容層：`iptables -V`（出現 `nf_tables` 代表相容層）

<br>

---

## firewall-cmd
- ### 安裝方式
    ```
    sudo apt install firewalld
    ```
- ### 服務狀態確認
    ```bash
    # 確認 firewalld 是否在跑
    sudo firewall-cmd --state
    ```
    ```
    $ sudo systemctl status firewalld
    ● firewalld.service - firewalld - dynamic firewall daemon
         Loaded: loaded (/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
         Active: active (running) since Thu 2025-08-14 15:58:49 CST; 1 week 3 days ago
           Docs: man:firewalld(1)
       Main PID: 2459 (firewalld)
          Tasks: 2 (limit: 309132)
         Memory: 36.9M
            CPU: 3.397s
         CGroup: /system.slice/firewalld.service
                 └─2459 /usr/bin/python3 /usr/sbin/firewalld --nofork --nopid
    ```
- ### 公網區域（public）臨時開 38888/tcp
    ```
    # 公網區域（public）臨時開 38888/tcp
    sudo firewall-cmd --zone=public --add-port=38888/tcp
    ```
- ### 常用操作
    ```bash=
    # 看有哪些 Zone
    sudo firewall-cmd --get-zones

    # 看預設 Zone
    sudo firewall-cmd --get-default-zone

    # 設定預設 Zone（影響未綁定的介面/來源）
    sudo firewall-cmd --set-default-zone=public

    # 看哪些 Zone 正在用 & 對應介面/來源
    sudo firewall-cmd --get-active-zones

    # 把介面綁到某 Zone（runtime）
    sudo firewall-cmd --zone=public --change-interface=ens10f0

    # 持久化（建議加 --permanent，再 reload）
    sudo firewall-cmd --permanent --zone=public --change-interface=ens10f0
    sudo firewall-cmd --reload

    # 在某 Zone 開服務/連接埠
    sudo firewall-cmd --zone=public --add-service=ssh
    sudo firewall-cmd --zone=public --add-port=48888/tcp
    # 永久化
    sudo firewall-cmd --permanent --zone=public --add-port=48888/tcp
    sudo firewall-cmd --reload

    # 查看某 Zone 目前完整設定
    sudo firewall-cmd --zone=public --list-all
    ```

<br>

---

## 情境1：server 有設定 iptables ，開放某個子網域連入，卻無法連入該 server

### iptables
```
sudo iptables -A INPUT -s 10.78.153.0/24 -p tcp -j ACCEPT
```

### 測試方法
```
$ sudo python3 -m http.server 48888 --bind 0.0.0.0
Serving HTTP on 0.0.0.0 port 48888 (http://0.0.0.0:48888/) ...

---

# 先看目前值（0=關閉, 1=嚴格, 2=寬鬆）
$ sysctl net.ipv4.conf.all.rp_filter
net.ipv4.conf.all.rp_filter = 0

$ sysctl net.ipv4.conf.default.rp_filter
net.ipv4.conf.default.rp_filter = 2

$ sysctl net.ipv4.conf.ens10f0.rp_filter
net.ipv4.conf.ens10f0.rp_filter = 2

$ sysctl net.ipv4.conf.ens10f0/206.rp_filter 
net.ipv4.conf.ens10f0/206.rp_filter = 0

---

$ sudo ufw status
Status: inactive

---

$ sudo iptables -L
(略)

---

$ sudo nft list ruleset
(略)

---

# 瀏覽器：http://10.78.26.241:48888/
$ sudo tcpdump -ni any tcp port 48888
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes

# 封包確實有進來
15:50:55.689891 ens10f0 In  IP 10.78.153.144.57094 > 10.78.26.241.48888: Flags [S], seq 1070280506, win 64240, options [mss 1460,sackOK,TS val 4117789310 ecr 0,nop,wscale 7], length 0
15:50:55.689893 ens10f0.206 In  IP 10.78.153.144.57094 > 10.78.26.241.48888: Flags [S], seq 1070280506, win 64240, options [mss 1460,sackOK,TS val 4117789310 ecr 0,nop,wscale 7], length 0
15:50:55.691515 ens10f0 In  IP 10.78.153.144.57098 > 10.78.26.241.48888: Flags [S], seq 2965925806, win 64240, options [mss 1460,sackOK,TS val 4117789312 ecr 0,nop,wscale 7], length 0
15:50:55.691517 ens10f0.206 In  IP 10.78.153.144.57098 > 10.78.26.241.48888: Flags [S], seq 2965925806, win 64240, options [mss 1460,sackOK,TS val 4117789312 ecr 0,nop,wscale 7], length 0

---

# 瀏覽器：http://10.78.26.241:48888/
This site can’t be reached
http://10.78.26.241:48888/ is unreachable.
ERR_ADDRESS_UNREACHABLE

```

<br>

## 無法連進任意 port ----> 分析 nftables

### `$ sudo iptables -L`
```
$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     tcp  --  10.78.153.0/24       anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DOCKER-USER  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             172.17.0.2           tcp dpt:8888

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere

```

---

### `$ sudo nft list ruleset`
```
$ sudo nft list ruleset
table inet firewalld {
	...
	chain filter_INPUT {
		...
		jump filter_INPUT_ZONES
		...
	}
	...
	chain filter_INPUT_ZONES {
		...
		goto filter_IN_public
	}
	...
	chain filter_IN_public {
		jump filter_INPUT_POLICIES_pre
		jump filter_IN_public_pre
		jump filter_IN_public_log
		jump filter_IN_public_deny
		jump filter_IN_public_allow
		jump filter_IN_public_post
		jump filter_INPUT_POLICIES_post
		meta l4proto { icmp, ipv6-icmp } accept
		reject with icmpx admin-prohibited (擋掉了)
	}
	...
	chain filter_IN_public_deny {
	}

	chain filter_IN_public_allow {
		tcp dport 22 ct state { new, untracked } accept
		ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
	}
```

---

### 分析原因 
- ### gpt-5 (2025/08/21)
    看起來問題已經很明確了：**firewalld（nftables）正在擋你 48888/TCP**。
    你用 `tcpdump` 已經看到來自 `10.78.153.144` 的 SYN 封包進來，但服務端沒有回 SYN-ACK；在你貼的 `nft` 規則裡，public zone 只 **允許 22/TCP**，其他進來的連線最後都被 `reject with icmpx admin-prohibited` 擋掉了（你用 `tcp port 48888` 當過濾，因此沒看到 ICMP 拒絕封包）。

    重點觀察：

    * `table inet firewalld` → `filter_INPUT` 會跳到 `filter_IN_public`，而 `filter_IN_public_allow` 只有 `tcp dport 22 accept`。
    * `iptables -L` 雖然看起來放行，但現在 **真正生效的是 nftables/firewalld**；所以 iptables 的那條允許規則救不了你。

    下面用最小變動把 48888 打開（建議）：

    ### 1) 立即打開 48888/TCP（runtime）

    ```bash
    # 確認 firewalld 是否在跑
    sudo firewall-cmd --state

    # 公網區域（public）臨時開 48888/tcp
    sudo firewall-cmd --zone=public --add-port=48888/tcp
    ```

    ### 2) 讓設定永久化

    ```bash
    sudo firewall-cmd --zone=public --add-port=48888/tcp --permanent
    sudo firewall-cmd --reload
    ```

    ### 3)（可選）直接用 nft 新增一條允許規則（等效、偏低階）

    如果你的環境不使用或沒安裝 `firewall-cmd`，可以直接加 nft 規則：

    ```bash
    sudo nft add rule inet firewalld filter_IN_public_allow tcp dport 48888 ct state { new, untracked } accept
    ```

    ### 4) 再次驗證

    ```bash
    # 確認服務有在聽 0.0.0.0:48888
    ss -ltnp | grep :48888

    # 抓封包（這次也看 ICMP 拒絕）
    sudo tcpdump -ni any 'host 10.78.153.144 and (tcp port 48888 or icmp)'

    # 從客戶端重試 http://10.78.26.241:48888/
    ```

    ---

    ### 可能的加分檢查（若仍不通）

    * 確認本機 IP 綁在你以為的介面上：

      ```bash
      ip -br -4 addr | grep 10\.78\.26\.241
      ```

    ---

    ### 為什麼會這樣？

    * 你同時看到 **iptables** 和 **nftables** 規則，但 **firewalld（nftables）在這台機器上是主導**，public zone 預設只開 22/TCP。
    * 因此你看到 SYN 進來（路由、ARP 都 OK），但被 nftables 在 `filter_IN_public` 收尾處拒絕，瀏覽器才會報 `ERR_ADDRESS_UNREACHABLE`。開啟 48888 後就能正常回應。


<br>

{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}
