Bảo mật Docker Socket: Đừng biến ‘/var/run/docker.sock’ thành cửa hậu cho Hacker

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

Cơn ác mộng mang tên /var/run/docker.sock

Anh em DevOps chắc chẳng lạ gì dòng config - /var/run/docker.sock:/var/run/docker.sock mỗi khi setup Traefik hay Portainer. Tiện thì tiện thật, chỉ cần copy-paste là chạy ngay. Nhưng bạn có biết mình đang “giao trứng cho ác”? Theo nhiều báo cáo bảo mật, việc lạm dụng Docker Socket là con đường ngắn nhất để hacker thực hiện container breakout.

Thú thực, Docker Socket chẳng khác nào chiếc chìa khóa vạn năng. Container nào nắm được file này sẽ có quyền điều khiển toàn bộ host với đặc quyền Root. Hacker chỉ cần chiếm được một ứng dụng web lỏng lẻo, sau đó dùng socket này để xóa sạch database hoặc biến server của bạn thành máy đào coin. Chỉ cần quyền “liệt kê container” mà lại dâng cả quyền admin? Đó là một sai lầm sơ đẳng mà mình từng mắc phải.

Ba cách tiếp cận Docker API phổ biến

Hãy thử đặt lên bàn cân những cách chúng ta thường dùng để kết nối ứng dụng với Docker API:

  • Mount trực tiếp socket: Nhanh, gọn nhưng cực kỳ nguy hiểm. Nếu container bị hack, coi như bạn mất luôn con VPS.
  • Mở Docker qua TCP (Port 2375/2376): Cách này đòi hỏi bạn phải cấu hình TLS cực kỳ chuẩn chỉ. Nếu sơ suất để lộ port mà không có chứng chỉ, các công cụ quét như Shodan sẽ tìm thấy bạn trong vòng vài phút.
  • Dùng Proxy trung gian (Tecnativa): Đây là giải pháp mình tin dùng nhất. Nó hoạt động như một lớp firewall thông minh đứng trước Docker Socket, chỉ mở đúng những gì cần thiết.

Tại sao Tecnativa Docker Socket Proxy là lựa chọn hàng đầu?

Thay vì tự loay hoay cấu hình Nginx hay HAProxy phức tạp, mình chọn Tecnativa Docker Socket Proxy. Image này được build sẵn dựa trên HAProxy, chuyên dụng để lọc các HTTP request gửi đến socket.

Những điểm cộng đáng giá:

  • Kiểm soát quyền lực tuyệt đối: Bạn có thể quy định máy A chỉ được xem danh sách (GET), trong khi máy B mới có quyền khởi động lại container (POST).
  • Chặn đứng hành vi phá hoại: Mặc định proxy sẽ khóa mọi quyền ghi. Muốn mở, bạn phải chủ động khai báo qua biến môi trường.
  • Tương thích 100%: Các công cụ như Traefik hay Portainer chỉ cần đổi URL kết nối sang tcp://docker-proxy:2375 là hoạt động mượt mà.

Con số thực tế:

Nhiều bạn lo ngại tốn tài nguyên, nhưng thực tế container này chỉ ngốn khoảng 15-20MB RAM. Một cái giá quá rẻ để đổi lấy sự an toàn cho toàn bộ hệ thống.

Triển khai thực tế trong 5 phút

Mình sẽ demo cách setup bằng Docker Compose V2. Cách này rất gọn và dễ quản lý cho các dự án thực chiến.

Bước 1: Thiết lập Proxy

Tạo file docker-compose.yml cho hạ tầng bảo mật:

services:
  docker-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: docker-proxy
    restart: always
    environment:
      - CONTAINERS=1 # Cho phép liệt kê container
      - NETWORKS=0   # Chặn can thiệp mạng
      - IMAGES=0     # Chặn quản lý image
      - VOLUMES=0    # Chặn can thiệp ổ cứng
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - backend-secure

networks:
  backend-secure:
    external: true

Lưu ý mình dùng mode ro (read-only) cho volume socket. Đây là lớp bảo vệ vật lý đầu tiên giúp ngăn chặn các script cố tình ghi đè vào file socket gốc.

Bước 2: Kết nối Traefik qua Proxy

Thay vì trỏ trực tiếp vào file socket, hãy hướng Traefik đi qua “trạm gác” vừa tạo:

services:
  traefik:
    image: traefik:v3.0
    command:
      - "--providers.docker=true"
      - "--providers.docker.endpoint=tcp://docker-proxy:2375"
      - "--providers.docker.exposedByDefault=false"
    networks:
      - backend-secure
    depends_on:
      - docker-proxy

Nhớ đặt cả hai cùng vào một network nội bộ. Điều này đảm bảo không ai từ bên ngoài có thể chọc thẳng vào port 2375 của proxy.

Kinh nghiệm xương máu khi vận hành

Sau vài năm triển khai, đây là những điều mình luôn nhắc nhở team:

  1. Đọc log để bắt bệnh: Nếu ứng dụng báo lỗi 403 Forbidden, hãy check ngay log của proxy. Nó sẽ cho bạn biết chính xác API call nào đang bị chặn để bạn nới lỏng quyền cho đúng.
  2. Ưu tiên đặc quyền tối thiểu: Đừng bao giờ lười biếng mà bật POST=1 cho tất cả. Watchtower cần quyền Image để cập nhật bản mới, nhưng Traefik thì tuyệt đối không nên cấp quyền này.
  3. Cô lập mạng: Tuyệt đối không map port 2375 ra ngoài host (tránh dùng ports: - 2375:2375). Hãy để nó ẩn mình trong network nội bộ.

Đừng đợi đến lúc server bị chiếm quyền mới bắt đầu lo bảo mật. Chỉ mất 5 phút refactor nhưng bạn sẽ ngủ ngon hơn rất nhiều. Chúc anh em triển khai thành công!

Share: