Thử tưởng tượng kịch bản này: hệ thống giám sát báo động đỏ, một service Node.js trong cụm Swarm của bạn vừa dính lỗ hổng RCE (Remote Code Execution) từ một thư viện bên thứ ba (supply-chain attack). Bạn tự tin vì đã triển khai các giải pháp ngăn chặn DDoS và cấu hình WAF để khóa chặt mọi cổng Ingress bên ngoài, chỉ chừa lại Port 443 cho Load Balancer.
Nhưng thực tế? Kẻ tấn công không quan tâm đến Ingress. Từ bên trong container bị chiếm quyền, chúng âm thầm chạy một lệnh curl đơn giản, tải thẳng một file binary cryptominer từ Internet về, hoặc mở một Reverse Shell kết nối ngược ra máy chủ C2 (Command & Control) của chúng. Mọi thứ lọt qua vì luồng mạng đi ra (Egress Traffic) của bạn đang mở.
Làm thế nào để kiểm soát kẽ hở bảo mật này, biến hệ thống của bạn thành một môi trường nội bất xuất? Làm sao để cấp quyền cho ứng dụng tải package hợp lệ nhưng lại chặn các kết nối gọi ra ngoài không xác định? Hãy cùng đi sâu vào giải pháp thiết lập và quản lý Egress Traffic an toàn cho Docker Swarm theo nguyên lý Default-Deny (chặn mặc định) ngay trên VPS Ubuntu.
Ác mộng Egress: Vì sao cấu hình HTTP_PROXY thôi là chưa đủ?
Phần lớn checklist bảo mật hạ tầng thường chỉ tập trung vào chặn lọc đầu vào. Tuy nhiên, một cụm Docker Swarm mặc định cấp quyền truy cập Internet không giới hạn cho mọi container.
Khi nhận ra vấn đề này, nhiều DevOps nghĩ ngay đến việc nhúng các biến môi trường HTTP_PROXY và HTTPS_PROXY vào Dockerfile. Ý tưởng là ép các request gọi API đi qua một proxy để kiểm duyệt. Tuy nhiên, cách làm này gần như không hiệu quả trước các cuộc tấn công có chủ đích vì những lý do thực chiến sau:
Mã độc không bao giờ tự nguyện đi qua Proxy
Biến môi trường proxy hoạt động theo cơ chế hợp tác. Các công cụ chuẩn như curl hay thư viện requests sẽ chủ động đọc biến này và tự định tuyến gói tin. Tuy nhiên, mã độc được viết ra để phớt lờ cấu hình môi trường. Chúng gọi thẳng các hàm API hệ điều hành như socket() và connect() để mở các kết nối TCP trực tiếp (bắt tay 3 bước) thẳng ra IP đích ngoài Internet.
Docker tự động vượt mặt tường lửa Host
Mặc định, Docker thao túng iptables rất mạnh tay để duy trì Routing Mesh. Nếu mã độc thiết lập một kết nối socket trực tiếp, gói tin sẽ đi từ container, qua bridge docker_gwbridge, rơi vào chuỗi FORWARD của iptables trên Host. Tại đây, luật MASQUERADE mặc định của Docker sẽ tự động dịch IP (NAT) và đẩy gói tin thẳng ra Internet mà không gặp bất kỳ rào cản nào từ các công cụ như UFW hay Firewalld.
Rào cản về giao thức
Proxy HTTP/HTTPS chỉ xử lý lưu lượng web (Lớp 7). Kẻ tấn công thường dùng các giao thức khác để giao tiếp và exfiltrate data (đánh cắp dữ liệu) như kết nối raw TCP, UDP, hay DNS Tunneling. Biến môi trường proxy không có tác dụng trước các loại lưu lượng này.

Điểm mù bảo mật Egress: Container bị chiếm quyền dễ dàng vượt qua tường lửa Ingress để kết nối ngược về máy chủ C2 của hacker.
Kiến trúc Default-Deny: Phòng thủ 2 lớp cho Docker Swarm
Để quản lý Egress Traffic an toàn cho Docker Swarm một cách triệt để, chúng ta cần áp dụng kiến trúc bảo mật Zero Trust phối hợp nhịp nhàng giữa tầng ứng dụng (Application Layer) và tầng mạng (Network Layer).
Lớp 1 (tầng ứng dụng): Squid Proxy Explicit kiểm duyệt Domain
Tại lớp này, chúng ta dựng một dịch vụ Squid Proxy đóng vai trò làm trạm gác. Các ứng dụng hợp lệ trong container bắt buộc phải cấu hình biến môi trường để gọi đến Squid. Squid sẽ đọc tên miền đích (thông qua lệnh CONNECT hoặc trường SNI của gói TLS ClientHello) để đối chiếu với một Whitelist.
- Nếu domain hợp lệ (ví dụ:
api.stripe.com): Squid tự đứng ra kết nối, lấy dữ liệu về cho container.
- Nếu domain lạ: Squid lập tức ngắt kết nối, trả về lỗi 403.
- Lợi thế: Mô hình Explicit Proxy giúp lọc HTTPS dựa trên domain mà không cần can thiệp SSL Bump (không giải mã TLS, không cần nhúng CA nội bộ phức tạp vào từng container).
Lớp 2 (tầng mạng): iptables DOCKER-USER chain
Đây là chốt chặn mạng mạnh mẽ. Bất kể tiến trình bên trong container tinh vi đến đâu, khi chúng cố gọi thẳng ra Internet, gói tin bắt buộc phải đi qua iptables của Host.
Chúng ta sử dụng chain DOCKER-USER, một chain đặc biệt do Docker thiết kế riêng để không bị daemon tự động ghi đè mỗi khi restart service. Tại đây, ta đặt luật DROP toàn bộ kết nối đi ra card mạng ngoài, chỉ mở ngoại lệ cho lưu lượng đi đến IP của Squid Proxy.

Kiến trúc Zero Trust 2 lớp: Squid Proxy kiểm duyệt tại tầng ứng dụng (Layer 7) và iptables khóa chặt tầng mạng (Layer 3).
Triển khai thực tế: Quản lý Egress Traffic an toàn cho Docker Swarm từng bước
Giả định hệ thống của bạn đang chạy Ubuntu 24.04 LTS và Docker Engine v27.x. Chúng ta sẽ bắt tay vào cấu hình hệ thống tường lửa nội bất xuất này.
Bước 1: Cài đặt Squid Proxy trên VPS Ubuntu & cảnh báo bảo mật
Để bắt đầu, bạn cần tiến hành tạo Proxy Server trên VPS Ubuntu bằng Squid và cài đặt thêm công cụ tạo mật khẩu (Apache utils).
Cập nhật danh sách gói phần mềm:
sudo apt update
Cài đặt Squid và công cụ tạo mật khẩu:
sudo apt install squid apache2-utils -y
⚠️ CẢNH BÁO BẢO MẬT THỜI SỰ (2026): Lỗ hổng Squidbleed (CVE-2026-47729)
Cộng đồng bảo mật vừa công bố một lỗ hổng tràn bộ nhớ nghiêm trọng ảnh hưởng đến các bản Squid < 7.6 thông qua tính năng FTP gateway. Để đảm bảo an toàn khi cài từ repository mặc định, hãy vô hiệu hóa tính năng FTP protocol trong cấu hình Squid hoặc nâng cấp lên phiên bản vá lỗi mới phát hành.
Không nên publish port 3128 của Squid ra 0.0.0.0 để tránh biến VPS thành Open Proxy. Hãy bind Squid vào IP của bridge docker0 (ví dụ: 172.17.0.1) hoặc Localhost.
Tạo file Whitelist chứa các domain được phép:
sudo touch /etc/squid/allowed_domains.txt
Mở file để chỉnh sửa:
sudo nano /etc/squid/allowed_domains.txt
Thêm các domain thực sự cần thiết:
.github.com
.docker.com
.ubuntu.com
.npmjs.org
.pypi.org
Sửa file cấu hình chính /etc/squid/squid.conf:
# Bind vào IP nội bộ của docker0
http_port 172.17.0.1:3128
# Tắt FTP Gateway để tránh CVE-2026-47729
acl ftp proto FTP
http_access deny ftp
# Cấu hình ACL cơ bản
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT
# Khai báo dải IP của Docker Swarm (VD: 172.16.0.0/12)
acl docker_network src 172.16.0.0/12
acl docker_network src 10.0.0.0/8
# Khai báo Whitelist
acl allowed_sites dstdomain "/etc/squid/allowed_domains.txt"
# Access Rules (Đọc từ trên xuống dưới)
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
# Cho phép container trong mạng Docker truy cập domain whitelist
http_access allow docker_network allowed_sites
# Chặn tất cả các request khác (Default-Deny)
http_access deny all
# Ẩn thông tin VPS
via off
forwarded_for delete
httpd_suppress_version_string on
Kỹ năng vận hành Zero-Downtime:
Sau khi sửa cấu hình, thay vì dùng sudo systemctl restart squid (sẽ cắt đứt mọi TCP session đang tải dở của container gây downtime), hãy sử dụng lệnh reconfigure.
Kiểm tra cú pháp file cấu hình:
sudo squid -k parse
Áp dụng cấu hình mới mà không gây downtime:
sudo squid -k reconfigure
Lệnh này nạp lại Whitelist mượt mà mà không làm rớt các kết nối đang hoạt động.

Squid đọc header CONNECT hoặc trường SNI để đối chiếu Whitelist mà không cần can thiệp giải mã gói tin TLS.
Bước 2: Ép các service trong Swarm đi qua Squid (không hardcode IP)
Trong các bài hướng dẫn cũ, người ta thường hardcode IP 172.17.0.1 vào biến môi trường. Điều này tiềm ẩn rủi ro khi scale hạ tầng vì IP bridge có thể khác nhau trên từng host.
Giải pháp hiện đại là sử dụng tính năng host.docker.internal được map thẳng vào host-gateway trong file docker-compose.yml:
version: "3.8"
services:
backend-api:
image: my-backend:v2
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- HTTP_PROXY=http://host.docker.internal:3128
- HTTPS_PROXY=http://host.docker.internal:3128
- NO_PROXY=localhost,127.0.0.1,.internal-network,db-service
networks:
- overlay-net
deploy:
replicas: 3
networks:
overlay-net:
driver: overlay
Ghi chú: Biến NO_PROXY đóng vai trò quan trọng. Đừng quên liệt kê các subnet hoặc hostname nội bộ (như db-service). Nếu thiếu, traffic nội bộ cũng được định tuyến đến Squid và bị drop lập tức.
Bước 3: Khóa luồng Egress trực tiếp bằng DOCKER-USER chain
Bây giờ, chúng ta dùng iptables để ngăn chặn mọi nỗ lực bypass proxy. Trên tất cả các node (manager và worker), thực thi các lệnh sau:
Cho phép các luồng kết nối trả về (RELATED, ESTABLISHED) không bị drop:
sudo iptables -I DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
Cho phép container truy cập cổng Proxy 3128 (Lưu lượng container truy cập Host IP đi vào chain INPUT, không phải FORWARD):
sudo iptables -I INPUT 1 -p tcp -s 172.16.0.0/12 --dport 3128 -j ACCEPT
Thiết lập chính sách chặn, DROP toàn bộ request từ mạng Docker đi ra card ngoài:
sudo iptables -A DOCKER-USER -o eth0 -j DROP
(Hãy thay eth0 bằng tên interface vật lý thực tế như ens3 hoặc eno1).
Lưu lại quy tắc iptables vĩnh viễn để chống mất config khi VPS reboot.
Cài đặt công cụ iptables-persistent:
sudo apt-get install iptables-persistent netfilter-persistent -y
Lưu cấu hình iptables:
sudo netfilter-persistent save

Nguyên lý Default-Deny ở tầng mạng: iptables rà soát gói tin qua từng bộ lọc trước khi chặn bằng lệnh DROP ở cuối chain.
Kiểm thử (Testing) và bịt các lỗ hổng nâng cao
Kiến trúc chỉ an toàn khi nó hoạt động hiệu quả qua các bài test thực tế. Hãy exec vào một container bất kỳ và giả lập hành vi không xác định.
Test kịch bản Bypass (thử nghiệm với socket trực tiếp)
Sử dụng cờ --noproxy '*' để bắt curl phớt lờ biến môi trường và kết nối trực tiếp ra ngoài:
Thực thi lệnh kiểm tra kết nối trực tiếp:
docker exec -it <container_name> curl -I --noproxy '*' --connect-timeout 5 https://google.com
Kết quả chuẩn: Lệnh bị treo cứng và báo Timeout. Gói tin đã bị DOCKER-USER chặn đứng.
Test kịch bản Domain Block (chặn C2 Server)
Gọi qua Proxy nhưng trỏ đến một domain ngoài Whitelist.
Thực thi lệnh truy cập domain không hợp lệ:
docker exec -it <container_name> curl -I https://malicious-c2.xyz
Kết quả chuẩn: Nhận ngay HTTP 403 Forbidden từ Squid.
Bịt kín các điểm mù kỹ thuật
- Rủi ro rò rỉ qua IPv6: Nếu VPS hỗ trợ IPv6, container có thể âm thầm dùng giao thức này để lách iptables IPv4. Khuyến nghị tắt hẳn IPv6 nếu không dùng (
sysctl -w net.ipv6.conf.all.disable_ipv6=1) hoặc cấu hình ip6tables tương tự.
- DNS Tunneling: Chặn TCP là chưa đủ. Kẻ tấn công có thể chèn dữ liệu đánh cắp vào gói tin truy vấn UDP Port 53. Hãy cấu hình container dùng CoreDNS nội bộ và DROP
UDP 53 ra Internet trong DOCKER-USER.
- Vấn đề với Legacy App (ứng dụng cũ): Có những phần mềm cũ (như một số công cụ viết bằng C/C++) không hỗ trợ biến
HTTP_PROXY. Trong trường hợp này, thay vì mở firewall, bạn có thể áp dụng giải pháp Transparent Proxy. Kỹ thuật này đòi hỏi cấu hình iptables PREROUTING REDIRECT port 80/443 về Squid, kết hợp với chế độ intercept và ssl_bump peek and splice để Squid xử lý gói tin một cách minh bạch mà không cần can thiệp cấu hình ứng dụng.
Giám sát tập trung (Monitoring) với Egress Logs
Luật phòng thủ đã xong, nhưng hệ thống cảnh báo (Observability) mới là thứ giúp bạn quản lý hiệu quả.
Khi Squid chặn một request, nó ghi mã TCP_DENIED/403 vào access.log. Bạn có thể phân tích nhanh trực tiếp trên Host để xem có container nào đang thực hiện hành vi bất thường không:
Lọc log Squid để tìm kiếm các request bị từ chối:
sudo awk '$4 ~ /TCP_DENIED/ {print $7}' /var/log/squid/access.log | sort | uniq -c | sort -nr | head -10
Trong môi trường Production, để thay thế cho việc đọc log thủ công, bạn nên xây dựng hệ thống giám sát Proxy với Grafana để thu thập metrics và đẩy cảnh báo tự động. Tạo ngay một Alert Rule: Nếu một IP nội bộ sinh ra quá 50 lỗi TCP_DENIED trong 1 phút -> Kích hoạt PagerDuty/Slack gọi kỹ sư trực ca vào isolate (cô lập) node đó ngay lập tức vì có khả năng cao node đó đang thực hiện các tác vụ rà soát hệ thống mạng bất thường.
Câu hỏi thường gặp (FAQ)
1. Tại sao không dùng tường lửa bên ngoài (như AWS Security Group) để quản lý Egress?
Tường lửa ngoài chỉ quản lý được IP/Port (Lớp 3/4). Các API hiện đại (Stripe, GitHub) đổi IP liên tục, nếu chặn theo IP sẽ làm ứng dụng ngừng hoạt động. Squid Proxy (Lớp 7) giải quyết việc này bằng cách lọc trực tiếp theo tên miền (Domain Whitelist).
2. Có bắt buộc phải cấu hình SSL Bump (giải mã HTTPS) trên Squid không?
Không cần thiết. Khi bạn cấu hình ứng dụng dùng biến HTTPS_PROXY (Explicit Proxy), Squid có thể đọc tên miền đích từ lệnh CONNECT ban đầu mà không cần giải mã nội dung gói tin TLS, tránh được các rắc rối về cấp phát chứng chỉ (Certificate).
3. Ứng dụng cũ (Legacy App) không tương thích với biến HTTP_PROXY thì xử lý thế nào?
Bạn có thể chuyển sang mô hình Transparent Proxy. Dùng iptables PREROUTING REDIRECT ép luồng mạng vào cổng intercept của Squid, kết hợp tính năng peek and splice để Squid bắt gói tin mà không yêu cầu cấu hình trên ứng dụng.
4. Cập nhật thêm tên miền vào Whitelist có làm rớt mạng container (Downtime) không?
Không, miễn là bạn không dùng lệnh restart. Chỉ cần chạy sudo squid -k reconfigure, Squid sẽ nạp lại file Whitelist mượt mà (Zero-downtime) mà không làm ngắt các kết nối TCP đang hoạt động.
5. Lệnh chặn trong chain DOCKER-USER có bị mất khi khởi động lại VPS không?
Có. Iptables chỉ lưu trên RAM. Bạn cần cài gói iptables-persistent (trên Ubuntu) và chạy lệnh sudo netfilter-persistent save để lưu trữ cấu hình, tự động nạp lại mỗi khi server khởi động lại.
6. Vì sao phải chặn luôn cả Port 53 (DNS) ra Internet?
Để chống Data Exfiltration. Kẻ tấn công có thể lách qua proxy bằng cách chèn dữ liệu vào các gói tin truy vấn DNS (DNS Tunneling). Chặn Port 53 ngoại mạng và yêu cầu dùng DNS nội bộ là phương pháp hiệu quả để kiểm soát rủi ro này.
Kết luận
Áp dụng phương pháp quản lý Egress Traffic an toàn cho Docker Swarm không phải là thao tác cấu hình làm một lần rồi thôi. Để duy trì hệ thống bảo mật này, cần lưu ý:
- Quyền tối thiểu (Least Privilege): Danh sách Whitelist cần tối giản. Không thêm các domain không thực sự cần thiết.
- Infrastructure as Code (IaC): Khuyến nghị code hóa cấu hình
squid.conf và iptables bằng Ansible hoặc Terraform để đảm bảo mọi node Worker đều áp dụng chung một tiêu chuẩn bảo mật.
- Cập nhật liên tục: Các lỗ hổng như Squidbleed (CVE-2026-47729) cho thấy công cụ bảo mật cũng có rủi ro. Hãy luôn theo dõi bản vá lỗi từ nhà phát hành.
Sự kết hợp giữa Domain filtering ở tầng ứng dụng và Iptables Drop ở tầng mạng mang đến một lưới rào Zero Trust vững chắc. Cho dù tiến trình lạ có vượt qua lớp phòng thủ đầu tiên, chúng cũng sẽ bị cô lập trong container mà không thể liên lạc với máy chủ C2.
Tài liệu tham khảo