Cách cấu hình sudo-rs trên Ubuntu 26.04
Ubuntu 26.04 chuyển sang sudo-rs mặc định. Các quy tắc sudoers chứa wildcard sẽ không còn khớp đối số lệnh. Xem ngay cách sửa file cấu hình để tránh lỗi quyền root.
Những thay đổi của sudo-rs trên Ubuntu
Ubuntu 26.04 LTS phát hành sudo-rs làm sudo mặc định, vì vậy lệnh sudo trên một server mới chạy bản cài đặt lại bằng Rust thay vì chương trình C gốc. Hầu hết các file sudoers vẫn hoạt động chính xác như trước. Quy tắc bị lỗi là quy tắc có chứa wildcard bên trong các đối số của lệnh, vì sudo-rs không khớp các mẫu glob với văn bản đối số.
Ubuntu 25.10 đã thực hiện chuyển đổi này trước và 26.04 LTS tiếp tục duy trì. Ubuntu 24.04 LTS không bị ảnh hưởng vì nó vẫn chọn sudo gốc trừ khi bạn tự cài đặt sudo-rs. Thời điểm cần lưu ý là lúc bạn nâng cấp từ Ubuntu 24.04 lên 26.04, hoặc lúc bạn xây dựng một máy chủ mới trên bản phát hành mới hơn. Nếu bạn cũng chạy các bản phát hành trung gian, sự khác biệt giữa các bản LTS và bản Ubuntu trung gian trên server sẽ giải thích máy nào gặp phải thay đổi này trước.
Kiểm tra phiên bản sudo thực tế đang chạy trên server
Đừng suy luận dựa trên số hiệu bản phát hành. Hãy hỏi trực tiếp máy chủ.
sudo --version
update-alternatives --config sudo
dpkg -l 'sudo*'Hãy tin tưởng sudo --version trên máy của bạn hơn bất kỳ bảng phiên bản nào trên internet, bao gồm cả trang này. update-alternatives --config sudo là nửa còn lại của câu trả lời: nó liệt kê mọi nhà cung cấp /usr/bin/sudo đã cài đặt và đánh dấu phiên bản đang được chọn. Một gói phần mềm được cài đặt không có nghĩa là nó đang được chọn, vì vậy hãy đọc mục được chọn, đừng đọc danh sách gói.
Cả hai bản cài đặt đều được đóng gói trong quá trình chuyển đổi. Bản viết bằng Rust là sudo-rs, ở phiên bản 0.2.13 trong 26.04 tính đến tháng 8 năm 2026. Bản gốc, do Todd C. Miller duy trì, vẫn là gói sudo; điểm thay đổi là các chương trình của nó mang hậu tố .ws để cả hai có thể được cài đặt cùng lúc: /usr/bin/sudo.ws và /usr/bin/visudo.ws, song song với cvtsudoers.ws và sudoreplay.ws. Đã xác minh với kho lưu trữ 26.04 vào tháng 9 năm 2026: dpkg -L sudo liệt kê các tệp nhị phân có hậu tố, và sudo-rs cung cấp /usr/bin/sudo-rs bên cạnh chúng.
Tại sao Ubuntu chuyển sang dùng sudo-rs
sudo chạy với quyền setuid root. Bất kỳ người dùng nào trên máy cũng có thể khởi chạy nó, và nó bắt đầu với toàn bộ đặc quyền, vì vậy một lỗi bộ nhớ bên trong nó sẽ trở thành một lỗ hổng leo thang đặc quyền root cục bộ. CVE-2021-3156 chính là trường hợp đó: một lỗi heap buffer overflow mà bất kỳ người dùng cục bộ nào cũng có thể khai thác, và nó đã tồn tại trong mã nguồn được phát hành suốt khoảng mười năm. Rust ngăn chặn loại lỗi này ngay tại thời điểm biên dịch, đó cũng là lý do chính cho việc viết lại công cụ này.
Lý do thứ hai là phạm vi tính năng, và đây là điều ảnh hưởng trực tiếp đến cấu hình của bạn. sudo nguyên bản đã tích lũy một bộ tính năng lớn trong suốt ba thập kỷ, và mỗi tính năng đều đồng nghĩa với việc có thêm mã nguồn chạy dưới quyền root. sudo-rs được thiết kế để chỉ triển khai một tập hợp con các tính năng. Bất kỳ tính năng nào mà tác giả đánh giá là quá chuyên biệt hoặc có hại đều bị loại bỏ, vì vậy một cấu trúc sudoers đã hoạt động nhiều năm có thể đơn giản là không còn tồn tại. Quy tắc wildcard của bạn là một trong số đó.
Tính an toàn bộ nhớ loại bỏ một lớp lỗi. Nó không làm cho chương trình hoàn toàn không có lỗi, và sudo-rs cũng đã phát hành các bản vá bảo mật của riêng mình kể từ khi trở thành mặc định. Hãy cập nhật nó như bất kỳ phần mềm nào khác.
Các quy tắc sudoers nào vẫn hoạt động
File cấu hình vẫn là file đó. sudo-rs đọc /etc/sudoers và các file drop-in trong /etc/sudoers.d/, đồng thời hỗ trợ các cấu hình thông thường mà người quản trị máy chủ hay dùng:
deploy ALL=(ALL:ALL) ALL, và các dạng nhóm như%sudo ALL=(ALL:ALL) ALL- các thẻ
NOPASSWD:vàPASSWD: User_Alias,Runas_Alias,Host_AliasvàCmnd_Alias- một lệnh với danh sách đối số chính xác, ví dụ
/usr/bin/systemctl restart app-api - một lệnh theo sau bởi
"", cho phép chạy lệnh đó nhưng không kèm bất kỳ đối số nào - một lệnh theo sau bởi
*ở cuối, cho phép thêm bất kỳ đối số nào phía sau - một đường dẫn thư mục kết thúc bằng
/, cho phép chạy bất kỳ lệnh nào trong thư mục đó !để loại bỏ một lệnh khỏi danh sách- một tập hợp con hữu ích của
Defaults, bao gồmsecure_path,env_keep,env_check,timestamp_timeout,passwd_tries,editor,umask,targetpw,rootpwvàuse_pty
Có hai thiết lập mặc định hoạt động khác biệt và thường gây nhầm lẫn. env_reset không thể tắt trong sudo-rs: nó luôn luôn bật. use_pty được bật theo mặc định, vì vậy lệnh sẽ chạy trong một pseudo-terminal riêng của nó.
Tại sao quy tắc sudoers sử dụng wildcard của bạn không còn khớp
Wildcard vẫn được cho phép ở một vị trí: tên tệp của lệnh. Một quy tắc %ops ALL = /sbin/fsck* vẫn cho phép sudo fsck và sudo fsck_exfat, vì * là một phần của đường dẫn đang được so khớp với hệ thống tệp.
Bên trong danh sách đối số, sudo-rs chỉ chấp nhận hai dạng đặc biệt và không dạng nào là pattern. "" nghĩa là không có đối số. * ở cuối nghĩa là bất kỳ đối số nào theo sau. Mọi đối số khác đều được so sánh như văn bản thuần (literal text). Vì vậy %ops ALL = /sbin/service ntp * là hợp lệ, vì ntp là văn bản thuần và * nằm ở cuối. Tuy nhiên, một quy tắc như thế này sẽ không cấp quyền như bạn dự định:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart app-*app-* là một pattern nằm ở giữa đối số. sudo-rs không mở rộng nó, vì vậy quy tắc này không bao gồm systemctl restart app-api và sudo sẽ từ chối lệnh đó. Hai lệnh sau đây sẽ cho bạn biết sự thật về bất kỳ quy tắc nào trên máy chủ của bạn: sudo -l -U deploy, chạy dưới quyền root, sẽ in ra những gì tài khoản đó thực sự có thể chạy, và sudo visudo -c cho bạn biết tệp có được phân tích cú pháp thành công hay không. Hãy chạy chúng trước khi bạn bắt đầu chỉnh sửa một cách tùy tiện.
Quy tắc wildcard luôn là một lỗ hổng
Trong sudo nguyên bản, các đối số bạn nhập được nối thành một chuỗi duy nhất và so khớp với chuỗi đối số của quy tắc bằng glob. Glob khớp với cả khoảng trắng. Đây là phần mà hầu hết mọi người đều bỏ qua.
Tài liệu của sudo-rs đưa ra minh họa rõ ràng nhất. Một quy tắc /bin/rm *.txt cũng cho phép sudo rm -rf /home .txt, bởi vì * nuốt trọn -rf /home và chuỗi đã nối vẫn kết thúc bằng .txt. Quy tắc này được hiểu là "chỉ các tệp văn bản". Thực tế nó có nghĩa là "bất kỳ đối số nào, miễn là dòng kết thúc bằng .txt".
Điều tương tự áp dụng cho ví dụ systemctl. Vì các đối số được so sánh như một chuỗi nối, một mẫu ở cuối cũng khớp với bất cứ thứ gì bạn thêm vào sau nó, vì vậy restart app-* bao hàm restart app-api cộng với bất kỳ đối số nào khác mà người gọi thêm vào. Một mẫu bên trong một đối số làm lộ các đối số xung quanh nó, và đối số chính là nơi chứa quyền năng của một lệnh. sudo-rs từ chối cấu trúc này thay vì cố gắng làm cho nó an toàn, bởi vì không có dạng tổng quát nào an toàn cho nó cả.
Thay thế wildcard bằng danh sách lệnh cụ thể
Hầu hết các quy tắc wildcard tồn tại vì người dùng không muốn gõ bốn dòng lệnh. Hãy gõ đủ bốn dòng đó.
Cmnd_Alias APP_RESTART = /usr/bin/systemctl restart app-api, /usr/bin/systemctl restart app-worker
Cmnd_Alias APP_STATUS = /usr/bin/systemctl status app-api, /usr/bin/systemctl status app-worker
deploy ALL=(root) NOPASSWD: APP_RESTART, APP_STATUSXác định đúng đường dẫn. Một quy tắc chỉ định /bin/systemctl trên hệ thống mà file binary thực tế nằm tại /usr/bin/systemctl sẽ không bao giờ khớp, và lỗi này trông giống hệt lỗi phân quyền. Hãy xác nhận bằng command -v systemctl và dán kết quả mà lệnh đó in ra.
Đặt quy tắc vào file drop-in riêng thay vì sửa trực tiếp /etc/sudoers, để việc nâng cấp gói không làm mất cấu hình của bạn:
sudo visudo -f /etc/sudoers.d/90-deploy
sudo visudo -c
sudo -l -U deployĐặt tên file không chứa dấu chấm và không có dấu ngã ở cuối. Sudo mặc định bỏ qua các file trong sudoers.d nếu tên có chứa dấu chấm, vì vậy 90-deploy.conf là một lỗi phổ biến khiến quy tắc không có tác dụng, và việc tuân thủ quy ước đặt tên này không gây hại gì cả.
Sử dụng wrapper do root sở hữu khi danh sách trở nên quá dài
Khi tập hợp các lệnh được phép quá lớn để liệt kê, hãy chuyển quyết định ra khỏi sudoers và đưa vào một chương trình nhỏ do root sở hữu.
sudo tee /usr/local/sbin/app-restart >/dev/null <<'EOF'
#!/bin/sh
set -eu
case "${1:-}" in
app-api|app-worker) ;;
*) echo "app-restart: not allowed: ${1:-}" >&2; exit 1 ;;
esac
exec /usr/bin/systemctl restart "$1"
EOF
sudo chown root:root /usr/local/sbin/app-restart
sudo chmod 0755 /usr/local/sbin/app-restart
ls -l /usr/local/sbin/app-restartPhía sudoers lúc này chỉ định một lệnh duy nhất:
deploy ALL=(root) NOPASSWD: /usr/local/sbin/app-restart *Ký tự * ở cuối là chấp nhận được trong trường hợp này vì script, chứ không phải sudo, mới là bên quyết định những gì được phép. Điều này chỉ đúng khi script do root sở hữu và không ai khác có quyền ghi. Nếu deploy có thể ghi vào file, deploy có thể thay thế nội dung của nó và chạy bất kỳ thứ gì dưới quyền root, điều này còn tệ hơn cả quy tắc wildcard mà bạn đã loại bỏ. Kiểm tra mode bằng ls -l, và nếu kết quả không rõ ràng với bạn, việc đọc chuỗi quyền drwxr-xr-x chỉ mất năm phút để học. Quy tắc tương tự áp dụng cho thư mục: /usr/local/sbin cũng không được phép để tài khoản đó có quyền ghi, vì một thư mục có quyền ghi đồng nghĩa với việc file có thể bị thay thế hoàn toàn.
Cấp cho tác vụ một tài khoản riêng thay vì dùng quy tắc sudo
Câu hỏi hợp lý hơn thường là tại sao lệnh đó lại cần quyền root. Một service chạy dưới tài khoản người dùng riêng có thể được quản lý bởi chính người dùng đó mà không cần đụng đến file sudoers. Đối với các system unit, systemd đã ủy quyền quyết định này cho polkit, vì vậy một quy tắc có thể chỉ định một unit và một người vận hành cụ thể:
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "app-api.service" &&
subject.user == "deploy") {
return polkit.Result.YES;
}
});Lưu file này dưới dạng /etc/polkit-1/rules.d/50-app-api.rules và deploy có thể chạy systemctl restart app-api mà không cần sudo. Hãy kiểm tra từ đúng ngữ cảnh sẽ sử dụng lệnh đó, vì một quy tắc hoạt động trong phiên SSH của bạn cần được xác nhận từ cron trước khi bạn tin tưởng vào nó. Dù bằng cách nào, tài khoản thực hiện công việc chỉ nên tồn tại cho công việc đó, đây cũng là lập luận đằng sau tài khoản người dùng có đặc quyền tối thiểu trên VPS.
Những tính năng khác mà sudo-rs không hỗ trợ
sudo -E chưa được triển khai. Hãy đặt tên các biến bạn cần bằng Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY" thay thế, và nhớ rằng env_reset luôn được bật, vì vậy bất kỳ thứ gì không được giữ lại đều sẽ bị xóa.
Lưu trữ sudoers tập trung trong LDAP đã bị loại bỏ. sudoers.ldap và cvtsudoers không được triển khai, và gói sudo-ldap đã bị xóa trong phiên bản 26.04. Xác thực LDAP thông qua PAM hoặc SSSD vẫn hoạt động bình thường. Chỉ có phần chính sách lưu trong thư mục là nằm ngoài phạm vi hỗ trợ.
INTERCEPT, tính năng từng cố gắng ngăn chặn shell escape từ một lệnh được cho phép, không được triển khai. Dù sao thì nó cũng chưa bao giờ ngăn cản được một người dùng quyết tâm. Nếu một quy tắc cho phép ai đó chạy trình soạn thảo hoặc trình thông dịch với quyền root, họ đã có quyền root, và không tùy chọn sudo nào thay đổi được điều đó.
Ghi lại phiên làm việc (session recording) không được triển khai, vì vậy không có I/O log và không có sudoreplay. Việc ghi log chỉ chuyển đến syslog, và không có tùy chọn logfile để chuyển hướng nó đến nơi khác, do đó các thông báo của sudo sẽ được gửi đến bất cứ nơi nào mà hệ thống của bạn đang gửi syslog.
Bạn có nên chuyển ngược lại sudo.ws không?
Bạn có thể làm vậy, và trong chu kỳ 26.04, phiên bản gốc vẫn được đóng gói chính xác vì lý do này.
sudo apt install sudo
update-alternatives --config sudo
sudo update-alternatives --set sudo /usr/bin/sudo.wsHãy sao chép chính xác các đường dẫn từ đầu ra của --config thay vì sao chép từ trang này, vì đó là danh sách mà hệ thống của bạn chấp nhận. Việc quay lại sudo-rs sau này đồng nghĩa với việc thiết lập alternative trỏ đến đường dẫn binary sudo-rs từ cùng danh sách đó.
Hãy giữ một phiên SSH thứ hai luôn mở, đã đăng nhập và ở trạng thái chờ, trước khi bạn thực hiện bất kỳ thay đổi nào ảnh hưởng đến sudo. Một file sudoers không thể phân tích cú pháp, hoặc một alternative trỏ đến một binary không tồn tại, có thể khiến bạn mất quyền truy cập root trên máy chủ từ xa. Thói quen này cần được áp dụng cùng với mọi việc khác bạn làm trong mười phút đầu tiên trên một VPS mới.
Hãy coi việc chuyển đổi ngược lại là một thời hạn thay vì một giải pháp khắc phục. Nó giúp bạn có thêm một tuần để viết lại các quy tắc một cách đúng đắn, và việc viết lại này rất đáng giá, bởi vì mỗi quy tắc wildcard mà bạn xóa bỏ trước đó đã cấp quyền nhiều hơn mức mà người tạo ra nó từng nghĩ.
FAQ
Tại sao quy tắc wildcard trong sudoers của tôi không còn hoạt động trên Ubuntu 26.04?
Vì Ubuntu 26.04 LTS chọn sudo-rs làm sudo mặc định, và sudo-rs không khớp các mẫu wildcard bên trong đối số của lệnh. Nó cho phép dùng wildcard trong tên file của lệnh, "" để chỉ không có đối số, và một * duy nhất làm đối số cuối cùng. Một quy tắc như /usr/bin/systemctl restart app-* đặt mẫu ở giữa một đối số, nên nó không cấp quyền gì cả và lệnh bị từ chối. Hãy chạy sudo -l -U deploy với quyền root để xem tài khoản thực sự có quyền gì, sau đó thay thế quy tắc đó bằng các lệnh chính xác hoặc bằng một wrapper script thuộc sở hữu của root.
Làm thế nào để chuyển về sudo gốc trên Ubuntu 26.04?
Bản gốc nằm trong gói sudo, với các file binary mang hậu tố .ws. Hãy cài đặt nó bằng sudo apt install sudo, sau đó trỏ alternative về nó bằng sudo update-alternatives --set sudo /usr/bin/sudo.ws. Hãy chạy update-alternatives --config sudo trước để đọc các đường dẫn chính xác mà hệ thống của bạn cung cấp, và giữ một phiên SSH thứ hai mở trong khi bạn thực hiện thay đổi. Việc này không mang lại sudo-ldap, vốn đã bị loại bỏ khỏi 26.04 bất kể bạn chọn cài đặt nào.
sudo-rs có đọc cùng file /etc/sudoers không?
Có. sudo-rs đọc /etc/sudoers và các file drop-in trong /etc/sudoers.d/, với cùng cú pháp cho người dùng, nhóm, alias, chỉ định run-as và thẻ NOPASSWD. Nó triển khai một tập con của ngôn ngữ sudoers, vì vậy các khác biệt xuất hiện dưới dạng các cấu trúc bị thiếu thay vì các cấu trúc hoạt động khác đi. Hãy chỉnh sửa bằng sudo visudo, sau đó xác minh bằng sudo visudo -c trước khi bạn đóng phiên làm việc.
Cái gì thay thế sudo -E trong sudo-rs?
sudo -E không được triển khai, và nó vốn đã bị hạn chế trong sudo gốc, vì việc cung cấp cho một tiến trình root một môi trường mà người gọi có thể kiểm soát là cách phổ biến để thay đổi hành vi của tiến trình đó. Thay vào đó, hãy liệt kê các biến bạn thực sự cần trong sudoers, với một dòng như Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY". env_reset luôn được bật trong sudo-rs và không thể tắt, vì vậy mọi biến bạn không giữ lại đều sẽ bị xóa.