Docker đã tự vượt qua UFW và làm lộ Database của bạn ra Internet
Docker có thể khiến cổng database bị truy cập từ Internet dù UFW đang bật. Đường forwarding khiến database bị expose ra Internet dù VPS đang bật UFW. Kiểm tra Docker Compose, port 5432 và cách bảo vệ database.
Chi tiết ưu đãi
Một lỗi cấu hình Docker rất dễ bỏ qua có thể khiến database trên VPS bị expose ra Internet — ngay cả khi bạn đã bật UFW.
Tôi viết bài này sau khi phát hiện một điều khá nguy hiểm trên chính VPS của mình:
UFW vẫn đang bật. Rule firewall vẫn trông hoàn toàn bình thường. Nhưng cổng PostgreSQL của Docker vẫn có thể truy cập từ Internet.
Nếu bạn đang chạy Docker + UFW trên VPS, hãy kiểm tra ngay.
Đây không phải là chuyện "5432 có nên mở hay không".
Vấn đề nằm ở cách Docker xử lý port forwarding và firewall.
⚠️ Tình huống rất dễ xảy ra
Bạn triển khai PostgreSQL bằng Docker Compose:
services: postgres: image: postgres ports: - "5432:5432"
Nhìn qua chẳng có gì bất thường.
Bạn tiếp tục bật UFW:
sudo ufw enable sudo ufw status
và thấy firewall đang hoạt động.
Bạn nghĩ:
Internet ↓ UFW ↓ PostgreSQL
Database được bảo vệ.
Nhưng thực tế với Docker có thể không đơn giản như vậy.
Docker tạo các rule NAT/forwarding riêng để đưa traffic từ port của host vào container.
Luồng traffic có thể trở thành:
Internet │ ▼ Public IP của VPS │ ▼ Docker published port │ ▼ Container │ ▼ PostgreSQL :5432
Và đây chính là phần mà rất nhiều người quản trị VPS bỏ qua.
🔥 Điều nguy hiểm nhất: Bạn có thể tưởng rằng firewall đang bảo vệ mình
Đây mới là vấn đề tôi muốn cảnh báo.
Không phải:
"Docker mở port nên database bị lộ."
Mà là:
Bạn có thể nhìn vào UFW và tưởng rằng cổng đó đang được firewall kiểm soát, trong khi Docker networking có thể tạo đường đi riêng cho traffic tới container.
Vì vậy, chỉ kiểm tra:
sudo ufw status
chưa đủ để kết luận Docker container của bạn đang an toàn.
🧨 Một dòng trong Docker Compose có thể thay đổi hoàn toàn mức độ exposure
So sánh:
ports: - "5432:5432"
với:
ports: - "127.0.0.1:5432:5432"
Đây không phải là hai cách viết tương đương.
Cấu hình thứ nhất
- "5432:5432"
đang publish port container ra host theo cách có thể khiến dịch vụ nhận kết nối từ các interface bên ngoài.
Cấu hình thứ hai
- "127.0.0.1:5432:5432"
chỉ bind port trên localhost.
Tức là:
Internet ✕ │ Public IP ✕ │ 127.0.0.1:5432 │ ▼ PostgreSQL
Trong khi ứng dụng Docker khác vẫn có thể giao tiếp với database thông qua Docker network.
💣 Và đây mới là lỗi cấu hình nguy hiểm
Nếu PostgreSQL không cần truy cập trực tiếp từ Internet, tại sao chúng ta lại publish nó?
Ví dụ:
ports: - "5432:5432"
Ứng dụng và PostgreSQL đều nằm trong Docker Compose:
┌─────────────────────────────┐ │ Docker │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ App │──▶│ Postgres│ │ │ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────┘
App có thể gọi:
postgres:5432
Không cần PostgreSQL phải xuất hiện trên Internet.
Vậy mà rất nhiều docker-compose.yml vẫn có:
ports: - "5432:5432"
Đây chính là kiểu cấu hình tôi muốn mọi người kiểm tra lại ngay trên VPS của mình.
🔎 Làm sao biết VPS của bạn có đang bị expose?
Đừng chỉ kiểm tra UFW.
Hãy kiểm tra Docker trước:
docker ps
Nếu thấy:
0.0.0.0:5432->5432/tcp
thì đáng để kiểm tra ngay.
Tiếp tục:
sudo ss -lntp
và kiểm tra các port database.
Ví dụ:
5432 3306 6379 27017 9200
Tương ứng thường là:
- PostgreSQL
- MySQL/MariaDB
- Redis
- MongoDB
- Elasticsearch
🚨 Không chỉ PostgreSQL
Đây là điều càng đáng chú ý.
Nếu bạn sử dụng Docker, hãy kiểm tra toàn bộ những service có:
ports:
Đặc biệt:
ports: - "3306:3306"
ports: - "6379:6379"
ports: - "27017:27017"
ports: - "9200:9200"
Một database bị expose Internet không đơn giản chỉ là vấn đề "có người nhìn thấy port".
Nó có thể dẫn tới:
Port scan → nhận diện service → brute-force → khai thác cấu hình yếu → truy cập dữ liệu.
Đặc biệt nguy hiểm nếu database:
- sử dụng mật khẩu yếu;
- cho phép remote login;
- không giới hạn source IP;
- chứa dữ liệu production;
- chứa API key hoặc thông tin nhạy cảm;
- không có authentication đúng cách.
🛡️ Cách tôi xử lý
Nếu database chỉ phục vụ application trong Docker, không publish port ra ngoài.
Thay vì:
postgres: image: postgres ports: - "5432:5432"
hãy để:
postgres: image: postgres
Application kết nối:
postgres:5432
Docker network sẽ xử lý giao tiếp nội bộ.
Nếu bắt buộc phải truy cập database từ VPS
Có thể bind vào localhost:
ports: - "127.0.0.1:5432:5432"
Sau đó nếu cần truy cập từ máy cá nhân, hãy dùng SSH tunnel thay vì mở PostgreSQL trực tiếp ra Internet.
Ví dụ:
Laptop │ │ SSH ▼ VPS │ 127.0.0.1:5432 │ ▼ PostgreSQL
So với:
Internet │ ▼ VPS:5432 │ ▼ PostgreSQL
thì đây là một khác biệt rất lớn về attack surface.
🔥 Đừng chỉ kiểm tra "UFW có bật không"
Đây là takeaway quan trọng nhất của bài.
Khi dùng Docker, câu hỏi không nên chỉ là:
UFW có chặn port 5432 không?
Mà phải là:
Docker đang publish port nào? Bind trên interface nào? Traffic đang đi qua chain nào? Container nào đang nhận traffic từ Internet?
Hãy kiểm tra cả:
sudo ufw status verbose
docker ps
sudo ss -lntup
và đặc biệt là các cấu hình:
ports:
trong toàn bộ Docker Compose.
⚠️ Nếu bạn đang chạy Docker trên VPS, hãy kiểm tra ngay hôm nay
Tôi từng nghĩ:
"Có UFW rồi thì database nội bộ không thể tự nhiên lộ ra Internet."
Cho đến khi kiểm tra thực tế.
Đừng đợi đến lúc nhìn thấy log đăng nhập bất thường, database bị scan hoặc dữ liệu bị truy cập mới kiểm tra.
Nếu một database không cần Internet, cách an toàn nhất thường rất đơn giản:
# Không publish database
hoặc:
ports: - "127.0.0.1:5432:5432"
Đừng mở một cánh cửa mà ứng dụng của bạn không cần.
📌 Tóm tắt nhanh
| Cấu hình | Rủi ro |
|---|---|
5432:5432 | ⚠️ Có thể expose ra interface bên ngoài |
127.0.0.1:5432:5432 | ✅ Chỉ bind localhost |
Không khai báo ports | ✅ Database chỉ dùng Docker network |
| Chỉ dựa vào UFW | ⚠️ Chưa đủ để đánh giá Docker exposure |
| Database public + password yếu | 🔴 Rủi ro cao |
Một dòng đáng nhớ:
Docker không chỉ là container. Docker còn can thiệp vào networking và firewall của Linux. Vì vậy, bật UFW không có nghĩa là bạn đã hiểu hết đường đi của traffic tới container.
Nếu bạn quản trị VPS bằng Docker, hãy kiểm tra docker ps ngay bây giờ. Một dòng 0.0.0.0:5432->5432/tcp có thể đáng để bạn dừng lại và xem xét.
