---
title: "限制對伺服器的存取，並在您的應用前加上 WAF"
description: "erpc worker 從第一次部署起就限制對伺服器的存取，而 erpc.toml 中的一個表就能在公開 API 前加上 WAF。說明它做什麼、不做什麼。"
canonical: "https://erpc.global/zh-tw/doc/erpc-cli/worker-firewall-and-waf/"
locale: "zh-tw"
---

# 限制對伺服器的存取，並在您的應用前加上 WAF

從第一次部署起，`erpc worker` 只允許來自您位址的 SSH 連入，並丟棄其他所有傳入連線。`[waf]` 表會在您的 API 前放置一個進行過濾的代理。

本頁說明 ERPC CLI `v0.1.0-rc.4`，這是發行前版本。

## 防火牆

部署會在應用首次啟動之前設定伺服器的防火牆：

- 只允許來自 `firewall.ssh_allow` 與部署來源位址的 SSH；
- 丟棄其他所有傳入連線，但來自 `firewall.app_allow` 的 `run.port` 除外；對於位於 WAF 後面的應用，則改為 WAF 的連接埠，而且只限於伺服器上的 WAF 已經在執行 `[waf]` 目前所產生規則的情況（[見下文](https://erpc.global/zh-tw/doc/erpc-cli/worker-firewall-and-waf/#在您的應用前加上-waf)）。

每次變更都在 120 秒的回滾計時器下執行，只有在新的連線成功連入後才會保留，因此錯誤的變更無法把您鎖在外面。

不過，白名單會比建立它時所用的位址存續得更久：如果您住家的位址變了，若是 ERPC 的伺服器，重新進入的途徑是 ERPC 支援團隊；若是您自己的伺服器，則是供應商的主控台。在信任住家位址之前，請先在 `firewall.ssh_allow` 中保留第二個可以連到伺服器的位址（辦公室、VPN 出口）。

如果您的位址變了，請加入管理員所使用的每一個固定位址：

```bash
erpc worker firewall allow --to ssh 203.0.113.10 198.51.100.7
```

| 命令 | 作用 |
| --- | --- |
| `erpc worker firewall list` | 顯示誰可以連到伺服器：`erpc.toml` 與伺服器上的表並列顯示 |
| `erpc worker firewall allow --to ssh\|app <addresses>` | 允許這些位址連入，並套用變更。SSH 只能允許單一位址；網段只有搭配 `--to app` 才會被接受 |
| `erpc worker firewall deny --to ssh\|app <addresses>` | 不再允許它們連入 |
| `erpc worker firewall sync` | 讓伺服器的防火牆再次與 `erpc.toml` 所寫的內容一致 |

## 在您的應用前加上 WAF

在 `erpc.toml` 中加入一個表：

```toml
[waf]
port = 8080
```

接著，`erpc worker deploy` 會在伺服器上以獨立的 systemd 單元（`erpc-waf-<app>-<env>`）執行 HTTP 反向代理：它以無特權的動態使用者身分執行，不持有任何金鑰、任何 Secret，也不持有 worker 的任何東西，記憶體上限為 1 GiB。它在 `waf.port` 上監聽，在應用之前讀取每個請求，拒絕其規則所指定的請求，並將其餘請求轉送到位於 `127.0.0.1:<run.port>` 的應用。

- **應用只在 loopback 上**。它自己的連接埠絕不會對外開放。`erpc worker api` 與健康檢查會繞過 WAF，在 loopback 上連到應用。
- **誰可以連線，仍由防火牆的清單決定**，而 WAF 的連接埠是該清單唯一可以寫成「所有人」的地方：

  ```sh
  erpc worker firewall allow --to app 0.0.0.0/0     # and ::/0
  ```

  若清單中沒有任何人，WAF 會監聽，但不會讓任何人連入，部署也會如此告知。
- **連接埠在 WAF 於其上監聽之後才會開放**，絕不會提前。WAF 未能啟動的部署會失敗並說明原因，而應用仍在執行。
- **當您變更 `[waf]` 時**，在部署使新規則就位之前，連接埠不會對任何新的對象開放。
- **從 `erpc.toml` 中移除 `[waf]`** 並部署，就會從伺服器上移除 WAF。

## WAF 設定

只有 `port` 是必填的。所有鍵及其預設值：

```toml
[waf]
port = 8080          # the public port, 1024 or above; the app keeps [run] port
methods = ["GET", "POST", "PUT", "DELETE"]
allow_paths = []     # every path
deny_paths = []
max_body_bytes = 1048576
max_header_bytes = 16384
rate = { per_second = 50, burst = 100 }
max_connections_per_address = 32
ban = { after = 100, window_secs = 60, for_secs = 600 }
block_probes = true
header_timeout_secs = 10
body_timeout_secs = 30
upstream_timeout_secs = 30
new_connections = { per_second = 50, burst = 100 }
```

| 鍵 | 作用 | 被拒絕的請求得到 |
| --- | --- | --- |
| `methods` | 轉送的方法，以大寫書寫（最多 16 個；絕不包括 `CONNECT`） | 405，附帶 `Allow` |
| `allow_paths` | 轉送的路徑；未列出任何路徑即代表所有路徑。一個模式是一條路徑，或在以 `*` 結尾時代表某個前綴下的所有路徑：`/api/*` 即 `/api` 及其下的所有路徑。與經過一次百分比解碼後的路徑比對，區分大小寫 | 404 |
| `deny_paths` | 被拒絕的路徑，寫法相同，且會先檢查 | 403 |
| `max_body_bytes` | 請求本文的最大大小，最多 64 MiB | 413 |
| `max_header_bytes` | 請求列與標頭合計的最大大小，1024 到 65536 | 431 |
| `rate` | 來自單一位址的每秒請求數，以及在此速率生效前可以同時送出多少個請求 | 429，附帶 `Retry-After` |
| `max_connections_per_address` | 單一位址可同時持有的連線數 | 連線被關閉，不予回應 |
| `ban` | 在 `window_secs` 內出現 `after` 個被阻擋的請求後，該位址會在 `for_secs` 內被拒絕（最長 30 天） | 其連線被關閉，不予回應 |
| `block_probes` | 拒絕掃描器所請求的內容（`/.env`、`/.git`、`/wp-login.php` 之類） | 403 |
| `header_timeout_secs` | 用戶端傳送請求開頭部分的時限，1 到 120。也是等待在本文中途停止傳送、或停止接收回應的用戶端的時間 | 請求開頭部分為 408；中途停止的本文為 400；未被接收的回應則關閉連線 |
| `body_timeout_secs` | 請求本文總共可花費的時間，1 到 600，不包括 WAF 等待應用的時間 | 408 |
| `upstream_timeout_secs` | 應用接受連線並開始回應的時限，1 到 600 | 504；應用未在監聽時為 502 |
| `new_connections` | 防火牆本身對於來自單一位址、連到 WAF 連接埠的每秒新 TCP 連線數的限制，在代理看到之前就由 nftables 丟棄。當 nftables 的位址集合已滿時，不在集合中的位址不會被計數，因此在集合再次有空間之前，此限制不適用於該位址；代理本身的限制（`rate`、`max_connections_per_address`、`ban`）仍然適用 | 封包被丟棄 |

`rate`、`max_connections_per_address`、`ban` 與 `new_connections` 可以用 `false` 關閉（`ban = false`），省略的鍵則採用其預設值。`erpc worker check` 會在任何部署之前，說明 `[waf]` 表將會做什麼，以及它有什麼問題。

## 不需任何設定，WAF 就會做的事

- 無法解讀為單一、明確的 HTTP/1 請求的請求會得到 400，不是正規形式的路徑也一樣。
- 要求變更協定（`Upgrade`）的請求會得到 501。
- 每次拒絕都會計入封鎖判定的次數，而且每次拒絕都會結束該連線。
- 一個位址是指一個 IPv4 位址，或一個 IPv6 `/64`。
- WAF 不會轉送用戶端與代理之間連線的標頭（`Connection` 及其所指名的標頭、`Keep-Alive`、`Proxy-Authenticate`、`Proxy-Authorization`、`Proxy-Connection`、`TE`、`Trailer`）、`Expect`、名稱含有底線的標頭，也不會轉送用戶端自己送來的 `Forwarded`、`X-Real-IP` 與 `X-Forwarded-Proto`。它會將 `X-Real-IP` 設為連線的來源位址，並將該位址加到 `X-Forwarded-For` 的末尾；`X-Forwarded-For` 中只有最後一個項目出自代理。

## 查看 WAF 的運作

| 命令 | 顯示內容 |
| --- | --- |
| `erpc worker status` | WAF 的連接埠、轉送目的地、起始時間，以及自那時以來的計數 |
| `erpc worker firewall list` | 誰可以連到 WAF 的連接埠，以及依規則列出被阻擋的內容 |
| `erpc worker logs --waf` | 每個被阻擋的請求：規則、狀態碼、用戶端的位址、方法，以及目標的前 200 個位元組 |

被轉送的請求不會留下任何日誌行，標頭或本文也絕不會被記錄；被阻擋請求的查詢字串則會被記錄，以 200 個位元組所及的範圍為限。用戶端送來的內容無法在您的終端機上寫入：WAF 以可列印的 ASCII 寫入其日誌。

## WAF 不做的事

- **不支援 TLS**。它在其連接埠上使用明文 HTTP/1.1。
- **不支援 HTTP/2**：對它回應 400。
- **不支援 WebSocket**，也不支援其他任何 `Upgrade`：回應 501。
- **不檢查本文的內容**。本文只會被測量大小，絕不會被讀取。探測清單很短，只是雜訊過濾器，而不是保證：應用仍要自己檢查其輸入。
- **不做身分驗證**。誰可以呼叫應用，由應用自己以及防火牆的清單決定。
- **一個位址就是一個用戶端**。位於同一個 NAT、代理或 CDN 後面的用戶端共用同一個速率與封鎖；擁有多個位址的用戶端則有同樣多個速率。
- **封鎖與計數會在 WAF 重新啟動時被清空**。
- **它最多記住 8192 個位址**，同時最多持有 1024 個連線。
- **它只在 IPv4 上監聽**，因此 `app_allow` 中的 IPv6 項目目前還連不到任何東西。
- **路徑以其解碼後的位元組進行比對**。以較寬鬆方式讀取路徑的應用（忽略大小寫、`;` 參數、解碼兩次），可以透過 `deny_paths` 未涵蓋的名稱被連到。`allow_paths` 沒有這種漏洞：未列出的就不會被轉送。
- **讀取緩慢的用戶端會一直得到服務，直到最後**。持續接收回應的用戶端，無論多慢，都會一直得到服務，直到最後。
- **大量湧入的流量可能中斷大型上傳**。WAF 以單一執行緒執行，因此大量湧入的流量可能以 408 中斷大型上傳。接受大型本文的應用要將 `body_timeout_secs` 與 `max_body_bytes` 一併調高。

[worker 指南](https://github.com/elsoul/erpc-cli/blob/v0.1.0-rc.4/docs/worker.md)列有每一條規則，包括 `app_allow` 的變更何時會被暫緩到下一次部署。
