Làm Chủ Linux Capabilities: Kiểm Soát Quyền Tiến Trình Chi Tiết với getcap và setcap thay thế SUID

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

SUID vs. Linux Capabilities: Hai Hướng Tiếp Cận Hoàn Toàn Khác Nhau về Đặc Quyền

Nếu bạn đã quản lý server Linux một thời gian, chắc chắn bạn đã gặp các binary SUID — những file có bit s đặc biệt cho phép tiến trình chạy với quyền của chủ sở hữu file thay vì người gọi. Ví dụ điển hình: ping, passwd, sudo. Chúng hoạt động được, nhưng đi kèm một vấn đề càng suy nghĩ càng khó bỏ qua.

SUID là tất cả hoặc không có gì. Khi ping chạy với quyền root, nó nhận được toàn bộ đặc quyền root — truy cập filesystem, kiểm soát tiến trình, cấu hình mạng, tất cả mọi thứ — dù thực tế nó chỉ cần quyền mở một raw socket. Đó là bề mặt tấn công khổng lồ cho một công cụ chẩn đoán đơn thuần.

Capabilities ra đời để giải quyết đúng vấn đề này. Thay vì cấp toàn bộ đặc quyền root, bạn chia nhỏ những đặc quyền đó thành các đơn vị riêng biệt và chỉ gán những gì tiến trình thực sự cần. ping cần CAP_NET_RAW. Không gì thêm. Không cần SUID.

Các bản phân phối Ubuntu và Debian hiện đại đã ship ping theo cách này. Chạy lệnh này và bạn sẽ thấy:

getcap /usr/bin/ping
# Kết quả: /usr/bin/ping cap_net_raw=ep

Không có bit SUID. Chỉ duy nhất một capability nó cần. Đó là toàn bộ ý tưởng.

Ưu và Nhược Điểm: Capabilities vs. SUID

SUID — kẻ quen thuộc nhưng nhiều rủi ro

Ưu điểm:

  • Cực kỳ đơn giản để thiết lập: chmod u+s /path/to/binary
  • Được hỗ trợ trên mọi phiên bản Linux
  • Không cần kernel module hay feature flag
  • Hoạt động trong suốt — binary không cần biết về nó

Nhược điểm:

  • Binary nhận toàn bộ đặc quyền UID 0 — nhiều hơn nhiều so với nhu cầu thực tế
  • Bất kỳ lỗ hổng nào trong binary đó đều dẫn đến leo thang đặc quyền root hoàn toàn
  • Khó kiểm tra: find / -perm -4000 cho ra danh sách nhưng không có ngữ cảnh về lý do
  • Áp dụng cho mọi người dùng có thể thực thi file — không có kiểm soát theo từng người dùng

Linux Capabilities — cách tiếp cận phẫu thuật chính xác

Ưu điểm:

  • Nguyên tắc đặc quyền tối thiểu: chỉ cấp những gì cần thiết
  • Nếu binary bị khai thác, kẻ tấn công chỉ nhận được các capability cụ thể đó — không phải root toàn diện
  • Có thể kiểm tra với getcap -r / — hiển thị chính xác những capability nào tồn tại và ở đâu
  • Chi tiết đến từng binary, hỗ trợ quản lý capability ở cấp thread trong code
  • Tương thích với các framework bảo mật như AppArmor và SELinux

Nhược điểm:

  • Không phải phần mềm nào cũng hiểu hoặc yêu cầu capabilities — các công cụ cũ có thể vẫn cần SUID
  • Các cờ capability (e, p, i) có đường cong học tập nhất định
  • Capabilities trên file được lưu trong extended attributes — một số filesystem hoặc công cụ backup có thể xóa chúng
  • Các ngôn ngữ scripting (Python, Perl) không tự kế thừa file capabilities — cần binary wrapper

Khuyến Nghị: Khi Nào Dùng Gì

Đây là cách tôi suy nghĩ về vấn đề này trên các hệ thống production:

  • Dùng capabilities cho các binary đã biên dịch cần một đặc quyền cụ thể: công cụ mạng (tcpdump, ping), daemon đồng bộ thời gian, agent bắt gói tin
  • Giữ SUID cho các công cụ hệ thống mà mô hình đặc quyền đầy đủ là có chủ đích và đã được kiểm tra kỹ: sudo, su, passwd — những công cụ này được thiết kế xoay quanh quyền root
  • Tránh hoàn toàn SUID cho bất cứ thứ gì bạn tự biên dịch hoặc công cụ bên thứ ba bạn đang triển khai — hãy dùng capabilities hoặc chạy với người dùng chuyên dụng ít đặc quyền cùng capabilities tối thiểu cần thiết
  • Chạy service với người dùng chuyên dụng kết hợp capabilities — đây là mô hình sạch nhất cho các daemon

Ví dụ thực tế từ server Ubuntu 22.04 của tôi: tôi có một công cụ phân tích gói tin tùy chỉnh đang chạy với quyền root qua SUID wrapper. Chuyển sang người dùng hệ thống netmon chuyên dụng với chỉ CAP_NET_RAW và CAP_NET_ADMIN trên binary chỉ mất khoảng mười phút. Hành vi giống hệt. Bề mặt tấn công thu hẹp đáng kể. Đó là loại chiến thắng nhanh khiến việc hardening bớt cảm giác nhàm chán.

Hướng Dẫn Triển Khai

Cài đặt công cụ

Trên Debian/Ubuntu:

sudo apt install libcap2-bin

Trên RHEL/CentOS/AlmaLinux:

sudo dnf install libcap

Lệnh này cài cho bạn getcap, setcap, và capsh.

Hiểu các cờ capability

Khi bạn thấy kết quả như cap_net_raw=ep, các chữ cái sau = là các tập capability:

  • e — Effective (Hiệu lực): capability đang hoạt động ngay bây giờ
  • p — Permitted (Được phép): capability có thể được kích hoạt
  • i — Inheritable (Kế thừa): được truyền cho tiến trình con

Với hầu hết trường hợp, =ep là thứ bạn muốn — capability vừa được phép vừa có hiệu lực khi binary chạy.

Kiểm tra capabilities hiện có

# Kiểm tra một file cụ thể
getcap /usr/bin/ping

# Kiểm tra toàn bộ filesystem (chạy với quyền root)
getcap -r / 2>/dev/null

Lệnh quét đệ quy đó là thứ tôi chạy hàng quý trên server production. Đây là bước kiểm tra nhanh — nếu có gì đó xuất hiện mà bạn không thiết lập, đó là điều đáng điều tra.

Gán capabilities cho binary

Giả sử bạn có công cụ tùy chỉnh /usr/local/bin/netmon cần quyền truy cập raw socket:

# Cấp CAP_NET_RAW (quyền truy cập raw socket)
sudo setcap cap_net_raw=ep /usr/local/bin/netmon

# Xác minh
getcap /usr/local/bin/netmon
# Kết quả: /usr/local/bin/netmon cap_net_raw=ep

Cho công cụ bắt gói tin cũng cần thiết lập chế độ interface:

sudo setcap cap_net_raw,cap_net_admin=ep /usr/local/bin/netmon

Xóa capabilities

# Xóa tất cả capabilities khỏi binary
sudo setcap -r /usr/local/bin/netmon

# Xác minh đã sạch
getcap /usr/local/bin/netmon
# (không có kết quả = không có capabilities)

Thay thế SUID binary hiện có bằng capabilities

Ví dụ cụ thể: gỡ SUID khỏi tcpdump và chuyển sang dùng capabilities.

# Kiểm tra trạng thái hiện tại
ls -la /usr/bin/tcpdump
# -rwxr-sr-x hoặc -rwsr-xr-x tùy distro

# Xóa bit SUID
sudo chmod u-s /usr/bin/tcpdump

# Chỉ cấp những gì nó cần
sudo setcap cap_net_raw,cap_net_admin=ep /usr/bin/tcpdump

# Kiểm tra với người dùng thường
tcpdump -i eth0 -c 5

Capabilities cho service chạy với người dùng không phải root

Mô hình tôi hay dùng nhất: tạo người dùng chuyên dụng, gán capabilities cho binary, xong.

# Tạo người dùng hệ thống (không có login shell, không có thư mục home)
sudo useradd -r -s /usr/sbin/nologin netmon

# Gán capabilities cho binary
sudo setcap cap_net_raw=ep /usr/local/bin/netmon

# Chạy service với người dùng đó (trong systemd unit)
# [Service]
# User=netmon
# ExecStart=/usr/local/bin/netmon

# Xác minh tiến trình có đúng capabilities khi chạy
cat /proc/$(pgrep netmon)/status | grep Cap

Kết quả từ /proc/PID/status hiển thị bitmask capability. Để giải mã chúng:

capsh --decode=0000000000002000
# 0x0000000000002000=cap_net_raw

Tham chiếu các capability thông dụng

  • cap_net_raw — Raw socket và packet socket (ping, tcpdump)
  • cap_net_admin — Cấu hình network interface, routing, iptables
  • cap_net_bind_service — Bind vào cổng dưới 1024 mà không cần root
  • cap_sys_time — Đặt đồng hồ hệ thống (ntpd, chrony)
  • cap_dac_override — Bỏ qua kiểm tra quyền đọc/ghi file
  • cap_setuid / cap_setgid — Thay đổi UID/GID của tiến trình
  • cap_sys_ptrace — Trace tiến trình tùy ý (debugger, strace)

Danh sách đầy đủ: man 7 capabilities

Lưu ý quan trọng: capabilities và script

Gán capabilities cho script Python hay Bash sẽ không hoạt động — kernel chỉ áp dụng file capabilities cho binary ELF, không phải script thông dịch. Có hai lựa chọn: viết một C wrapper nhỏ, hoặc dùng ambient capabilities qua directive AmbientCapabilities= của systemd trong unit file:

[Service]
User=netmon
AmbientCapabilities=CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_RAW
ExecStart=/usr/bin/python3 /opt/netmon/monitor.py

Đây là cách tiếp cận sạch nhất cho service Python cần một capability duy nhất — không SUID, không chạy với quyền root.

Script kiểm tra nhanh

Thêm script này vào bộ công cụ của bạn — nó hiển thị cả SUID binary lẫn file có capabilities cạnh nhau:

#!/bin/bash
echo "=== Các Binary SUID ==="
find / -perm -4000 -type f 2>/dev/null | sort

echo ""
echo "=== Các File có Capabilities ==="
getcap -r / 2>/dev/null | sort

Chạy trước và sau mọi thay đổi hệ thống để có bức tranh rõ ràng về bề mặt đặc quyền của bạn. Trên hệ thống được cấu hình tốt, danh sách SUID nên ngắn và dễ đoán — và mọi entry trong danh sách capabilities phải có lý do được ghi chép đầy đủ.

Capabilities không phải phép màu. Bạn vẫn cần suy nghĩ kỹ về những gì mỗi binary thực sự cần — làm sai và bạn chỉ thay thế một binary quá nhiều đặc quyền bằng một binary khác. Nhưng khi dùng đúng cách, chúng là sự nâng cấp thực sự so với SUID, và các buổi kiểm tra hardening sẽ bớt cảm giác như đang khai quật khảo cổ.

Share: