Traefik cần đọc Docker API để tự phát hiện container, label, network và cập nhật router. Cách cấu hình phổ biến là mount trực tiếp /var/run/docker.sock vào Traefik. Cách này tiện nhưng làm tăng phạm vi ảnh hưởng: nếu Traefik bị khai thác, kẻ tấn công có thể tiếp cận Docker daemon trên máy chủ. Vì vậy, bảo mật Docker Socket là bước quan trọng khi vận hành Traefik trên VPS production.
Giải pháp trong bài là bảo mật Docker Socket bằng docker-socket-proxy. Traefik chỉ kết nối đến một HTTP proxy trong mạng Docker nội bộ. Proxy cho phép các API đọc cần thiết và trả 403 Forbidden với yêu cầu ghi hoặc API không được cấp quyền.
Docker Socket Proxy giúp giảm quyền truy cập, nhưng không biến Docker Socket thành tài nguyên hoàn toàn an toàn. Chỉ dùng image tin cậy, không publish cổng
2375, cập nhật định kỳ và tiếp tục bảo vệ máy chủ bằng firewall, phân quyền SSH và backup.
Vì sao cần bảo mật Docker Socket?
Docker daemon chạy với quyền rất cao. Người có quyền điều khiển daemon thường có thể tạo container mới, mount filesystem của host hoặc can thiệp container đang chạy. Docker cũng khuyến cáo chỉ người dùng tin cậy mới được quyền điều khiển daemon.
Khi mount trực tiếp socket vào Traefik như dưới đây, Traefik nhìn thấy toàn bộ Unix socket:
services:
traefik:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Cờ :ro chỉ làm bind mount ở chế độ chỉ đọc theo filesystem. Docker API vẫn có thể nhận HTTP method ghi nếu tiến trình bên trong container gửi yêu cầu phù hợp. Vì vậy, :ro không phải lớp ủy quyền API.
Mục tiêu bảo mật Docker Socket trong mô hình này là:
- Chỉ cho Traefik đọc container, network, event, ping và phiên bản API;
- Chặn
POST, thao tác tạo, chạy, dừng, xóa hoặc exec container; - Không publish cổng proxy ra host hoặc Internet;
- Tách proxy trong một Docker network nội bộ chỉ Traefik được tham gia;
- Giữ khả năng tự động phát hiện container bằng label.
Kiến trúc Traefik Docker Socket Proxy
Luồng truy cập sau khi hardening:
Internet → Traefik → Website hoặc dịch vụ
↓
socket_proxy network nội bộ
↓
Docker Socket Proxy → /var/run/docker.sock
Traefik không còn mount /var/run/docker.sock. Chỉ container socket-proxy giữ socket thật. Mọi yêu cầu từ Traefik đi qua HAProxy và được lọc theo biến môi trường. Mô hình này giúp bảo mật Docker Socket mà vẫn giữ khả năng tự động phát hiện dịch vụ của Traefik.

Yêu cầu trước khi triển khai
Hướng dẫn áp dụng cho Docker Engine và Docker Compose trên Linux. Trước khi thay đổi, cần:
- Có quyền
roothoặc tài khoản được phép chạy Docker; - Biết thư mục chứa file Compose hiện tại của Traefik;
- Đảm bảo Traefik đang hoạt động bình thường;
- Có cửa sổ bảo trì ngắn và phương án rollback;
- Không triển khai trực tiếp nếu chưa xác định container nào đang dùng chung Docker Socket.
Kiểm tra nhanh:
docker --version
docker compose version
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
Tìm container đang mount Docker Socket:
docker inspect $(docker ps -q) \
--format '{{.Name}} {{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}' \
| grep '/var/run/docker.sock'
Bước 1: Backup cấu hình Traefik
Di chuyển đến thư mục Compose và tạo bản sao có timestamp:
cd /opt/traefik
backup_dir="/root/backups/traefik-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
cp -a docker-compose.yml .env "$backup_dir"/ 2>/dev/null || true
printf 'Backup: %s\n' "$backup_dir"
Xuất cấu hình Compose đã hợp nhất để dễ đối chiếu:
docker compose config > "$backup_dir/docker-compose.rendered.yml"
Không đưa file .env, token DNS hoặc nội dung backup lên Git công khai.
Bước 2: Tạo mạng nội bộ cho Socket Proxy
Trong file Compose, khai báo hai mạng. socket_proxy chỉ dùng giữa Traefik và proxy; proxy dùng để Traefik kết nối đến các dịch vụ web.
networks:
socket_proxy:
name: socket_proxy
internal: true
proxy:
name: proxy
Thuộc tính internal: true giúp mạng không có kết nối ra ngoài mặc định. Không thêm ports cho dịch vụ Socket Proxy và không bind 2375:2375 ra host.
Bước 3: Thêm Docker Socket Proxy
Thêm service sau vào Compose. Ví dụ ghim docker-socket-proxy phiên bản 0.5.0, là bản phát hành được kiểm tra khi viết bài.
services:
socket-proxy:
image: ghcr.io/tecnativa/docker-socket-proxy:v0.5.0
container_name: socket-proxy
privileged: true
environment:
CONTAINERS: 1
NETWORKS: 1
EVENTS: 1
PING: 1
VERSION: 1
POST: 0
AUTH: 0
SECRETS: 0
BUILD: 0
COMMIT: 0
CONFIGS: 0
EXEC: 0
IMAGES: 0
INFO: 0
NODES: 0
SERVICES: 0
SWARM: 0
SYSTEM: 0
TASKS: 0
VOLUMES: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- socket_proxy
restart: unless-stopped
Theo tài liệu upstream, container proxy cần privileged trong một số môi trường SELinux hoặc AppArmor để kết nối socket. Quyền này chỉ cấp cho Socket Proxy, không cấp cho Traefik. POST: 0 buộc API về chế độ đọc đối với các nhóm đã cho phép.
Các quyền quan trọng:
EVENTS,PING,VERSION: Cho phép Traefik theo dõi thay đổi và thương lượng API;CONTAINERS: Cho phép đọc danh sách và metadata container;NETWORKS: Cho phép Traefik xác định network của backend;POST: 0: Chặn toàn bộ HTTP POST;AUTH,SECRETS,EXEC,IMAGES,VOLUMES: Giữ ở trạng thái từ chối.
Bước 4: Chuyển Traefik sang Socket Proxy
Xóa mount Docker Socket khỏi service Traefik:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Sau đó cấu hình endpoint và hai network:
services:
traefik:
image: traefik:v3.7.18
depends_on:
- socket-proxy
command:
- --providers.docker=true
- --providers.docker.endpoint=tcp://socket-proxy:2375
- --providers.docker.exposedbydefault=false
- --providers.docker.network=proxy
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
networks:
- socket_proxy
- proxy
security_opt:
- no-new-privileges:true
restart: unless-stopped
exposedByDefault=false giúp Traefik chỉ công khai container có label traefik.enable=true. Đây là lớp giảm rủi ro riêng, không thay thế việc bảo mật Docker Socket.

Bước 5: Kiểm tra cú pháp và khởi động
Chạy kiểm tra trước khi pull hoặc tạo lại container:
docker compose config -q
Nếu không có lỗi, tải image và khởi động:
docker compose pull
docker compose up -d
Kiểm tra trạng thái và log:
docker compose ps
docker compose logs --tail=100 socket-proxy traefik
Log Traefik không được xuất hiện lỗi kết nối tcp://socket-proxy:2375. Các router hiện có phải tiếp tục xuất hiện trên dashboard hoặc API nội bộ của Traefik.
Bước 6: Kiểm tra quyền đọc và yêu cầu bị chặn
Dùng container curl tạm thời trong mạng socket_proxy. Không cần cài thêm gói lên host.
Kiểm tra ping và danh sách container:
docker run --rm --network socket_proxy curlimages/curl:8.23.0 \
-sS -o /dev/null -w 'PING HTTP %{http_code}\n' \
http://socket-proxy:2375/_ping
docker run --rm --network socket_proxy curlimages/curl:8.23.0 \
-sS -o /dev/null -w 'CONTAINERS HTTP %{http_code}\n' \
http://socket-proxy:2375/containers/json
Hai lệnh phải trả 200. Tiếp theo thử một yêu cầu POST không kèm payload hợp lệ:
docker run --rm --network socket_proxy curlimages/curl:8.23.0 \
-sS -o /dev/null -w 'POST HTTP %{http_code}\n' \
-X POST http://socket-proxy:2375/containers/create
Kết quả mong đợi là 403. Yêu cầu bị chặn ở proxy trước khi đến Docker daemon, vì vậy không có container nào được tạo.

Bước 7: Kiểm tra website sau khi bảo mật Docker Socket
Kiểm tra từng tên miền đang đi qua Traefik:
curl -I https://example.com
curl -I https://app.example.com
Kiểm tra Traefik vẫn nhận container có label:
docker compose logs --since=10m traefik \
| grep -Ei 'error|docker|provider'
Nếu website trả 404, kiểm tra container backend cùng tham gia network proxy và label traefik.docker.network=proxy. Nếu Traefik báo không kết nối được provider, kiểm tra tên service socket-proxy, network socket_proxy và endpoint TCP.
Checklist hardening bổ sung
Sau khi bảo mật Docker Socket, nên rà thêm:
- Không có mapping
2375:2375hoặc rule firewall mở cổng 2375; - Chỉ Traefik và Socket Proxy tham gia network
socket_proxy; - Traefik không còn mount
/var/run/docker.sock; POST,EXEC,IMAGES,VOLUMES,SECRETSvàAUTHđều bị tắt;- Traefik dùng
exposedByDefault=false; - Dashboard Traefik không được công khai nếu chưa có xác thực và giới hạn IP;
- Image được ghim phiên bản thay vì dùng
latesttrên production; - Có quy trình kiểm tra release mới và rollback trước khi cập nhật;
- Docker daemon không lắng nghe TCP công khai nếu không có TLS và kiểm soát truy cập.
Rollback khi Traefik không nhận container
Nếu cần khôi phục nhanh, dùng file backup:
cd /opt/traefik
cp -a /root/backups/traefik-YYYYMMDD-HHMMSS/docker-compose.yml ./docker-compose.yml
cp -a /root/backups/traefik-YYYYMMDD-HHMMSS/.env ./.env 2>/dev/null || true
docker compose config -q
docker compose up -d
Sau rollback, kiểm tra docker compose ps, log Traefik và HTTPS của tất cả tên miền. Không xóa container hoặc network Socket Proxy cho đến khi xác nhận dịch vụ cũ hoạt động bình thường.
Câu hỏi thường gặp
Mount Docker Socket với :ro đã đủ an toàn chưa?
Chưa. :ro kiểm soát bind mount ở mức filesystem, không phải chính sách ủy quyền cho từng Docker API endpoint hoặc HTTP method. Socket Proxy bổ sung lớp lọc API.
Có nên publish cổng 2375 ra localhost không?
Với mô hình Compose trong bài, không cần publish. Traefik kết nối trực tiếp qua network socket_proxy. Upstream cũng khuyến cáo không để cổng proxy trên mạng công khai.
Docker Socket Proxy có thay thế TLS không?
Không. Proxy dùng HTTP nội bộ và không tích hợp TLS. Nếu truy cập Docker daemon qua mạng khác máy, cần thiết kế xác thực, TLS và ủy quyền phù hợp theo tài liệu Docker.
Có cần bật SERVICES và TASKS không?
Docker standalone thông thường không cần. Hai nhóm này chủ yếu liên quan Docker Swarm. Chỉ bật quyền sau khi xác định Traefik thực sự cần và đã đánh giá rủi ro.
Kết luận
Cách bảo mật Docker Socket tốt hơn cho Traefik là bỏ mount socket trực tiếp, chuyển Docker provider sang tcp://socket-proxy:2375, chỉ cấp API đọc tối thiểu và đặt proxy trong network nội bộ. Sau triển khai, cần chứng minh cả hai chiều: API đọc trả 200, còn yêu cầu POST trả 403.
Nếu đang triển khai Traefik cho n8n, có thể áp dụng cấu hình này sau bài cài n8n trên VPS bằng Docker Compose và Traefik. Bạn cũng có thể xem thêm hướng dẫn cài đặt và sử dụng Docker Compose trên Ubuntu.
