Skip to main content

3.4. Operations Deployment: SELinux và mandatory access control

Mục lục​


1. Vì sao cần mandatory access control?​

Linux có sẵn vài dạng kiểm soát truy cập ở mức thô sơ. File và thư mục có permission quy định ai được đọc, ghi hay thực thi nội dung, chi tiết ở Part 2.2: Phân quyền file và permission đặc biệt. Ngoài ra user thường không được làm một số việc mà user root làm được.

Nhưng các dạng bảo mật này không đủ cho thế giới tấn công mạng tinh vi hiện nay. Chúng quá chung chung và không đủ tinh chỉnh: một permission read hay write đơn thuần đã trao cho user hoặc process quá nhiều quyền lực trên một vùng cụ thể của hệ thống.

May là kernel Linux mở rộng được bằng các thứ gọi là module, và có một security module tên SELinux bổ sung những khả năng rất chi tiết để giới hạn truy cập. SELinux được cấu hình sẵn và bật mặc định trên hệ điều hành nhóm Red Hat, nhưng không bật mặc định trên Ubuntu, và việc cấu hình ban đầu trên Ubuntu hơi rắc rối, phần đó nằm ở mục 4 trở đi.

SELinux cho phép kiểm soát rất chi tiết việc hành động nào nên được cho phép và hành động nào nên bị từ chối. Cơ chế quyết định của nó tương tự cách Linux làm với permission thường. Lệnh ls -l cho thấy permission nào đang bật hay tắt cho một file hoặc thư mục, qua chuỗi kiểu rw-rw-r--. SELinux dùng một cơ chế giống vậy: thay vì option -l, dùng option -Z để xem label của file và thư mục:

ls -Z

# Output (minh họa):
unconfined_u:object_r:user_home_t:s0 notes.txt

Phần đầu dòng đó gọi là SELinux context, thỉnh thoảng cũng gọi là label, vì ở tầng file system nó chỉ là một nhãn dán lên file, một phần metadata bổ sung gắn thêm vào. Nhìn vào label, SELinux quyết định được security context của file này là gì và nó nên cho phép những hành động nào. Các label này trông mơ hồ giống permission của file và thư mục, nhưng chúng phức tạp hơn nhiều.


2. SELinux context gồm bốn phần​

Các thành phần của context luôn hiển thị theo đúng thứ tự này: user, role, type, level. Với ví dụ ở mục trên thì unconfined_u là user, object_r là role, user_home_t là type, và s0 là level.

Để cho phép hay từ chối một hành động, SELinux đi theo logic đại khái như sau.

Bước một, nó nhìn phần user của label. Phần này không giống username dùng để đăng nhập; nó là một SELinux user. Mỗi user đăng nhập vào hệ thống Linux đều được map sang một SELinux user, và việc map đó là một phần của cấu hình SELinux policy.

Bước hai, sau khi xác định được user, một quyết định được đưa ra để xem user đó có đảm nhận được role hay không. Mỗi user chỉ đảm nhận được một tập role đã định trước. Hình dung một user developer_u chỉ nên vào được các role như developer_r hoặc docker_r, những role cho phép đọc và ghi code ứng dụng, khởi chạy container và tương tự. Nhưng user đó không nên vào được các role như sysadmin_r, role cho phép đổi thiết lập hệ thống.

Bước ba, nếu role vào được thì một lần kiểm tra nữa chạy để xem role này có chuyển sang được type hay không. Tên của trường này đến từ Type Enforcement. Type giống một nhà tù mềm có tác dụng bảo vệ: thứ gì đã vào trong phạm vi type enforcement khai ở đây thì chỉ làm được một số việc nhất định và không làm được gì khác.

Phần thứ tư, level, gần như không bao giờ được dùng tới trên hệ thống thông thường. Nó phù hợp hơn với các tổ chức phức tạp cần nhiều cấp độ bảo mật.

💡 Hình dung: level giống hệ thống phân loại tài liệu trong quân đội. Jane là một sĩ quan cấp cao, có clearance level cao, nên đọc được tài liệu phân loại từ level 0 tới level 4, với level 4 là tối mật. Ngược lại, một user có quyền truy cập level 1 chỉ xem được thứ ở level 1 và level 0; mọi thứ trên level 1 đều bị chặn.

Nhìn tổng thể thì đường đi là user tới role tới type, để bước vào một chiếc hộp bảo mật có giới hạn. Qua ba bước này, SELinux đạt được ba việc: nó đảm bảo chỉ một số user nhất định vào được một số role và một số type nhất định, nhờ đó chặn các user và process không được phép bước vào những type hay domain không dành cho chúng; nó cho phép các user và process được phép làm việc của mình, bằng cách trao đúng những permission họ cần; và cùng lúc, kể cả user và process được phép cũng chỉ làm được một tập hành động giới hạn, mọi thứ khác đều bị từ chối.

Vì sao việc này quan trọng tới vậy? Lấy một ví dụ: nó bảo vệ được hệ thống trước một chương trình bị chiếm quyền. Hình dung một hacker nắm được quyền điều khiển chương trình Apache phục vụ web page cho khách truy cập website. Dù kẻ xấu đang điều khiển chương trình, họ vẫn bị SELinux giới hạn: ứng dụng Apache bị chiếm quyền đó bị giam trong một security domain nhất định áp lên process đó, nên nó chỉ làm được những hành động được phép cho security domain ấy. Kẻ tấn công không bước ra khỏi ranh giới đó được; họ bị giam trong một nhà tù ảo, và thiệt hại có thể gây ra giảm đi rất nhiều.

Trong phần lớn tình huống, phần quan trọng nhất của SELinux context là type, hay domain, tức thành phần thứ ba trong label.

Anatomy of an SELinux contextunconfined_uuserobject_rroleuser_home_ttypes0levelnot the login namean SELinux usermapped at loginrarely restrictivewhich roles a useris allowed to assumedeveloper_r orsysadmin_rtype enforcementthe actual jailmost important partof the whole labelmulti level securityalmost never usedlevel 0 up to level 4military clearancels -Z shows this for files. ps axZ shows it for processes.

3. Context của process và của user​

Process cũng được gán context giống file. Để xem context nào đang áp lên chúng thì dùng chính option -Z. Lệnh xem toàn bộ process là ps ax, và thêm -Z vào để output có cả security label:

ps axZ

# Output (rút gọn):
system_u:system_r:sshd_t:s0-s0:c0.c1023 920 ? Ss 0:00 sshd: /usr/sbin/sshd -D
unconfined_u:unconfined_r:unconfined_t:s0 1312 pts/0 Ss 0:01 -bash

Trong output đó thấy SSH daemon đang chạy và bị giới hạn trong type enforcement domain sshd_t. Domain này chứa một tập policy nghiêm ngặt định nghĩa process này được phép làm gì.

Một chi tiết cũng quan trọng: không phải thứ gì cũng vào được domain sshd_t. Trong SELinux policy mặc định toàn hệ thống, chỉ những file có SELinux type sshd_exec_t trong label của chúng mới vào được domain này. Kiểm tra file chứa chương trình SSHD thì đúng là nó được dán label với type đó.

Nhắc lại cho rõ: chỉ file được đánh dấu bằng một type nhất định mới vào được một process có thể chuyển sang một domain nhất định. Type quy định các hành động được phép cho một file. Domain quy định các hành động được phép cho một process.

Quay lại output của ps axZ. Bất cứ thứ gì dán label unconfined_t thì đang chạy không bị giới hạn: SELinux cho các process đó làm gần như mọi thứ chúng muốn.

Context của user đang đăng nhập​

SELinux bọc một bong bóng bảo mật quanh mọi thứ, kể cả một user, không riêng file hay process. Để xem security context gán cho user hiện tại, tức thứ được áp lên user lúc đăng nhập:

id -Z

# Output (minh họa):
unconfined_u:unconfined_r:unconfined_t:s0

Lúc đăng nhập, user được đăng nhập sẽ tự động map sang một SELinux user. Mapping đó tra được bằng lệnh liệt kê login mapping của semanage. Output kiểu đó nghĩa là khi user root đăng nhập, họ được map sang SELinux user tên unconfined_u. Nếu một user khác không được định nghĩa trong danh sách đó đăng nhập, họ được map sang giá trị ghi ở dòng __default__, trong trường hợp này cũng là unconfined_u.

Theo mặc định, SELinux không giới hạn user ở bước đầu, vì họ được chuyển sang user unconfined. Trên phần lớn hệ thống, chính thành phần role và type của context mới là chỗ bắt đầu giới hạn truy cập. Nhưng với các use case nâng cao, hệ thống dựng được theo kiểu giới hạn bắt đầu ngay từ tầng user.

Để xem các role mà mỗi user đảm nhận được trên hệ thống:

sudo semanage user -l

Trạng thái enforcing, permissive hay disabled​

Cuối cùng, để biết SELinux có đang bật và có đang chủ động giới hạn hành động hay không, tức có đang enforcing security hay không, có lệnh kiểm tra trạng thái, chi tiết ở mục 4. Chỉ khi thấy enforcing thì mới nghĩa là nó đang làm việc và từ chối các hành động không được phép. permissive nghĩa là nó cho qua mọi thứ: nó chỉ ghi log các hành động đáng lẽ bị chặn, nhưng không chủ động chặn gì cả. Còn disabled nghĩa là SELinux không làm gì hết, nó không enforce bảo mật và cũng không ghi log gì.


4. Bật SELinux trên Ubuntu từ đầu​

Hệ điều hành Red Hat và CentOS bật SELinux mặc định. Nhưng trên Ubuntu, một security module khác tên AppArmor được dùng mặc định. Vì vậy, khi tiếp quản một hệ thống do người khác dựng, nếu cần SELinux thì trước tiên nên kiểm tra xem nó đã được cấu hình chưa, trước khi cài thêm bất cứ thứ gì.

Phần này đi theo tình huống đã biết rõ là hệ thống chưa có SELinux. Vì AppArmor đang bật mặc định, việc đầu tiên là tắt nó đi: dùng hai security module cùng lúc có thể dẫn tới xung đột hoặc kết quả ngoài dự kiến. Nên trước hết stop service AppArmor, rồi disable nó khỏi việc tự khởi chạy lúc hệ thống boot, bằng các lệnh systemctl ở Part 3.1: Boot, systemd service và bash script.

Tiếp theo, để bắt đầu với SELinux thì cài hai package:

sudo apt install selinux-basics auditd

Package SELinux kéo vào toàn bộ các utility SELinux cần dùng cùng một số security policy mặc định. Package auditd chứa audit daemon, chính là utility sẽ ghi log một số event hệ thống và giúp dựng thêm các SELinux security policy may đo cho hệ thống này.

Để kiểm tra SELinux có đang bật trên hệ thống hay không:

sestatus

# Output (minh họa):
SELinux status: disabled

Đúng như dự đoán, ở đây nó đang disabled. Để bật SELinux lên thì có vài thứ cần thiết lập. Thứ nhất, bootloader phải được cấu hình để bảo kernel Linux nạp security module này. Thứ hai, mọi file trên hệ thống phải được dán label đúng, vì SELinux dùng label cho mọi thứ để quyết định một object nhất định được phép làm gì. Nhìn vào hệ thống lúc này sẽ thấy các SELinux label đang thiếu: dấu hỏi xuất hiện thay cho security label đúng.

Điểm thuận lợi là Ubuntu có một utility đi qua hết các bước cần thiết để bật SELinux và dán lại label cho file system:

sudo selinux-activate

Output cho thấy nó đã sửa GRUB bootloader để chỉ thị kernel Linux nạp module SELinux lúc boot. Một dòng trong đó chỉ thị bootloader truyền một tham số cho kernel Linux, và kernel sẽ nạp SELinux theo tham số ấy.

Nhìn vào root file system với ls ở chế độ hiện cả file ẩn dạng dấu chấm, sẽ thấy một file đặc biệt tên /.autorelabel. File đó chỉ thị SELinux dán lại label đúng cho mọi file khi reboot. Nên bước tiếp theo là reboot.

Quá trình dán lại label mất một lúc; file system càng nhiều file thì càng lâu. Sau lần reboot đó, bootloader được cấu hình để chờ 30 giây. Đây là biện pháp giúp người quản trị kịp can thiệp và sửa quá trình boot nếu có gì sai. Sau khi lần boot đầu tiên kể từ lúc bật SELinux thành công, độ trễ này của bootloader được bỏ đi, nên lần sau SELinux boot bình thường.

Giờ SSH vào lại, mọi thứ đã khác. Trước hết, các file đã có security label đúng. Và kiểm tra lại trạng thái SELinux sẽ ra output khác:

sestatus

# Output (minh họa):
SELinux status: enabled
Current mode: permissive

Trạng thái SELinux cho biết security model đang enabled, nhưng ở phần current mode thì nó đang ở trạng thái permissive. SELinux chạy được ở hai mode là permissive và enforcing. Một cách khác để kiểm tra nó đang ở mode nào:

getenforce

# Output:
Permissive
From disabled to enforcing on Ubuntu1. Stop AppArmor and disable it at boottwo security modules at once can conflict2. Install selinux-basics and auditdsestatus still reports disabled at this point3. Run selinux-activate, then rebootit edits GRUB and drops a .autorelabel file at the root4. Stay in permissive mode for a whilenothing is denied, every violation lands in the audit log5. audit2allow, semodule -i, then setenforce 1edit etc selinux config only once everything is verifiedsetenforce 1 lasts until the next reboot. The config file does not.

5. Permissive mode, audit log và audit2allow​

Khi SELinux đã bật nhưng đang ở permissive mode, nó không enforce các security rule. Nó chỉ quan sát mọi hành động trên hệ thống một cách thụ động.

💡 Hình dung: permissive mode giống một học viên đứng quan sát ca trực mà chưa được phép can thiệp. Nó không chặn hành động nào, nó chỉ ghi lại những gì security policy hiện tại đáng lẽ sẽ từ chối, và ghi các vi phạm tiềm năng đó vào audit log, nơi mà daemon auditd cài từ đầu có nhiệm vụ ghi vào.

Trong một tình huống thật, phải để SELinux chạy ở permissive mode một thời gian, coi như giai đoạn huấn luyện nó: để nó quan sát hết các hành động bình thường diễn ra trên hệ thống này mỗi ngày. Khi thấy nó đã quan sát đủ thì mới sang bước kế tiếp, tức tạo một security policy may đo cho hệ thống.

Ở thời điểm hiện tại, chuyển SELinux sang enforcing mode thì nó còn không cho đăng nhập qua SSH. Nhưng vì nó đang ở learning mode, audit log đã ghi lại các hành động nó quan sát được lúc đăng nhập. Để xem SELinux đã ghi những gì:

sudo audit2why --all | less

Option --all bảo lệnh audit2why hiện mọi event đã ghi log. Output được pipe sang less để cuộn và tìm text trong đó. Tìm các phần chứa chữ ssh sẽ thấy nhiều match, kiểu như sau.

Phần nhắc tới AVC: denied là phần đáng chú ý. AVC viết tắt của Access Vector Cache. Dòng đó cho biết hành động mà SELinux quan sát được ở đây sẽ bị security policy hiện tại từ chối trong điều kiện bình thường. Cũng để ý label sshd_t: một process có label này đã thử làm gì đó không được phép. Nhưng vì SELinux đang ở permissive mode, hành động đó được ghi log mà không bị chặn.

Một chi tiết nữa: có một thứ gọi là type enforcement rule đang không cho phép việc này với context sshd_t, và output khuyến nghị dùng tool audit2allow để sửa.

Thoát khỏi log đó. Để xem những process nào đang dán label sshd_t, dùng ps với tham số -e để hiện mọi process và tham số -Z để hiện SELinux context của chúng, rồi lọc bằng grep:

ps -eZ | grep sshd_t

Cách nhớ hai tham số này của ps: nghĩ tới từ easy, vì hai chữ e và z đọc lên thành easy. Output cho thấy process SSHD được dán label với SELinux type sshd_t. Còn nhìn vào label của chính file chương trình SSH daemon thì thấy file đó được dán label type sshd_exec_t. Khi SELinux chạy một thứ được dán label sshd_exec_t, process sinh ra sẽ được chuyển vào một domain dán label sshd_t. Domain là một kiểu bong bóng bảo mật giam process đó lại, và chỉ cho nó làm những gì được định nghĩa trong các type enforcement rule của sshd_t. Vì file được dán label theo một cách nhất định, SELinux biết nó phải áp giới hạn nào lên process khởi chạy ra.

Sinh policy từ những gì đã quan sát​

Hiện tại các process bị giới hạn trong domain sshd_t chưa làm được đủ việc. Nếu đặt SELinux sang enforcing mode thì thậm chí không đăng nhập được. Nhưng vì nó đã quan sát được một số hành động lúc đăng nhập trước đó, có một lệnh bảo SELinux cho phép mọi thứ nó đã thấy tới thời điểm này:

sudo audit2allow --all -m mymodule

Option --all bảo tool kiểm tra mọi event đã ghi log, còn option -m bảo nó sinh ra cái gọi là module, hay policy package: một file nạp được vào SELinux để tạo một security policy cho phép tất cả các hành động đã ghi log trước đó. mymodule chỉ là một cái tên tự chọn cho file security package này; đặt tên gì cũng được, nhưng nên đặt tên dễ nhớ để sau này biết module đó phục vụ mục đích gì.

Trong output, lệnh còn gợi ý luôn cách nạp các rule này vào SELinux:

sudo semodule -i mymodule.pp

Option -i ở đây bảo lệnh semodule cài package module này vào SELinux. Tùy phiên bản SELinux đang dùng, có thể thấy một thông báo lạ: đây là một bug trong phiên bản hiện tại. Policy mặc định có kèm một SELinux user định nghĩa sẵn tên sddm, nhưng thông báo này bỏ qua được vì hệ thống đang không dùng sddm, tức Simple Desktop Display Manager.

Nhớ rằng ở kịch bản hiện tại, SELinux vẫn đang chạy ở permissive mode. Để nó bắt đầu enforce các security policy:

sudo setenforce 1
getenforce

# Output:
Enforcing

Trong một tình huống thật, tới thời điểm này sẽ phải test lại toàn bộ trên server để xem mọi thứ còn chạy đúng như trông đợi hay không. Vì SELinux mặc định từ chối mọi thứ, nếu security policy hiện tại chưa có đủ các allow rule cần thiết thì một số chương trình sẽ bị chặn khỏi việc của chúng. Vậy nên chỉ đưa SELinux sang enforcing mode khi đã chắc chắn các policy bao gồm hết những gì cần thiết.

Một điểm hữu ích cần biết: cách bật enforcing vừa làm chỉ có tác dụng tạm thời. Reboot một cái là nó quay về permissive mode. Với lần test đầu thì bật tạm như vậy là tốt, vì nếu có gì sai thì cứ reboot server và bắt đầu lại từ permissive mode. Khi đã chắc chắn các policy đủ đầy thì mới đặt SELinux sang enforcing mode vĩnh viễn, bằng cách sửa một file:

sudo vim /etc/selinux/config

Trong đó đổi dòng mode từ permissive thành enforcing. Reboot lần sau, SELinux khởi động ở enforcing mode. Lưu ý: nếu các SELinux security policy chưa đủ bao quát thì có thể không đăng nhập lại được hoặc không dùng hệ thống bình thường được, nên chỉ bật khi đã chắc chắn mọi thứ đã dựng đúng.

Đọc policy đã sinh ra​

Quay lại module vừa sinh bằng audit2allow. Thực tế có hai file được tạo ra. File mymodule.pp ở định dạng binary, dễ cắm vào SELinux; pp viết tắt của policy package. File thứ hai cho thấy các rule ở dạng text, với extension .te, viết tắt của type enforcement. Mở mymodule.te ra là nắm được đại khái security policy mà audit2allow đã sinh.

Trong một tình huống thật, phải rà file này rất kỹ và nắm hết những gì nó cho phép trên hệ thống trước khi đem các rule đó triển khai vào SELinux. Công việc này tẻ nhạt, có thể cần chỉnh sửa thêm, và cần có tài liệu SELinux bên cạnh. Cũng nên tạo một file policy package riêng cho từng object cần bảo vệ, vì file nhận được từ audit2allow gom khá nhiều thứ không liên quan vào chung.

Nhìn vào phần sshd_t trong file đó. Nội dung thoạt nhìn khá đáng ngợp, nhưng thử đơn giản hóa và hiểu lấy một dòng. SELinux giam process vào các bong bóng bảo mật gọi là domain, và một domain được dán label bằng một type nhất định. Type áp lên process quyết định rule nào được enforce, và đó là lý do nó được gọi là type enforcement. Ở đây sshd_t là tên của type đó. Nên trong policy này, SELinux được bảo rằng các process trong domain type sshd_t được phép làm một số việc nhất định. Cụ thể ở dòng đang xét, các process thuộc domain này được phép thực hiện một số hành động trên file dán label type var_log_t: chúng được phép tạo, mở và sửa những file như vậy.

Nối các điểm lại với nhau. Nhìn lại SELinux label của process SSH daemon thì thấy nó bị giam trong domain type sshd_t. Và SSH daemon phải ghi log hoạt động của nó vào file /var/log/auth.log. Nhìn SELinux label của file đó thì thấy nó được dán label type var_log_t. Nghĩa là dòng policy do audit2allow sinh ra đã bảo SELinux cho phép các process thuộc domain sshd_t tạo và ghi file dán label var_log_t.

Trên Linux thì đặt được permission trên file và kiểm soát được user nào đọc, ghi hay thực thi. Với SELinux thì mức kiểm soát chi tiết hơn: giới hạn được một process cụ thể truy cập được những file nào. Nhờ vậy, kể cả khi có người chiếm được quyền điều khiển một process web server Nginx, họ cũng không đọc và sửa được file hệ thống; họ chỉ chạm được vào những file có label dành riêng cho HTTP daemon.

How a label becomes a running confinementthe sshd binarylabelledsshd_exec_trunsthe sshd processconfined in domainsshd_tmay writevar log auth.loglabelledvar_log_tThe generated policy line says all of this in one sentenceprocesses in the sshd_t domain may create, open and write files labelled var_log_tbreak the label on auth.log and the SSH daemon can no longer log anythingOnly a file labelled sshd_exec_t may enter the sshd_t domain.

6. Đổi context bằng chcon và restorecon​

Nhìn kỹ lại label của file /var/log/auth.log. Phần user label là system_u. Phần role label là object_r. Phần type label là var_log_t. Chữ cái cuối cùng cho biết phần đó là user, role hay type. Ba phần này giam mọi file và mọi process vào thứ SELinux gọi là security context. Vì vậy lệnh dùng để đổi context này có tên là chcon, viết tắt của change context.

Để đổi user label, truyền option -u kèm label muốn dùng, ví dụ unconfined_u. Để đổi role thì truyền option -r. Để đổi type thì truyền option -t:

sudo chcon -u unconfined_u /var/log/auth.log
sudo chcon -r object_r /var/log/auth.log
sudo chcon -t var_log_t /var/log/auth.log

Thông thường chỉ cần làm việc với type. SELinux cũng enforce được một số giới hạn dựa trên user đang thực hiện hành động, bằng cách kiểm tra user đó có chuyển sang được một số role nhất định hay không. Nhưng việc này không được enforce theo mặc định, vì lúc đăng nhập thì user được gán với một SELinux user tên unconfined_u, mà user đó truy cập được gần như mọi thứ.

User, role và type đặt bằng chcon phải là các SELinux label hợp lệ, nên chỉ dùng được những thứ đã được định nghĩa sẵn trong security policy. Nhưng label nào là hợp lệ? Có thể tìm ra bằng cách đọc một phần lớn tài liệu, nhưng có đường tắt:

seinfo -u
seinfo -r
seinfo -t

seinfo -u hiện các SELinux user label hợp lệ, -r hiện role, -t hiện type. Nhưng để hiểu hành động nào được phép với từng type thì vẫn phải đi qua một phần lớn tài liệu. Cách dễ hơn là nhìn các file có mục đích tương tự và từ đó suy ra type cần dùng cho một tình huống nhất định.

Sửa một label đã bị làm sai​

Thử ngay cách đó, vì tới lúc này label của file auth.log đã bị làm lộn, do nó bị đổi bằng lệnh chcon với những giá trị ngẫu nhiên không hợp nghĩa cho một file log. Với label sai như vậy, SSH daemon không ghi được vào file đó nữa.

Có hai cách sửa. Cách thứ nhất là nhìn label của các file khác trong thư mục /var/log: nếu may thì thấy các file auth.log khác như auth.log.1, những file có label đúng. Hoặc để ý các file tương tự như syslog, rồi đoán rằng auth.log cần được dán label giống syslog, vì hai file có mục đích tương tự nhau.

Nhưng đặt tay từng phần label cho user, role và type thì tẻ nhạt. Có cách đặt cả ba trong một lần: bảo chcon dùng một file khác làm reference và copy hết các phần label từ file tốt sang file xấu. Ở đây file dmesg được chọn làm file tốt dùng làm reference:

sudo chcon --reference /var/log/dmesg /var/log/auth.log
ls -Z /var/log/auth.log

Nhìn lại label là thấy nó đã được sửa.

Label cho file và thư mục mới​

Còn file và thư mục mới tạo thì sao? Giả sử dựng một website và lưu file của nó ở đường dẫn mặc định cho mục đích này, tức thư mục /var/www:

sudo mkdir /var/www
sudo touch /var/www/{1..10}
ls -Z /var/www

Với SELinux đã bật, web server Apache hay Nginx không làm việc được với các file này nếu chúng không có label đúng. Nhìn label hiện tại của chúng thì thấy ngay là không ổn: type label là var_t, nghe rất chung chung, nên khó đoán label đúng ở đây phải là gì. Nhưng có một lệnh giúp được.

SELinux có một kiểu database cho biết nó phải dán label thế nào cho file trong những thư mục nhất định. Khi label sai, sửa bằng lệnh restorecon, viết tắt của restore context, với tham số -r để thực hiện hành động này đệ quy trên cả thư mục cùng mọi subdirectory và file bên trong:

sudo restorecon -r /var/www
ls -Z /var/www

# Output (minh họa):
unconfined_u:object_r:httpd_sys_content_t:s0 1

Lần này type là httpd_sys_content_t, nghe gợi ý rõ ràng và có mục tiêu hơn nhiều, cho biết đây là system content dành cho HTTP daemon.

Nhưng có một thứ không lộ ra ngay: theo mặc định, restorecon chỉ phục hồi đúng label cho phần type. Ví dụ đổi user label của một trong các file này thành staff_u rồi chạy lại restorecon đệ quy, sẽ thấy user label không được phục hồi: file1 vẫn tham chiếu user sai, vì restorecon không chạm vào user và role label, chỉ chạm vào phần cuối là type. Để ép restorecon phục hồi hết các label cho user, role và type thì truyền option -F, tức force:

sudo chcon -u staff_u /var/www/1
sudo restorecon -r /var/www
sudo restorecon -r -F /var/www

Kiểm tra lại các file sẽ thấy các phần của label đã được đổi về mặc định hệ thống của SELinux cho thư mục này.


7. Default file context với semanage fcontext​

Việc đổi label bằng chcon có một điểm bất lợi: nếu file system từng được dán lại label lần nữa, giống lúc cài SELinux ở mục 4, thì label tự đặt trên một file có thể bị mất. Để thay đổi của mình chịu được chuyện đó thì làm theo cách khác.

Giả sử muốn file1 luôn có một type label khác. Thay vì chcon, chạy lệnh sau:

sudo semanage fcontext --add --type var_log_t /var/www/10

Thông báo lạ về sddm lại xuất hiện, nhưng không sao. Ở đây lệnh semanage được dùng để thông báo cho SELinux về một default file context mới: option --add thêm một entry vào database của nó, nói rằng label ở option --type nên là var_log_t cho file /var/www/10.

Lệnh này chỉ nói cho SELinux về label mặc định, nó không thực sự đổi label ngay. Nên phải theo sau bằng một lệnh nữa để phục hồi label mặc định đó:

sudo restorecon /var/www/10
ls -Z /var/www/10

Kiểm tra là thấy thay đổi đã có hiệu lực.

Đặt label mặc định cho cả một thư mục​

Để đặt label mặc định cho cả một thư mục và toàn bộ nội dung bên trong thì cú pháp rắc rối hơn một chút. Nhưng có thể lấy gợi ý bằng cách liệt kê toàn bộ database default file context của SELinux cho các file và thư mục khác nhau:

sudo semanage fcontext -l

Trong đó có những dòng cho thấy cách dùng regular expression với cặp ngoặc đơn để đặt label mặc định cho một thư mục cùng mọi subdirectory và file nó có thể chứa. Lệnh cần dùng có dạng:

sudo semanage fcontext --add --type httpd_sys_content_t "/var/www-demo(/.*)?"
sudo mkdir /var/www-demo
sudo restorecon -r /var/www-demo

Đường dẫn thư mục phải được bọc trong cặp nháy để Bash không thông dịch các ký tự đặc biệt. Phần /.* chỉ là một regular expression khớp với mọi loại đường dẫn file hay subdirectory có thể có bên dưới thư mục đó. Sau đó phải chạy restorecon để phục hồi default context vừa tạo cho vị trí đó. Nhưng vì thư mục chưa tồn tại, phải tạo nó trước đã, rồi mới restorecon. Kiểm tra lại là thấy context đúng đã được đặt.

Three ways to set a label, three lifetimeschcon-u user, -r role, -t type--reference copies allset by hand, right nowlost on a relabel ofthe whole file systemgood for a quick fixrestorecon-r walks the whole tree-F forces user and rolereads the context databaseby default it restoresonly the type partgood for repairingsemanage fcontext--add --type var_log_twrites the databasesurvives a full relabelstill needs a restoreconrun to touch the diskgood for new pathsA label that must outlive a relabel belongs in the context database.

8. Boolean và port binding​

SELinux dùng các label trên để quyết định những gì được phép xảy ra trên hệ thống, ít nhất là với phần lớn trường hợp. Nhưng có vài ngoại lệ, hay nói đúng hơn là vài đường tắt dùng được.

Thay vì định nghĩa các type enforcement policy phức tạp, có thể dùng những thứ gọi là Boolean. Nó giống việc bật một công tắc để bảo SELinux cho phép hoặc không cho phép một tập hành động. Các boolean mà SELinux hỗ trợ xem được qua lệnh liệt kê boolean của semanage, cho ra những dòng kiểu:

sudo semanage boolean -l

# Output (minh họa):
virt_use_nfs (off , off) Allow virt to use nfs

Trên một dòng như vậy thấy boolean virt_use_nfs đang bị tắt. Chữ off đầu tiên trong ngoặc cho biết nó hiện được đặt thành false, tức 0. Chữ off thứ hai cho biết trạng thái mặc định của boolean này cũng là off. Phần description ở cuối cho biết mỗi boolean làm gì. SELinux có lệnh riêng để bật một boolean như virt_use_nfs lên, và một lệnh riêng để đọc giá trị hiện tại của một boolean cụ thể.

Port binding​

Một thứ nữa mà SELinux enforce là các daemon được phép bind vào những port nào để nghe kết nối vào. Để liệt kê các port binding được phép cho từng type:

sudo semanage port -l
sudo semanage port -l | grep ssh

# Output (minh họa):
ssh_port_t tcp 22

Lọc lại để chỉ hiện port binding cho SSH thì thấy chỉ port 22 được phép. Muốn cho phép thêm một port như 2222 thì chạy một lệnh dạng --add, và để bỏ port đó khỏi tập rule thì chỉ cần đổi --add thành --delete:

sudo semanage port --add --type ssh_port_t --proto tcp 2222
sudo semanage port --delete --type ssh_port_t --proto tcp 2222

Phần trên chỉ phủ các khái niệm bề mặt về việc enforce rule với SELinux. Để hiểu SELinux ở tầng sâu hơn thì cần khoảng mười tới hai mươi giờ học và thực hành. Nó là một tool phức tạp, thoạt đầu trông khá ngợp, nhưng đáng để học thêm: nhiều vụ tấn công làm lộ tài khoản của hàng triệu người dùng đã có thể được ngăn lại bằng một SELinux policy thiết kế cẩn thận.


Nguồn tham khảo​

Nguồn gốc: Khóa "Prep Course - Linux Foundation Certified System Administrator (LFCS) Certification" — phần "Operations Deployment" (SELinux context áp lên file và process; enforce mandatory access control bằng SELinux security module trên Ubuntu, gồm permissive mode, audit log, audit2allow, chcon, restorecon, semanage fcontext, boolean và port binding), nền tảng KodeKloud. Giảng viên: Jeremy Morgan và Alexandru Andrei.

Fact-check:

  • Lệnh đặt SELinux sang enforcing mode tạm thời là setenforce 1; getenforce chỉ đọc mode hiện tại. Bài giảng gốc có một câu gán hành vi đặt mode cho getenforce, trong khi hai đoạn trước đó chính bài dùng đúng sudo setenforce 1. Nguồn: man 8 setenforce và man 8 getenforce, 2026-10-25.