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).
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ệnhChức năng
erpc worker firewall listHiể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óaChức năngThứ bị từ chối nhận được
methodsCác method được chuyển tiếp, viết hoa (tối đa 16; không bao giờ CONNECT)405, kèm Allow
allow_pathsCá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ường404
deny_pathsCác path bị từ chối, viết theo cùng cách và được kiểm tra trước403
max_body_bytesBody request lớn nhất, tối đa 64 MiB413
max_header_bytesKích thước lớn nhất của dòng request và các header cộng lại, từ 1024 đến 65536431
rateSố 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ụng429, kèm Retry-After
max_connections_per_addressSố kết nối mà một địa chỉ giữ cùng lúcKết nối bị đóng, không được trả lời
banSau 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_probesTừ 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_secsThờ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ời408 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_secsTổ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ụng408
upstream_timeout_secsThời gian ứng dụng có để nhận kết nối và bắt đầu trả lời, từ 1 đến 600504; 502 khi ứng dụng không lắng nghe
new_connectionsGiớ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ụngGó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ệnhHiển thị
erpc worker statusCổ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 listAi 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 --wafTừ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 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.