Bảo mật Dokploy: thêm WAF và IPS ngay trên máy chủ

Ảnh: Scott Rodgerson / Unsplash
Dokploy là một nền tảng PaaS tự-host (self-hosted) rất tiện, một lựa chọn thay cho Heroku/Vercel mà bạn tự chủ trên VPS của mình, cùng nhóm với Coolify, CapRover, Easypanel, Dokku hay Portainer. Đẩy code lên, nó tự build Docker image, tự cấp chứng chỉ TLS Let's Encrypt, tự route qua Traefik trên nền Docker Swarm.
Trải nghiệm mượt tới mức dễ quên mất một câu hỏi quan trọng: giữa Internet và ứng dụng của bạn, có ai đứng gác không?
Với một bản cài mặc định, câu trả lời gần như là: không có ai. Bài này chỉ ra ba lỗ hổng mà một bản Dokploy mặc định để lại ở lớp ingress, và cách bịt từng lỗ bằng công cụ chạy ngay trên chính máy chủ của bạn, không phụ thuộc gói CDN trả phí.
Bài viết cho Dokploy, nhưng mọi nguyên tắc áp dụng nguyên vẹn cho bất kỳ PaaS tự-host nào dựng reverse proxy riêng (Traefik, Nginx Proxy Manager, hay Nginx thuần) trên một VPS phơi ra Internet.
Tóm tắt cho người bận
- Dokploy dùng Traefik làm reverse proxy. Traefik lo TLS và route rất tốt, nhưng nó không phải WAF/IPS, không soi payload tấn công, không chặn brute-force, không có IP reputation.
- Docker ghi thẳng
iptables, bỏ qua UFW: port container có thể lộ ra Internet dù tường lửa đang "deny all". - Domain để DNS-only trên cùng IP với domain proxy qua CDN sẽ làm lộ IP gốc, kẻ tấn công gõ thẳng IP kèm header
Host:là lách sạch lớp CDN. - Hướng xử lý: một lớp phòng thủ nhiều tầng chạy trên chính máy Dokploy — CrowdSec (engine + AppSec/WAF + bouncer) cộng rate-limit và origin-lock.
Ba lỗ hổng cần kiểm ngay
Nguyên tắc xuyên suốt: không kết luận bảo mật bằng cảm giác. Mỗi lỗ hổng dưới đây đều kiểm được bằng một lệnh thật, và bạn nên tự chạy trên hệ thống của mình trước khi tin bất cứ điều gì.
1. Docker có thể âm thầm chọc thủng tường lửa
Đây không phải lỗi của Dokploy, đó là cách Docker hoạt động, và chính tài liệu của Dokploy cũng cảnh báo:
Docker chỉnh trực tiếpiptables, nên nó bỏ qua luật UFW. Một container chạy với-p 3000:3000sẽ mở port 3000 ra Internet ngay cả khi UFW đang "deny all". Đây là cảm giác an toàn giả.
Dokploy có sẵn một lớp luật iptables ở bảng raw/PREROUTING để drop truy cập vào IP container từ card mạng công cộng. Lớp này thường đủ để che các port nội bộ, nhưng nó là thứ bạn phải chủ động xác minh, không phải thứ mặc định trông chờ. Nếu bạn tự chạy thêm container với -p mà không hiểu lớp bảo vệ này, bạn có thể đang mở cổng mà không hề biết.
Cách kiểm, quét từ một máy khác ngoài Internet, đừng quét từ chính máy chủ đó:
# Chạy từ một VPS khác. Liệt kê những port nội bộ bạn thực sự có:
# panel quản trị, cơ sở dữ liệu, cache, exporter metric...
for p in 3000 9000 5432 6379; do
curl -s -o /dev/null -w "port $p: %{http_code}\n" --connect-timeout 5 http://IP_MAY_CHU:$p/ \
|| echo "port $p: timeout (bị chặn - tốt)"
doneKết quả mong muốn: mọi port ngoài 80/443/SSH đều timeout.
2. Ingress không có WAF
Traefik không soi payload để bắt SQL injection (SQLi), Cross-Site Scripting (XSS), Remote Code Execution (RCE) hay path traversal; không có danh sách IP reputation để chặn bot và scanner; không làm chậm kẻ đang brute-force hay credential stuffing. Một bản Dokploy mặc định không có thành phần nào làm những việc đó, không WAF (Web Application Firewall), không IDS/IPS (hệ thống phát hiện/ngăn chặn xâm nhập). Mọi domain đi thẳng từ Internet vào Traefik rồi vào ứng dụng, không qua bất kỳ bộ lọc an ninh nào.
Hệ quả dễ thấy nhất nằm ở các endpoint có xác thực cơ bản (basic-auth): mặc định, nhập sai mật khẩu bao nhiêu lần cũng chỉ nhận lại 401, không hề bị làm chậm hay chặn. Nghĩa là kẻ tấn công có thể thử mật khẩu không giới hạn tốc độ. Hãy tự thử trên hệ thống của bạn: gọi liên tiếp hai mươi lần vào một endpoint có basic-auth và xem có bao giờ nhận được 429 không.
3. Origin-leak: khi domain DNS-only phản bội domain sau CDN
Đây là lỗ tinh vi nhất. Nếu bạn đặt vài domain sau CDN để lấy chút bảo vệ, nhưng lại để các domain khác ở chế độ DNS-only trỏ thẳng IP gốc, và tất cả nằm trên cùng một IP, thì các domain DNS-only đang làm lộ IP gốc của cả cụm.
Kẻ tấn công chỉ cần hai lệnh:
# 1. Lấy IP gốc từ một domain DNS-only (không núp sau CDN)
dig +short status.example.com # -> 160.x.x.x (IP gốc lộ ra)
# 2. Gõ THẲNG vào IP đó, giả Host của domain đang núp sau CDN
curl -H "Host: shop.example.com" --resolve shop.example.com:443:160.x.x.x \
https://shop.example.com/Vì Traefik nhận kết nối từ mọi nguồn, không chỉ từ dải IP của CDN, cú gõ thẳng này bỏ qua toàn bộ lớp CDN của những domain kia. Lớp phòng thủ ở rìa bị vô hiệu bởi một record DNS tưởng như vô hại.
Cách kiểm: liệt kê toàn bộ domain trỏ về máy chủ, đánh dấu domain nào proxy qua CDN và domain nào DNS-only. Nếu hai nhóm dùng chung một IP, bạn đã có lỗ hổng.
Vì sao nên chắn tại gốc, không đẩy hết cho CDN
Phản xạ thường gặp là "cho hết qua CDN proxy là xong". Có ba lý do để không dừng ở đó:
- Gói CDN miễn phí rất hạn chế. Chống DDoS tầng mạng thì có, nhưng luật WAF thật gần như không. Trông vào nó để chống tấn công tầng ứng dụng là ảo tưởng.
- Phụ thuộc nhà cung cấp. Bảo mật cột vào một dịch vụ bên thứ ba nghĩa là khi họ đổi chính sách, đổi giá, hoặc khi bạn cần chuyển hạ tầng đi nơi khác, lớp phòng thủ của bạn đi theo.
- Rìa có thể bị vòng qua, như lỗ hổng thứ ba vừa chứng minh. Lớp chạy ngay trên máy thấy đúng gói tin thật sự chạm tới ứng dụng, và luôn là lớp cuối cùng đứng vững.
CDN vẫn hữu ích. Nó chỉ không nên là lớp phòng thủ duy nhất. Đây cũng là tinh thần của phòng thủ nhiều lớp cho doanh nghiệp: không có lớp nào là đủ một mình.
Giải pháp: bốn lớp, tất cả trên máy Dokploy
Triết lý là defense-in-depth, không có viên đạn bạc, chỉ có nhiều lớp mà kẻ tấn công phải xuyên qua từng cái. Sắp xếp từ mạng lên ứng dụng:

Lớp 1 — CrowdSec: bộ não của phòng thủ
Có vài hướng để có WAF trên Traefik: bộ luật OWASP Core Rule Set (CRS) chạy qua ModSecurity hoặc engine Coraza (bản kế nhiệm thuần Go của ModSecurity), hoặc dùng CrowdSec, thứ gộp cả WAF lẫn IPS trong một, kèm mạng lưới IP reputation cộng đồng mà một bộ luật tĩnh không có.
CrowdSec là một engine phát hiện hành vi mã nguồn mở (open-source, thay thế hiện đại cho Fail2Ban). Nó gồm bốn mảnh, chia việc rõ ràng:
- Engine — trả lời "IP này có hành xử xấu không?", bằng cách đọc access log cộng IP reputation cộng đồng. Đây là nơi ra quyết định.
- AppSec (WAF) — trả lời "request này có phải đòn tấn công không?", bằng cách soi payload: SQLi, XSS, RCE, virtual-patching CVE. Chặn ở tầng HTTP, trước khi chạm ứng dụng.
- firewall-bouncer — thực thi lệnh chặn ở tầng mạng (
iptables/nftables). - plugin bouncer cho Traefik — thực thi ở tầng HTTP, trả 403.
Điểm tinh tế đáng nhớ:
Engine trả lời "AI xấu" (IP reputation, hành vi). AppSec trả lời "CÁI GÌ xấu" (payload tấn công). Hai câu hỏi khác nhau, hai lớp bù nhau. Thiếu một là hở một mảng.
Engine chạy như container và đọc access log của Traefik:
# acquis.yaml — dạy CrowdSec đọc log Traefik
filenames:
- /var/log/traefik/access.log
labels:
type: traefik
---
# bật AppSec như một nguồn thu thập riêng (lắng nghe :7422)
appsec_config: crowdsecurity/appsec-default
labels:
type: appsec
listen_addr: 0.0.0.0:7422
source: appsecCài các collection cần thiết, đặc biệt là virtual-patching:
cscli collections install crowdsecurity/traefik
cscli collections install crowdsecurity/appsec-virtual-patching
cscli collections install crowdsecurity/appsec-generic-rulesvirtual-patching rất đáng giá: nó vá ảo các CVE mới (lộ .git/config, Laravel debug mode, Symfony profiler…) ngay tại proxy, trước khi bản vá thật của ứng dụng kịp ra.
Bouncer plugin gắn vào Traefik như một middleware, bật cả hai chế độ, chặn IP xấu và đẩy request sang AppSec để soi payload:
# middleware động
http:
middlewares:
crowdsec:
plugin:
crowdsec:
enabled: true
crowdsecMode: live
crowdsecLapiHost: crowdsec:8080
crowdsecLapiKey: "${CROWDSEC_LAPI_KEY}"
# bật WAF: forward request sang AppSec
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec:7422Lớp 2, rate-limit và security headers: món rẻ, làm trước
Trước cả khi dựng CrowdSec, có những thứ vá được ngay chỉ bằng một file cấu hình động. Nhớ endpoint basic-auth "sai bao nhiêu lần cũng 401" ở trên chứ? Traefik có sẵn thuốc:
http:
middlewares:
ratelimit:
rateLimit:
average: 100
burst: 50
period: 1m
inflight:
inFlightReq:
amount: 50
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
contentTypeNosniff: true
customResponseHeaders:
Server: "" # đừng để lộ "tôi là Traefik"Áp global cho mọi router qua entryPoints.websecure.http.middlewares. Rẻ, hiệu lực ngay. Nếu chỉ làm được một việc hôm nay, hãy làm việc này.
Lớp 3 — Origin-lock: bịt đường vòng qua CDN
Với những domain thật sự núp sau CDN, khoá origin để chỉ nhận kết nối từ dải IP của CDN. Ai gõ thẳng IP gốc sẽ bị Traefik từ chối trước khi chạm bất cứ thứ gì:
http:
middlewares:
cdn-only:
ipAllowList:
sourceRange:
# dải IP của CDN bạn đang dùng — ví dụ với Cloudflare
- 173.245.48.0/20
- 103.21.244.0/22
- 104.16.0.0/13
# ... lấy đủ danh sách từ nguồn chính thức của nhà cung cấp
# và ĐỒNG BỘ TỰ ĐỘNG: hard-code rồi quên cập nhật
# là tự chặn chính người dùng của mình.Lưu ý quan trọng: ipAllowList mặc định kiểm IP kết nối TCP thật, không đọc X-Forwarded-For. Đó chính xác là hành vi bạn cần ở đây, vì đây là lớp xác thực "gói này có thật sự tới từ CDN không". Đừng khai thêm ipStrategy để đọc header ở middleware này, kẻo tự mở đường cho kẻ giả mạo header.
Đây là siết origin trên chính máy mình, không phải "nhờ CDN bảo vệ". Danh sách IP của CDN chỉ đóng vai một danh sách trắng, quyền quyết định nằm ở Traefik của bạn.
Lớp 0, nền móng: mạng, panel, SSH
Cuối cùng nhưng không kém quan trọng, và theo đúng bộ khuyến nghị bảo mật chính thức của Dokploy:
- Chỉ để 80/443/SSH ra Internet, và xác minh bằng cách quét từ ngoài.
- Đưa panel quản trị Dokploy về chỉ nghe qua VPN nội bộ (Tailscale/WireGuard). Không bao giờ phơi UI admin ra công cộng.
- SSH chỉ dùng key, tắt đăng nhập bằng mật khẩu, bật Fail2Ban.
Đây là những thứ nhàm chán nhưng là nơi phần lớn sự cố thật sự xảy ra.
Hai cái bẫy thực chiến
Giải pháp trên chỉ đúng nếu bạn né được hai cái bẫy này.
Bẫy 1: Dokploy tự quản cấu hình Traefik
Dokploy tự sinh lại cấu hình Traefik. Nếu bạn sửa tay file cấu hình tĩnh (traefik.yml), bản sửa có thể bị ghi đè khi cập nhật hoặc khi bạn thay đổi ứng dụng qua panel. Nguyên tắc sống còn:
Ưu tiên cấu hình động (file trong thư mụcdynamic/, hoặc nhãn container), thứ Dokploy tôn trọng và giữ lại. Chỉ đụng vào cấu hình tĩnh khi bắt buộc (ví dụ khai báo plugin ởexperimental.plugins), và phải kiểm lại sau mỗi lần nâng cấp Dokploy.
Các middleware rateLimit / headers / ipAllowList và firewall-bouncer đều là cấu hình động, an toàn. Chỉ có bouncer-plugin cần khai báo ở tầng tĩnh, và đó là chỗ phải cẩn thận nhất.
Bẫy 2: IP client thật, hay câu chuyện "ban nhầm cả thế giới"
Đây là bẫy khiến nhiều người tự bắn vào chân. CrowdSec và rate-limit ra quyết định dựa trên IP client. Nhưng sau một reverse proxy (và sau CDN), "IP client" mà Traefik nhìn thấy có thể là IP của CDN, không phải của kẻ tấn công.
Hậu quả nếu cấu hình sai: brute-force tới từ khắp nơi nhưng Traefik chỉ thấy một dải IP của CDN → CrowdSec ban dải đó → bạn chặn luôn CDN, tức là chặn toàn bộ người dùng thật.
Cách làm đúng phải phân biệt hai nhóm domain:
- Nhóm sau CDN: khai
forwardedHeaders.trustedIPsbằng dải IP CDN, rồi đọc IP thật từ headerCF-Connecting-IP/X-Forwarded-For. - Nhóm DNS-only: IP client chính là IP kết nối trực tiếp, không được tin
X-Forwarded-For(kẻo bị giả mạo header).
Cấu hình chiến lược lấy IP sai không chỉ làm phòng thủ vô dụng, nó biến phòng thủ thành vũ khí tự sát, tự chặn chính người dùng của mình. Đây là bước phải làm trước khi bật bất kỳ cơ chế ban IP nào.
Kiểm chứng: đừng tin, hãy đo
Một thay đổi bảo mật chưa được kiểm chứng thì chỉ là hy vọng. Bộ nghiệm thu tối thiểu:
- Bề mặt mạng: quét từ một máy ngoài Internet — chỉ 80/443/SSH mở, mọi port khác timeout.
- Origin-lock:
curlthẳng vào IP gốc kèmHost:của domain sau CDN → phải nhận 403, không phải nội dung thật. - Rate-limit: bắn liên tiếp quá ngưỡng → phải thấy 429, không phải 401 mãi.
- WAF: gửi một payload SQLi/XSS kinh điển tới một route thử → AppSec chặn, xuất hiện trong
cscli alerts list. - IPS: mô phỏng scan → IP bị ban,
cscli decisions listcó bản ghi, request tiếp theo từ IP đó bị drop. - Không tự bắn vào chân: người dùng thật qua CDN vẫn vào bình thường;
cscli decisions listkhông chứa dải IP của CDN.
Và vì bản thân lớp phòng thủ cũng có thể chết, hãy đưa nó vào hệ giám sát: cảnh báo khi bouncer mất kết nối, hoặc khi số lượng ban tăng vọt — dấu hiệu của một đợt tấn công đang diễn ra.
Khi quy mô lớn hơn một máy chủ
Mọi thứ trên đây viết cho một cụm Dokploy. Khi tổ chức của bạn có hàng chục dịch vụ nội bộ, nhiều cụm, nhiều đội cùng đẩy ứng dụng lên, bài toán đổi chất: không còn là "cài CrowdSec lên máy" mà là ai chịu trách nhiệm khi 2 giờ sáng có sự cố, ai rà lại cấu hình sau mỗi lần nâng cấp, và lấy gì chứng minh với bên kiểm toán rằng lớp phòng thủ thật sự đang chạy.
Ở Việt Nam, phần chứng minh đó không còn là chuyện tuỳ tâm. Khi hệ thống của bạn thuộc diện phải phân loại theo cấp độ an toàn thông tin, "ingress không ai gác" thôi cũng đã là một khoảng trống hồ sơ. Và nếu ứng dụng phơi ra Internet là kênh kinh doanh chính, việc gom WAF, chống DDoS, chặn bot và bảo vệ API vào một lớp thống nhất chính là bài toán WAAP.
Điều đọng lại
Dokploy giúp việc tự-host trở nên dễ đến mức nguy hiểm: dễ tới nỗi dễ quên rằng "chạy được" và "an toàn" là hai chuyện khác nhau. Bốn điều đọng lại:
- Reverse proxy không phải tường lửa. Traefik lo TLS và route; nó không soi tấn công. Lớp WAF/IPS là việc bạn phải chủ động thêm vào.
- Xác minh, đừng mặc định. Từ "Docker có bypass tường lửa không" tới "origin có bị lộ không", mọi khẳng định đều kiểm được bằng một lệnh. Bảo mật dựa trên cảm giác là bảo mật chưa tồn tại.
- Phòng thủ tại gốc. CDN có ích nhưng có thể bị vòng qua. Lớp chạy ngay trên máy, thấy đúng gói tin chạm tới ứng dụng, mới là lớp cuối cùng đứng vững, và nó thuộc về bạn.
- Nhiều lớp, không một viên đạn bạc. Firewall + rate-limit + IPS + WAF + origin-lock: mỗi lớp bắt một loại đòn khác nhau. Kẻ tấn công phải xuyên qua tất cả; bạn chỉ cần một lớp đứng vững.
Nếu bạn đang chạy Dokploy (hay bất kỳ PaaS tự-host nào dựng reverse proxy riêng) trên Internet công cộng, hãy dành mười lăm phút: liệt kê toàn bộ domain, quét port từ bên ngoài, và thử tự tấn công chính mình. Rất có thể bạn sẽ tìm thấy một cánh cửa đang mở.
Câu hỏi thường gặp
Dokploy có sẵn WAF hay tường lửa ứng dụng không?
Không. Dokploy dùng Traefik làm reverse proxy để lo TLS và định tuyến, nhưng không kèm WAF, IDS/IPS hay rate-limiting mặc định. Bạn phải tự thêm các lớp này.
Traefik có phải là một WAF không?
Không. Traefik là reverse proxy và load balancer, không phải Web Application Firewall. Nó không soi payload để chặn SQL injection, XSS hay RCE. Muốn có WAF, bạn cần thêm CrowdSec AppSec, Coraza/ModSecurity (OWASP CRS), hoặc một dịch vụ tương đương.
CrowdSec khác Fail2Ban thế nào?
Fail2Ban đọc log và ban IP cục bộ theo luật regex, tốt cho SSH. CrowdSec làm được điều đó nhưng nhanh hơn (viết bằng Go), hỗ trợ IPv6, có mạng IP reputation cộng đồng, và có thêm AppSec đóng vai WAF. Một cấu hình phổ biến là giữ Fail2Ban cho SSH và dùng CrowdSec cho tầng HTTP.
Bảo mật Dokploy theo cách này có cần Cloudflare không?
Không. Toàn bộ giải pháp chạy ngay trên máy chủ Dokploy, độc lập với mọi CDN. CDN (kể cả gói trả phí) là lớp bổ sung tuỳ chọn, không phải điều kiện cần, và như bài đã chỉ ra, chỉ dựa vào CDN thôi thì vẫn có thể bị vòng qua.
Các nguyên tắc này có áp dụng cho Coolify, CapRover hay Portainer không?
Có. Bất kỳ PaaS tự-host nào dựng một reverse proxy riêng (Traefik hoặc Nginx) trên VPS công cộng đều có chung ba lỗ hổng: Docker bypass tường lửa, ingress không WAF, và nguy cơ lộ IP gốc. Cùng một mô hình phòng thủ nhiều lớp áp dụng được.
Bài viết liên quan

Luật An ninh mạng 2025: phân loại hệ thống 5 cấp độ, doanh nghiệp cần làm gì
Từ 1/7/2026, Luật An ninh mạng 116/2025 hợp nhất hai luật cũ, lần đầu nêu "an ninh dữ liệu" và phân loại hệ thống theo 5 cấp độ. Nghĩa vụ mới và việc cần làm.
Đọc ↗
Hansoll bị ransomware: doanh nghiệp sản xuất, FDI phòng thủ thế nào
Vụ ransomware nhắm Hansoll Textile cho thấy doanh nghiệp sản xuất và FDI cũng là mục tiêu. Bài học phòng thủ thực chiến cho khối sản xuất.
Đọc ↗
Hai hệ thống cấp bộ bị xâm nhập, SOC "mù": vì sao cần giám sát hiện đại
Tại Vietnam Security Summit 2026, cơ quan an ninh mạng công bố hai sự cố ở cấp bộ mà SOC tại chỗ không phát hiện. Bài học về giám sát và bảo vệ.
Đọc ↗Checklist tuân thủ Luật Bảo vệ dữ liệu cá nhân
Rà soát doanh nghiệp trước khi Luật có hiệu lực 01/01/2026.