Thắt chặt bảo mật Linux Sudoers: Đừng để quyền ‘Root cho mọi người’ hủy hoại môi trường Production

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Cơn ác mộng lúc 2 giờ sáng: Tại sao thiết lập Sudo mặc định là một rủi ro

Đó là lúc 2:14 sáng thứ Ba khi cảnh báo PagerDuty vang lên. Một trong những máy chủ web chính của chúng tôi đang gặp lỗi 500 trên 100% lưu lượng truy cập. Tôi đăng nhập với đôi mắt ngái ngủ, mong chờ một sự cố cơ sở dữ liệu hoặc một lỗi rò rỉ bộ nhớ đơn giản. Thay vào đó, tôi thấy một “thị trấn ma”.

Thư mục /var/www/html—toàn bộ 8.4GB frontend production của chúng tôi—hoàn toàn trống rỗng. Một lập trình viên ít kinh nghiệm, trong khi cố gắng khắc phục vấn đề phân quyền trên một tệp CSS duy nhất, đã vô tình thực hiện lệnh xóa đệ quy với quyền root. Sai lầm duy nhất của họ? Là thành viên của nhóm sudo mặc định.

Sau khi chứng kiến 1.200 lần đăng nhập SSH thất bại chỉ trong một giờ trên một VPS mới, tôi đã học được cách ưu tiên bảo mật bên ngoài. Nhưng đêm đó đã dạy tôi rằng mối đe dọa không phải lúc nào cũng là tin tặc. Thông thường, rủi ro đến từ một đồng nghiệp có ý định tốt nhưng lại sở hữu quá nhiều quyền hạn. Việc cấp quyền truy cập ALL=(ALL:ALL) ALL không chỉ là sự cẩu thả trong kỹ thuật; đó là lời mời gọi cho một thảm họa xóa sạch hệ thống.

Để bảo vệ hệ sinh thái Linux, chúng ta phải từ bỏ tư duy nhị phân “hoặc là root hoặc không có gì”. Chúng ta cần Nguyên tắc Đặc quyền Tối thiểu (Principle of Least Privilege – PoLP). Bằng cách sử dụng tệp /etc/sudoers đúng cách, bạn đảm bảo người dùng chỉ có thể thực thi các lệnh cụ thể cần thiết cho công việc của họ. Không hơn. Không kém.

Giải mã cấu trúc một dòng cấu hình Sudoers

Trước khi đi sâu vào các tệp cấu hình, bạn cần hiểu cú pháp. Hầu hết các quản trị viên chỉ nhìn thấy dòng cấu hình root mặc định và sao chép-dán nó cho mọi nhân viên mới. Đây là một sai sót lớn. Hãy cùng phân tích dòng tiêu chuẩn đó:

root    ALL=(ALL:ALL) ALL
  • Người dùng (User): Trường đầu tiên (root) là người dùng hoặc nhóm đối tượng. Các nhóm luôn bắt đầu bằng dấu % (ví dụ: %sudo).
  • Máy chủ (Hosts): Chữ ALL đầu tiên xác định quy tắc này áp dụng cho những máy chủ nào. Trong thiết lập một máy chủ duy nhất, giá trị này luôn là ALL. Tuy nhiên, trong môi trường mạng sử dụng LDAP, bạn có thể giới hạn quy tắc cho các máy cụ thể.
  • Chạy dưới tư cách Người dùng/Nhóm (Run-as User/Group): Phần (ALL:ALL) xác định người dùng có thể giả danh ai. Phần đầu tiên là người dùng; phần thứ hai là nhóm.
  • Lệnh (Commands): Chữ ALL cuối cùng là đường dẫn đến tệp thực thi. Đây là biến nguy hiểm nhất trong cấu hình của bạn.

Quy tắc vàng: Không bao giờ chỉnh sửa trực tiếp /etc/sudoers

Một dấu chấm phẩy đặt sai chỗ trong /etc/sudoers có thể khiến bạn mất quyền truy cập root hoàn toàn. Nếu điều đó xảy ra, cách duy nhất để khắc phục có thể là khởi động lại vật lý vào chế độ single-user hoặc gắn ổ đĩa từ một ISO cứu hộ. Hãy luôn sử dụng lệnh visudo. Nó mở tệp trong một bộ đệm và chạy kiểm tra cú pháp trước khi các thay đổi thực sự được áp dụng vào hệ thống đang chạy.

sudo visudo

Nếu bạn làm sai cú pháp, visudo sẽ dừng bạn lại với lời nhắc “What now?”. Nhấn e để chỉnh sửa và sửa lỗi. Đừng bao giờ lưu một tệp bị lỗi cú pháp.

Thực hành thực tế: Kiểm soát chi tiết

Hãy xem xét một kịch bản thực tế. Bạn có một nhà phát triển tên là ‘alex’ cần quản lý dịch vụ Nginx và xem nhật ký (logs). Alex không nên được phép cài đặt phần mềm mới, chỉnh sửa cấu hình mạng hoặc chạm vào cơ sở dữ liệu.

Bước 1: Định nghĩa các bí danh lệnh (Command Aliases)

Các bí danh giúp tệp sudoers của bạn dễ đọc hơn. Thay vì liệt kê các đường dẫn lặp đi lặp lại, hãy nhóm chúng một cách logic ở đầu tệp. Điều này làm giảm khả năng sai sót về sau.

# Các bí danh lệnh (Command Aliases)
Cmnd_Alias WEB_MGMT = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx
Cmnd_Alias LOGS = /usr/bin/tail -f /var/log/nginx/*, /usr/bin/journalctl -u nginx

Bước 2: Cấp đặc quyền

Bây giờ, hãy gán các bí danh này cho Alex. Chúng ta sẽ cho phép Alex chạy các lệnh cụ thể này mà không cần mật khẩu để giữ cho quy trình làm việc của họ nhanh chóng, nhưng chúng ta sẽ giới hạn phạm vi một cách nghiêm ngặt.

alex ALL=(root) NOPASSWD: WEB_MGMT, LOGS

Bằng cách chỉ định (root), chúng ta giới hạn Alex chỉ được chạy các lệnh này dưới tư cách người dùng root. Nếu Alex cố gắng chạy sudo systemctl stop sshd, hệ thống sẽ thẳng thừng từ chối yêu cầu.

Bẫy NOPASSWD

Thẻ NOPASSWD là con dao hai lưỡi. Nó loại bỏ sự mệt mỏi khi phải nhập mật khẩu và hỗ trợ tự động hóa, nhưng nó tạo ra một lỗ hổng lớn. Nếu tài khoản của Alex bị xâm nhập thông qua việc mất khóa SSH, kẻ tấn công ngay lập tức có được quyền root đối với các lệnh cụ thể đó.

Đừng bao giờ sử dụng NOPASSWD cho các tệp thực thi “nguy hiểm” như vim, find, hoặc python. Nhiều công cụ có tính năng “thoát ra shell” (shell escapes) cho phép người dùng rơi vào một shell root từ bên trong ứng dụng. Ví dụ, một người dùng có quyền sudo đối với vi có thể chỉ cần nhập :!/bin/bash bên trong trình chỉnh sửa để trở thành root. Mọi chuyện đơn giản như vậy đấy.

Ngăn chặn thoát ra shell với NOEXEC

Nếu bạn bắt buộc phải cấp quyền truy cập vào một trình chỉnh sửa hoặc một công cụ có khả năng gọi shell, hãy sử dụng thẻ NOEXEC. Thẻ này ngăn lệnh tạo ra một tiến trình con như shell.

# Giới hạn người dùng chỉnh sửa cấu hình cụ thể mà không cho phép thoát ra shell
%junior_devs ALL=(ALL) NOEXEC: /usr/bin/vi /etc/nginx/sites-available/*

Giới hạn người dùng ‘Run-As’

Thông thường, các nhà phát triển chỉ cần quản lý người dùng ứng dụng, chẳng hạn như www-data hoặc deploy. Họ không thực sự cần quyền root. Bạn có thể giới hạn quyền truy cập sudo của họ để họ thậm chí không thể chạm vào tài khoản root.

# cho phép 'deployer' chỉ chạy các lệnh dưới danh nghĩa 'www-data'
deployer ALL=(www-data) ALL

Để sử dụng điều này, người triển khai (deployer) phải chỉ định người dùng mục tiêu:

sudo -u www-data git pull origin main

Kiểm tra và Ghi nhật ký sử dụng Sudo

Bảo mật không chỉ là ngăn ngừa; đó còn là về trách nhiệm giải trình. Khi mọi thứ đổ vỡ lúc 2 giờ sáng, nhật ký sudo là nơi đầu tiên tôi kiểm tra. Theo mặc định, hầu hết các bản phân phối Linux ghi lại các lần thử này vào /var/log/auth.log hoặc /var/log/secure.

Thắt chặt nhật ký của bạn sẽ đưa việc này đi xa hơn một bước. Bạn có thể buộc sudo ghi nhật ký vào một tệp tùy chỉnh hoặc thậm chí ghi lại toàn bộ I/O của một phiên làm việc. Lưu ý rằng việc ghi lại toàn bộ I/O có thể tiêu tốn đáng kể dung lượng đĩa trên các máy chủ bận rộn.

# Thêm vào /etc/sudoers thông qua visudo
Defaults logfile="/var/log/sudo.log"
Defaults log_year, log_host

Mỗi lệnh được thực thi qua sudo giờ đây đều được gắn dấu thời gian và ghi lại trong một tệp riêng biệt. Điều này giúp việc điều tra sự cố sau đó trở nên suôn sẻ hơn nhiều.

Kết luận

Cấu hình /etc/sudoers có vẻ tẻ nhạt cho đến khi nó cứu vãn công việc của bạn. Để mặc định quyền hạn rộng rãi là con đường ngắn nhất dẫn đến thảm họa. Bằng cách dành ra ba mươi phút để định nghĩa các nhóm Cmnd_Alias và giới hạn quyền truy cập ở mức tối thiểu, bạn xây dựng một hệ thống có khả năng phục hồi trước cả tai nạn lẫn ý đồ xấu.

Lần tới khi bạn thiết lập một máy chủ, đừng chỉ đưa người dùng vào nhóm sudo. Hãy nghĩ về những gì họ thực sự cần làm. Sử dụng visudo, tận dụng các bí danh và luôn giả định rằng cuối cùng, ai đó sẽ gõ sai lệnh vào sai thời điểm. Con người của bạn trong tương lai lúc 2 giờ sáng sẽ cảm ơn bạn vì điều này.

Share: