---
title: "Siết chặt truy cập server và đặt WAF trước ứng dụng của bạn"
description: "erpc worker siết chặt truy cập server từ lần triển khai đầu tiên, và một bảng trong erpc.toml đặt WAF trước API công khai. Những gì nó làm và không làm."
canonical: "https://erpc.global/vi/doc/erpc-cli/worker-firewall-and-waf/"
locale: "vi"
---

# Siết chặt truy cập server và đặt WAF trước ứng dụng của bạn

Ngay từ lần triển khai đầu tiên, `erpc worker` chỉ cho phép SSH từ địa chỉ của bạn và loại bỏ mọi kết nối đến khác. Một bảng `[waf]` đặt một proxy lọc phía trước API của bạn.

Trang này mô tả ERPC CLI `v0.1.0-rc.4`, một bản pre-release.

## Tường lửa

Một lần triển khai cấu hình tường lửa của server trước khi ứng dụng khởi động lần đầu:

- SSH chỉ được phép từ `firewall.ssh_allow` và từ địa chỉ thực hiện lần triển khai;
- mọi kết nối đến khác đều bị loại bỏ, trừ `run.port` từ `firewall.app_allow`; với ứng dụng đứng sau WAF thì cổng của WAF thay cho cổng đó, và chỉ khi WAF trên server đang chạy sẵn các quy tắc mà `[waf]` hiện tạo ra ([bên dưới](https://erpc.global/vi/doc/erpc-cli/worker-firewall-and-waf/#đặt-waf-trước-ứng-dụng-của-bạn)).

Mỗi thay đổi chạy dưới một bộ hẹn giờ rollback 120 giây và chỉ được giữ lại khi một kết nối mới vào được, nên một thay đổi sai không thể khiến bạn bị khóa ở ngoài.

Tuy vậy, allowlist tồn tại lâu hơn địa chỉ đã được dùng để tạo ra nó: nếu địa chỉ nhà của bạn thay đổi, cách để vào lại là bộ phận hỗ trợ ERPC đối với server của ERPC, hoặc console của nhà cung cấp đối với server của riêng bạn. Hãy thêm một địa chỉ thứ hai mà bạn có thể truy cập server từ đó (văn phòng, điểm ra của VPN) vào `firewall.ssh_allow` trước khi tin cậy một địa chỉ nhà.

Nếu địa chỉ của bạn thay đổi, hãy thêm mọi địa chỉ cố định mà các quản trị viên của bạn sử dụng:

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

| Lệnh | Chức năng |
| --- | --- |
| `erpc worker firewall list` | Hiển thị ai có thể truy cập server: `erpc.toml` bên cạnh bảng của server |
| `erpc worker firewall allow --to ssh\|app <addresses>` | Cho phép các địa chỉ vào và áp dụng thay đổi. SSH được phép từ từng địa chỉ đơn lẻ; một mạng chỉ được chấp nhận với `--to app` |
| `erpc worker firewall deny --to ssh\|app <addresses>` | Ngừng cho phép các địa chỉ đó vào |
| `erpc worker firewall sync` | Đưa tường lửa của server trở lại đúng như `erpc.toml` quy định |

## Đặt WAF trước ứng dụng của bạn

Thêm một bảng vào `erpc.toml`:

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

Sau đó `erpc worker deploy` chạy một HTTP reverse proxy trên server, trong một unit systemd riêng (`erpc-waf-<app>-<env>`): một dynamic user không có đặc quyền, không có khóa, không có secret và không có gì của worker, bị giới hạn ở 1 GiB bộ nhớ. Proxy lắng nghe trên `waf.port`, đọc từng request trước ứng dụng, từ chối những gì các quy tắc của nó chỉ định, và chuyển tiếp phần còn lại tới ứng dụng tại `127.0.0.1:<run.port>`.

- **Ứng dụng vẫn ở trên loopback.** Cổng riêng của ứng dụng không bao giờ được mở ra bên ngoài. `erpc worker api` và health check truy cập ứng dụng trên loopback, không đi qua WAF.
- **Ai được kết nối vẫn do danh sách của tường lửa quyết định**, và cổng của WAF là nơi duy nhất mà danh sách đó có thể cho phép tất cả mọi người:

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

  Khi danh sách không có ai, WAF vẫn lắng nghe nhưng không ai được vào, và lần triển khai sẽ báo điều đó.
- **Cổng chỉ được mở khi WAF đã lắng nghe trên đó**, không bao giờ trước đó. Một lần triển khai mà WAF không khởi động được sẽ thất bại và cho biết lý do, trong khi ứng dụng vẫn chạy.
- **Khi bạn thay đổi `[waf]`,** cổng sẽ không được mở cho bất kỳ ai mới cho đến khi một lần triển khai đã đưa các quy tắc mới vào hoạt động.
- **Gỡ `[waf]`** khỏi `erpc.toml` rồi triển khai sẽ xóa WAF khỏi server.

## Thiết lập WAF

Chỉ `port` là bắt buộc. Mọi khóa cấu hình, kèm giá trị mặc định:

```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 }
```

| Khóa | Chức năng | Thứ bị từ chối nhận được |
| --- | --- | --- |
| `methods` | Các method được chuyển tiếp, viết hoa (tối đa 16; không bao giờ `CONNECT`) | 405, kèm `Allow` |
| `allow_paths` | Các path được chuyển tiếp; không liệt kê path nào nghĩa là mọi path. Một mẫu là một path, hoặc mọi thứ dưới một tiền tố khi mẫu kết thúc bằng `*`: `/api/*` là `/api` và mọi thứ bên dưới. Được so sánh với path sau khi giải mã percent-encoding một lần, có phân biệt chữ hoa chữ thường | 404 |
| `deny_paths` | Các path bị từ chối, viết theo cùng cách và được kiểm tra trước | 403 |
| `max_body_bytes` | Body request lớn nhất, tối đa 64 MiB | 413 |
| `max_header_bytes` | Kích thước lớn nhất của dòng request và các header cộng lại, từ 1024 đến 65536 | 431 |
| `rate` | Số request mỗi giây từ một địa chỉ, và số request cùng lúc được chấp nhận trước khi giới hạn tốc độ áp dụng | 429, kèm `Retry-After` |
| `max_connections_per_address` | Số kết nối mà một địa chỉ giữ cùng lúc | Kết nối bị đóng, không được trả lời |
| `ban` | Sau `after` request bị chặn trong `window_secs`, địa chỉ bị từ chối trong `for_secs` (tối đa 30 ngày) | Các kết nối của địa chỉ đó bị đóng, không được trả lời |
| `block_probes` | Từ chối những gì các trình quét yêu cầu (`/.env`, `/.git`, `/wp-login.php` và những thứ tương tự) | 403 |
| `header_timeout_secs` | Thời gian một client có để gửi phần đầu của request, từ 1 đến 120. Cũng là thời gian chờ một client đã dừng giữa chừng khi gửi body, hoặc đã ngừng nhận câu trả lời | 408 cho phần đầu; 400 cho body bị dừng giữa chừng; kết nối bị đóng đối với câu trả lời không được nhận |
| `body_timeout_secs` | Tổng thời gian tối đa cho body của một request, từ 1 đến 600, không tính thời gian WAF chờ ứng dụng | 408 |
| `upstream_timeout_secs` | Thời gian ứng dụng có để nhận kết nối và bắt đầu trả lời, từ 1 đến 600 | 504; 502 khi ứng dụng không lắng nghe |
| `new_connections` | Giới hạn riêng của tường lửa về số kết nối TCP mới mỗi giây từ một địa chỉ tới cổng của WAF, bị nftables loại bỏ trước khi proxy thấy chúng. Trong khi tập địa chỉ của nftables đã đầy, một địa chỉ không có trong tập sẽ không được đếm, nên giới hạn này không áp dụng cho địa chỉ đó cho đến khi tập có chỗ trở lại; các giới hạn riêng của proxy (`rate`, `max_connections_per_address`, `ban`) vẫn áp dụng | Gói tin bị loại bỏ |

`rate`, `max_connections_per_address`, `ban` và `new_connections` được tắt bằng `false` (`ban = false`), và một khóa không được ghi sẽ lấy giá trị mặc định. `erpc worker check` cho biết bảng `[waf]` sẽ làm gì, và bảng đó sai ở đâu, trước bất kỳ lần triển khai nào.

## Những gì WAF làm mà không cần thiết lập

- Một request không đọc được như một request HTTP/1 duy nhất và không mơ hồ sẽ nhận 400, và một path không ở dạng chuẩn cũng vậy.
- Một request đổi giao thức (`Upgrade`) nhận 501.
- Mọi lần từ chối đều được tính cho việc cấm địa chỉ, và mỗi lần từ chối sẽ kết thúc kết nối của nó.
- Một địa chỉ là một địa chỉ IPv4, hoặc một `/64` IPv6.
- WAF không chuyển tiếp các header thuộc về kết nối giữa client và proxy (`Connection` và các header mà nó nêu tên, `Keep-Alive`, `Proxy-Authenticate`, `Proxy-Authorization`, `Proxy-Connection`, `TE`, `Trailer`), `Expect`, các header có tên chứa dấu gạch dưới, hay các header `Forwarded`, `X-Real-IP` và `X-Forwarded-Proto` do chính client gửi. WAF đặt `X-Real-IP` bằng địa chỉ mà kết nối đến từ đó và thêm địa chỉ đó vào cuối `X-Forwarded-For`; chỉ có mục cuối cùng của `X-Forwarded-For` là do proxy ghi.

## Xem WAF làm gì

| Lệnh | Hiển thị |
| --- | --- |
| `erpc worker status` | Cổng của WAF, nơi WAF chuyển tiếp tới, từ khi nào, và các số đếm kể từ đó |
| `erpc worker firewall list` | Ai có thể truy cập cổng của WAF, và những gì đã bị chặn, theo từng quy tắc |
| `erpc worker logs --waf` | Từng request bị chặn: quy tắc, trạng thái, địa chỉ của client, method và 200 byte đầu tiên của target |

Một request được chuyển tiếp không để lại dòng log nào, và header hay body không bao giờ được ghi log; query của một request bị chặn thì có, trong phạm vi 200 byte đó. Những gì client gửi không thể ghi lên terminal của bạn: WAF ghi journal của nó dưới dạng ASCII in được.

## Những gì WAF không làm

- **Không có TLS.** WAF dùng HTTP/1.1 thuần trên cổng của mình.
- **Không có HTTP/2:** WAF trả lời 400 cho nó.
- **Không có WebSocket**, cũng không có bất kỳ `Upgrade` nào khác: 501.
- **Không xem bên trong body.** Body được đo kích thước, không bao giờ được đọc. Danh sách probe ngắn và chỉ là bộ lọc nhiễu, không phải một sự bảo đảm: ứng dụng vẫn tự kiểm tra đầu vào của mình.
- **Không xác thực.** Ai được gọi ứng dụng là do ứng dụng quyết định, cùng với danh sách của tường lửa.
- **Một địa chỉ là một client.** Các client đứng sau cùng một NAT, proxy hoặc CDN dùng chung một giới hạn tốc độ và một lệnh cấm; một client có nhiều địa chỉ thì có bấy nhiêu giới hạn tốc độ.
- **Lệnh cấm và các số đếm bị quên** khi WAF khởi động lại.
- **WAF ghi nhớ tối đa 8192 địa chỉ** và giữ tối đa 1024 kết nối cùng lúc.
- **WAF chỉ lắng nghe trên IPv4,** nên một mục IPv6 trong `app_allow` hiện chưa truy cập được gì.
- **Path được so sánh theo các byte mà nó giải mã ra.** Một ứng dụng đọc path lỏng lẻo hơn (bỏ qua chữ hoa chữ thường, tham số `;`, giải mã hai lần) có thể bị truy cập dưới một tên mà `deny_paths` không bao phủ. `allow_paths` không có lỗ hổng như vậy: những gì không được liệt kê sẽ không được chuyển tiếp.
- **Client đọc chậm vẫn được phục vụ đến hết.** Một client vẫn tiếp tục nhận câu trả lời, dù chậm đến đâu, sẽ được phục vụ đến cuối.
- **Một đợt flood có thể cắt ngang một lần upload lớn.** WAF chạy trên một luồng, nên một đợt flood có thể cắt ngang một lần upload lớn bằng 408. Ứng dụng nhận body lớn thì tăng `body_timeout_secs` cùng với `max_body_bytes`.

[Hướng dẫn worker](https://github.com/elsoul/erpc-cli/blob/v0.1.0-rc.4/docs/worker.md) có mọi quy tắc, bao gồm cả khi nào một thay đổi đối với `app_allow` được giữ lại cho đến lần triển khai tiếp theo.
