SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-22

Chạy service bằng user không có quyền root

Chạy service bằng root biến một lỗi thành quyền truy cập toàn bộ server. Tạo user riêng cho từng service hoặc dùng systemd DynamicUser để giảm thiệt hại.

Tại sao không chạy mọi thứ dưới quyền root

root có thể làm mọi việc trên máy: đọc mọi file, thay đổi mọi thiết lập và xóa toàn bộ hệ thống. Khi chạy một service dưới quyền root, bạn trao toàn bộ quyền đó cho service. Nếu service có lỗi mà attacker có thể khai thác, attacker không chỉ chiếm được service mà còn chiếm được root, tức là toàn bộ server. Chạy service bằng một user không có quyền đặc biệt sẽ giới hạn thiệt hại. Nếu một service chạy dưới tài khoản bị giới hạn có lỗi, attacker chỉ truy cập được những gì tài khoản đó có quyền truy cập, và phạm vi này gần như không đáng kể.

Đây là nguyên tắc quyền tối thiểu: cấp cho mỗi phần của hệ thống đúng những quyền cần thiết để thực hiện nhiệm vụ, không cấp thêm. Đây là thói quen hiệu quả nhất để giới hạn phạm vi ảnh hưởng khi server bị breach. Trên server hiện đại, việc áp dụng nguyên tắc này gần như không tốn thêm chi phí.

Tài khoản riêng cho từng service

Cách làm kinh điển là tạo một system user riêng cho từng service. Tài khoản đó chỉ sở hữu các file của service tương ứng và không thể đăng nhập. Ví dụ, system account cho một web app có thể được tạo như sau:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvc

Mỗi flag đều có tác dụng. --system tạo một service account thay vì tài khoản đăng nhập của người dùng. --no-create-home bỏ qua việc tạo home directory vì service không cần thư mục này. --shell /usr/sbin/nologin nghĩa là ngay cả khi attacker lấy được tài khoản, họ cũng không thể mở shell bằng tài khoản đó. Tài khoản này chỉ tồn tại để sở hữu một process và các file của process đó.

Sau đó, chỉ cấp cho user này các file cần thiết, không cấp thêm:

sudo chown -R appsvc:appsvc /opt/myapp

Bây giờ service có thể đọc và ghi trong directory của chính nó, nhưng không có quyền thao tác ở nơi khác trên disk. Nếu service bị exploit, các file mà attacker có thể thay đổi chỉ giới hạn ở /opt/myapp. Account vẫn có thể đọc mọi file được phép đọc bởi tất cả user, nhưng không thể sửa phần còn lại của system.

Để systemd chạy service bằng user đó

Sau khi tạo account, hãy cấu hình systemd chạy service bằng account đó. Trong unit file, chỉ cần thêm một dòng:

[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvc

User=appsvc nghĩa là process khởi động với quyền hạn giới hạn của account đó thay vì quyền của root. Đây là cách thông thường và chuẩn để chạy ứng dụng bằng systemd. Bạn nên áp dụng cho mọi service có unit file. Việc hạ quyền sẽ không có tác dụng nếu systemd đang theo dõi sai process. Nếu unit vẫn báo active sau khi daemon đã âm thầm thoát, hãy kiểm tra bạn đã chọn đúng Type= theo cách process khởi động hay chưa.

Hoặc bỏ qua tài khoản hoàn toàn bằng DynamicUser

systemd có thể làm thêm một bước và tạo cho bạn một user tạm thời, chỉ tồn tại trong thời gian service chạy. Đặt DynamicUser=yes và bạn không cần quản lý tài khoản nào:

[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myapp

Khi khởi động, systemd cấp một user ID chưa được sử dụng; khi dừng, systemd thu hồi ID đó. Service cũng nhận một /tmp riêng tư, một chế độ xem chỉ đọc đối với hầu hết filesystem và một state directory có quyền ghi bên dưới /var/lib/myapp do StateDirectory= tạo và bàn giao cho service. SysV init không có cơ chế tương tự. Khi đó, việc hạ quyền được giao cho cách mà start script riêng của từng service xử lý. Khoảng thiếu này là một phần quan trọng trong lý do ngay từ đầu các distribution chuyển sang systemd. Với một service tự chứa chỉ cần state directory riêng, DynamicUser=yes là cách ít tốn công nhất để có isolation mạnh, vì hoàn toàn không có tài khoản tồn tại lâu dài để attacker nhắm tới.

Tự viết unit khá mất công, và phần lớn giá trị nằm ở việc đặt đúng các hardening directive. Generator trong hướng dẫn về service và timer của systemd có thể tự điền các tùy chọn này để unit đúng ngay từ lần đầu.

Cách này kết hợp với các biện pháp khác

Least privilege là một lớp bảo vệ và phối hợp với các lớp khác thay vì thay thế chúng. Firewall mặc định từ chối kiểm soát những gì có thể truy cập service; chạy service bằng user không có đặc quyền kiểm soát những gì service có thể làm nếu bị breach; còn SSH được harden ngăn attacker truy cập vào máy ngay từ đầu. Không biện pháp nào trong số này là đủ nếu đứng riêng lẻ. Kết hợp với nhau, chúng bảo đảm một bug trong một service không biến thành việc toàn bộ server bị compromise. Việc host một dịch vụ bảo vệ secret cho thấy giới hạn của các lớp bảo vệ này: account bị giới hạn sẽ hạn chế những gì process đã bị breach có thể truy cập, nhưng password manager tự host như Vaultwarden vẫn phụ thuộc hoàn toàn vào cách bạn bảo vệ admin token và file backup của nó. User isolation không bảo vệ được hai thành phần này.

Trước khi tiếp tục, hãy rà soát checklist hardening cho toàn bộ máy chủ và tạo một bản được cá nhân hóa để sử dụng:

ToolVPS hardening checklist

FAQ

Tại sao không nên chạy service dưới quyền root?

Vì root có thể làm mọi việc trên máy, nên nếu một service chạy dưới quyền root bị khai thác, attacker sẽ chiếm được toàn bộ server, không chỉ service đó. Chạy service bằng một account giới hạn, không có quyền đặc biệt sẽ giới hạn thiệt hại trong phạm vi account đó có thể truy cập. Chỉ dùng root cho công việc quản trị và chạy mọi service hoạt động lâu dài bằng một user bị hạn chế.

Làm cách nào tạo user không thể đăng nhập?

Chạy sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME. Shell nologin khiến account không thể mở interactive session, ngay cả khi credential của account bị đánh cắp, --system đánh dấu đây là service account và --no-create-home bỏ qua việc tạo home directory không cần thiết. Chỉ cấp quyền sở hữu các file của chính service cho account bằng chown.

systemd DynamicUser là gì?

DynamicUser=yes yêu cầu systemd tạo một user tạm thời cho service. User này chỉ tồn tại trong thời gian service chạy, nên bạn không phải quản lý một account tồn tại lâu dài. Tính năng này cũng cấp cho service một /tmp riêng, một filesystem view hầu như chỉ đọc và một state directory được quản lý. Đây là cách ít tốn công nhất để chạy một service độc lập bằng một identity có quyền thấp và chỉ dùng tạm thời.

Chạy bằng user không phải root có thay thế firewall không?

Không. Hai cơ chế này bảo vệ các thành phần khác nhau. Chạy bằng user không có quyền đặc biệt sẽ giới hạn những gì service có thể làm nếu bị breach, còn firewall giới hạn những nguồn có thể kết nối đến service ngay từ đầu. Hãy dùng cả hai, cùng với SSH được harden, để mỗi lớp xử lý những rủi ro mà các lớp khác không thể xử lý.

Service user nên sở hữu những file nào?

Chỉ những file service thực sự cần, không thêm gì khác. Cấp cho account quyền sở hữu working directory và data của chính service, còn mọi thứ khác để root sở hữu. Một cách bố trí phù hợp là dùng sudo chown -R svc-app:svc-app /opt/svc-app cho application directory, còn cấu hình bên dưới /etc vẫn do root sở hữu và chỉ cho service đọc. Mục tiêu là nếu process bị compromise, các file nó có thể thay đổi chỉ giới hạn trong data của chính service, không lan sang phần còn lại của system.

#bảo mật#least-privilege#systemd#users#hardening#linux