Bối cảnh & Lý do: Cuộc gọi lúc 2 giờ sáng
Tiếng rung điện thoại trên kệ đầu giường lúc 2 giờ sáng là âm thanh mà tôi dần cảm thấy kinh hãi. Prometheus liên tục gửi cảnh báo về việc CPU tăng vọt lên 98% trên một máy chủ ứng dụng phụ. Chỉ vài giờ trước đó, tôi đã phải chặn thủ công một đợt tấn công brute-force SSH với 5.000 yêu cầu, nên tâm lý lúc đó vốn đã rất căng thẳng.
Lần này, mối đe dọa đến từ bên trong. Một kẻ tấn công đã khai thác lỗ hổng thực thi mã từ xa (RCE) trong một ứng dụng PHP cũ. Vì Docker daemon đang chạy dưới quyền root, cuộc tấn công đã kích hoạt lỗi thoát khỏi container (container escape), cấp cho kẻ tấn công quyền truy cập shell trên hệ thống máy chủ với toàn quyền quản trị.
Sự cố đó đã để lại cho tôi một bài học đắt giá. Hầu hết các quản trị viên đều cài đặt Docker, thêm tên người dùng vào nhóm docker và coi như xong việc. Nhưng vấn đề nằm ở đây: Docker daemon (dockerd) thường chạy dưới quyền root tối cao. Nếu hacker thoát ra khỏi container, họ không chỉ kiểm soát ứng dụng mà còn làm chủ toàn bộ máy chủ. Rootless Docker giải quyết triệt để lỗi thiết kế cơ bản này.
Chính xác thì Rootless Docker là gì?
Chế độ Rootless cho phép bạn chạy cả Docker daemon và các container dưới quyền một người dùng tiêu chuẩn, không có đặc quyền. Nó tạo ra một bức tường bảo vệ xung quanh daemon. Ngay cả khi container bị xâm nhập và xảy ra sự cố thoát container, kẻ tấn công vẫn bị kẹt trong phạm vi quyền hạn của một người dùng cấp thấp. Họ không thể chạm vào các tệp hệ thống hoặc chuyển sang tấn công các dịch vụ khác. Điều kỳ diệu này được thực hiện thông qua user_namespaces, giúp ánh xạ một dải UID nội bộ sang một dải không có đặc quyền an toàn trên máy chủ.
Cài đặt: Chuẩn bị môi trường
Việc thiết lập yêu cầu một môi trường sạch. Bạn không thể dễ dàng chạy chế độ rootless nếu dịch vụ Docker tiêu chuẩn đang chiếm dụng các socket của hệ thống. Trong quá trình khôi phục khẩn cấp đêm đó, tôi đã phải đảm bảo máy chủ được chuẩn bị các công cụ ánh xạ (mapping) cần thiết để cách ly namespace.
Điều kiện tiên quyết
Người dùng Ubuntu và Debian cần newuidmap và newgidmap. Không có những công cụ này, quá trình thiết lập rootless sẽ không thể ánh xạ nhiều ID người dùng vào namespace và tiến trình sẽ thất bại ngay lập tức.
# Cài đặt các công cụ ánh xạ và dbus-user-session
sudo apt-get update
sudo apt-get install -y uidmap dbus-user-session
Người dùng của bạn cần một dải UID riêng. Hãy kiểm tra /etc/subuid và /etc/subgid để đảm bảo tài khoản của bạn đã được định nghĩa một dải ID. Đối với người dùng deploy của mình, tôi đã cấp một dải gồm 65.536 ID để đảm bảo đủ dung lượng cho các hệ thống đa container phức tạp.
# Kiểm tra dải subuid hiện tại
grep $(whoami) /etc/subuid
# Nếu trống, hãy thêm thủ công (thay 'user' bằng tên người dùng của bạn)
# sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami)
Script cài đặt
Docker cung cấp một script shell để xử lý các công việc nặng nhọc. Hãy chạy script này với tư cách người dùng cụ thể sẽ quản lý các container—tuyệt đối không sử dụng sudo ở đây.
# Chạy script cài đặt rootless dưới quyền người dùng bình thường
curl -fsSL https://get.docker.com/rootless | sh
Script sẽ tải các tệp thực thi về ~/bin và tạo các tệp dịch vụ systemd trong thư mục home tại ~/.config/systemd/user/.
Cấu hình: Duy trì lâu dài
Đừng bỏ qua các biến môi trường mà script xuất ra. Nếu bạn đóng terminal ngay bây giờ, lệnh docker ps sẽ báo lỗi ‘command not found’ hoặc lỗi kết nối vì nó không biết socket rootless nằm ở đâu.
Biến môi trường
Thêm các dòng sau vào tệp .bashrc hoặc .zshrc của bạn. Tôi đã từng quên bước này trên các máy chủ từ xa, dẫn đến mười phút khắc phục sự cố bực bội cho một daemon ‘mất tích’ trong khi thực tế nó vẫn đang chạy bình thường.
export PATH=$HOME/bin:$PATH
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
Áp dụng các thay đổi ngay lập tức:
source ~/.bashrc
Kích hoạt tính năng duy trì (Linger)
Thông thường, một dịch vụ ở cấp người dùng sẽ dừng ngay khi bạn đăng xuất. Đó là điều không thể chấp nhận được đối với môi trường production. Bạn cần các container của mình duy trì hoạt động sau khi ngắt kết nối. Hãy sử dụng loginctl để giữ cho phiên làm việc của người dùng tiếp tục chạy ngầm (lingering).
# Cho phép dịch vụ người dùng chạy mà không cần phiên SSH hoạt động
sudo loginctl enable-linger $(whoami)
Quản lý dịch vụ
Việc quản lý Docker bây giờ yêu cầu cờ --user. Đây là một thay đổi nhỏ trong tư duy. Thay vì dùng sudo systemctl, giờ đây bạn là chủ sở hữu hoàn toàn phạm vi dịch vụ của riêng mình.
# Khởi động rootless docker daemon
systemctl --user start docker
# Cho phép tự động khởi động cùng hệ thống
systemctl --user enable docker
Xác minh & Giám sát
Xác minh là cách duy nhất để chắc chắn rằng bạn không còn chạy dưới quyền root. Không có gì tệ hơn một cảm giác an toàn giả tạo.
Kiểm tra Rootless Daemon
Chạy lệnh docker info. Kiểm tra kỹ phần ‘Security Options’.
docker info | grep -i rootless
Bạn sẽ thấy rootless: true. Một bài kiểm tra nhanh khác là thử liên kết với cổng 80. Rootless Docker không thể sử dụng các cổng dưới 1024 nếu không có sự hỗ trợ thêm. Đây là một tính năng, không phải lỗi; nó ngăn chặn người dùng không có đặc quyền chiếm đoạt lưu lượng web tiêu chuẩn.
# Lệnh này BẮT BUỘC phải thất bại nếu rootless được cấu hình đúng
docker run -p 80:80 nginx
Giám sát và Nhật ký
Hãy quên /var/log/docker.log đi. Nhật ký của bạn giờ đây được xử lý bởi journald ở cấp độ người dùng. Khi có vấn đề xảy ra, đây là nơi đầu tiên bạn cần kiểm tra:
# Nhật ký thời gian thực cho rootless daemon
journalctl --user -u docker.service -f
Những đánh đổi
Máy chủ của tôi đã an sau hơn đáng kể, nhưng cũng có một số lưu ý. Chế độ Rootless sử dụng slirp4netns cho mạng. Nó hoạt động tốt, nhưng hiệu suất truyền tải mạng có thể giảm khoảng 10-15% so với cầu nối (bridge) tiêu chuẩn. Ngoài ra, việc mount các ổ đĩa NFS sẽ trở nên phức tạp hơn vì việc ánh xạ UID không phải lúc nào cũng tương thích tốt với các hệ thống tệp qua mạng.
Có đáng không? Chắc chắn là có. Kể từ khi chuyển các ứng dụng công khai trên internet sang chế độ rootless, tôi đã ngủ ngon hơn. Nếu kẻ tấn công xâm nhập được, chúng sẽ bị kẹt trong một sandbox có đặc quyền thấp và máy chủ của tôi vẫn an toàn. Bảo mật không phải là làm cho hệ thống không thể bị hack, mà là đảm bảo rằng khi có sự cố xảy ra, thiệt hại sẽ được khoanh vùng một cách triệt để.

