SELinux

1. 引言

安全增強式 Security-Enhanced Linux(SELinux)是一個在內核中實踐的強制存取控制(MAC)安全性機制。SELinux 首先在 CentOS 4 出現,並在 CentOS 5 獲得重大改善。

1.1. 部份問題所在

要更瞭解 SELinux 為何是重要、及能夠為你做甚麼,最簡單的方法就是參考一些例子。在未啟用 SELinux 的情況下,要控制用戶的檔案存取權,唯有透過酌情存取控制(DAC)方法如檔案權限或存取控制清單(ACL)。不論用戶或程式都可以將不安全的檔案權限賦予其他人,或反過來存取系統在正常運作下無須存取的部份。舉個例說:

基本上在傳統 DAC 模式只在兩個權限級別,root 及用戶,而當中無法簡易地實施最小權限的理念。很多由 root 啟動1的進程在後期會撇除它們的權限並以受限制的用戶身份來運行,有些則會在 chroot 的情況下執行,但這些安全措施都是酌情的。

1.2. 解決方案

SELinux 更能遵從最小權限的理念。在預設的 enforcing 情況下,一切均被拒絕,接著有一系列例外的政策來允許系統的每個元素(服務、程式、用戶)運作時所需的存取權。當一項服務、程式或用戶嘗試存取或修改一個它不須用的檔案或資源時,它的請求會遭拒絕,而這個行動會被記錄下來。

由於 SELinux 是在內核中實踐的,應用程式無須被特別編寫或重寫便可以採用 SELinux。當然,如果一個程式特別留意稍後所提及的 SELinux 錯誤碼,它的運作可能會更暢順。假若 SELinux 攔阻了一個行動,它會以一個標準的(至少是常規的)「拒絕存取」類錯誤來匯報給該應用程式。然而,很多應用程式不會測試系統函數所傳回的錯誤碼,因此它們也許不會輸出訊息解釋問題所在,或者輸出錯誤訊息。

理論上,下列方案可提供更高安全度:

不過截至第 6 版的 CentOS,這些方案都不包含在SELinux 規則內。這是一個發展中的領域,事實上亦不會在短期內落實。這是由於上游發行者的系統管理員客戶群難以接受上述的做法,勢必會「啼哭」一番,並增加上游的支援工作量。

2. SELinux 模式

SELinux 擁有三個基本的操作模式,當中 Enforcing 是預設的模式。此外,它還有一個 targetedmls 的修飾語。這管制 SELinux 規則的應用有多廣泛,當中 targeted 是較寬鬆的級別。

SELinux 的模式可以透過 Adminstration 選單裡的 SELinux 圖像管理介面、或者在指令行執行 system-config-selinux 來檢視及更改(SELinux 圖像管理介面是 policycoreutils-gui 套件的一部份,預設是不會被安裝的)。

較喜歡指令行的用戶可使用 sestatus 這個指令來檢視現時的 SELinux 狀況:

# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 21
Policy from config file:        targeted

setenforce 這個指令可以即時切換 EnforcingPermissive 這兩個模式,但請注意這些改動在系統重新開機時不會被保留。

要令改動過渡系統開機,請在 /etc/selinux/config 內修改 SELINUX= 這一行為 enforcing、permissive 或 disabled。例如 SELINUX=permissive。

註: 當你由 Diabled 切換至 PermissiveEnforcing 模式時,我們強烈建議你重新啟動系統並重新標籤檔案系統。

3. SELinux 政策

較早前我們提及 SELinux 遵從最小權限這個理念;在預設情況下一切均被拒絕,而系統的每部份有一個政策來允許它存取所需的功能。這個描述用來形容嚴格型政策最為貼切。不過要令這類政策適用於企業級 Linux 可能會應用到的各個環境,編寫方面是困難的。後果可能是 SELinux 會為系統管理員及用戶產生很多問題,而系統管理員索性停用 SELinux 而不解決這些問題,最後還是違背了內置的保護。

SELinux 的設計允許調配不同類型的政策。CentOS 4、5 及 6 內的預設政策是針對型政策,專門「針對」和規限選定的系統進程。CentOS 4 只定義了 15 個目標(包括 http、named、dhcpd、mysqld)。隨後在 CentOS 5 這個數字已升超過 200 個目標。

系統內其它一切系統進程、用戶程式和所有自製應用程式都在不受規限的區域下運行,並且不納入 SELinux 的保護範圍內。

這樣做的目標之一,是要令每個已安裝及在開機時執行的進程,都預設在一個受規限的區域下運行。針對型政策在設計時儘量保護最多的主要進程而不會對用戶的經驗產生不利影響,所以多數用戶甚至乎不應察覺 SELinux 正在運行中。

4. SELinux 存取控制

SELinux 擁有三種存取控制方法:

所有進程及檔案都擁有一個 SELinux 的安全性脈絡。讓我們檢視 Apache 的主頁,index.html 的 SELinux 安全性脈絡來看看它們如何運作:

$ ls -Z /var/www/html/index.html -rw-r--r--  username username system_u:object_r:httpd_sys_content_t /var/www/html/index.html 

註: -Z 這個標旗在多數工具內都可用來顯示 SELinux 安全性脈絡(例如:ls -Z、 ps axZ 等)。

除了標準的檔案權限及擁有權,我們更可以看到 SELinux 脈絡欄:system_u:object_r:httpd_sys_content_t。

這是建基於「用戶:角色:類別:多層保障」。在上述例子裡,「用戶:角色:類別」欄都有顯示,而「多層保障」是隱藏的。在預設的針對型政策裡,類別是用來實施「強制類別」的重要欄位,在這裡它是 httpd_sys_content_t。

現在讓我們看看 Apache 網頁伺服器,httpd,這個進程的 SELinux 安全性脈絡:

$ ps axZ | grep httpd
system_u:system_r:httpd_t        3234 ?        Ss     0:00 /usr/sbin/httpd

從類別欄我們看出 Apache 在 httpd_t 這個類別區域內運行。

最後,讓我們看看位於我們的主目錄內的一個檔案的安全性脈絡:

$ ls -Z /home/username/myfile.txt
-rw-r--r--  username username user_u:object_r:user_home_t      /home/username/myfile.txt

它的類別是 user_home_t,這是位於每個戶主目錄內的檔案的預設類別。

唯有相似的類別才可互相存取,因此以 httpd_t 運行的 Apache 可以讀取擁有 httpd_sys_content_t 類別的 /var/www/html/index.html。由於 Apache 在 httpd_t 這個區域內運行但不屬 username 這個用戶,縱使 /home/username/myfile.txt 可供任何人讀取,Apache 卻無法存取該檔案,因為它的 SELinux 安全性脈絡並不是 httpd_t 類別。倘若 Apache 被人佔用,又假設它仍未取得更改 SELinux 標籤至另一個脈絡的 root 權限,它將會無法啟動 httpd_t 區域外的進程(藉此防止權限升級),或存取與 httpd_t 區域不相關的檔案。

5. 排除 SELinux 疑難

你終有一天會被 SELinux 阻止你存取所需的東西,而且要解決這個問題。SELinux 拒絕某個檔案、進程或資源被存取的基要原因有數個:

頭三個情況我們可以處理,而發出警報及通告正正是第四個情況的預期表現。

日誌檔是排除任何疑難的關鍵,而 SELinux 亦不例外。SELinux 預設會透過 Linux 審計系統 auditd 將日誌寫在 /var/log/audit/audit.log 內,而該務服預設為啟用的。假若 auditd 長駐程式並未運行,信息將會被寫進 /var/log/messages。SELinux 的日誌都以 AVC 這個關鍵字作標籤,讓 grep 等程式可輕易地把它們從其它信息中過濾出來。

由 CentOS 5 起,你可以用 SELinux 排除疑難工具協助你分析日誌檔,將它們轉換為供人閱讀的格式。這個工具包含一個以可讀格式顯示信息及解決方案的圖像介面、一個桌面通報圖示、與及一個長駐進程,setroubleshootd,它負責查閱新的 SELinux AVC 警告並傳送至通報圖示。不運行 X 伺服器的用戶可設置以電郵通報。SELinux 排除疑難工具是由 setroubleshoot 套件所提供的。這個工具可以從 X 視窗圖像管理員的「系統」選單或從指令行啟動:

sealert -b 

不運行 X 伺服器的人可以透過指令行產生供人閱讀的報告:

sealert -a /var/log/audit/audit.log > /path/to/mylogfile.txt 

5.1. 重新標籤檔案

chcon 這個指令可以用來更改一個或多個檔案與目錄的 SELinux 安全性脈絡,正如 'chown' 或 'chmod' 可以用來更改一個檔案的擁有者或標準權限。

讓我們看一些例子。

就以 Apache 為例,假設你想修改 DocumentRoot 以另一個位置來伺服網頁,替代預設的 /var/www/html 目錄。譬如說我們在 /html 建立了一個目錄(又或者掛載點),然後在那裡建立一個 index.html 檔:

# mkdir /html
# touch /html/index.html
# ls -Z /html/index.html
-rw-r--r--  root root user_u:object_r:default_t        /html/index.html
# ls -Z | grep html
drwxr-xr-x  root root user_u:object_r:default_t        html 

我們可以見到 /html 這個目錄以及 /html/index.html 這個檔案都擁有預設的 default_t 安全性脈絡類型。如果我們開啟瀏覽器並嘗試檢視該頁,SELinux 將會正確地拒絕它們被存取並記錄錯誤,因為該目錄與檔案擁有不正確的安全性脈絡。我們須要設定供 Apache 使用的 httpd_sys_content_t 正確安全性脈絡。

# chcon -v --type=httpd_sys_content_t /html
context of /html changed to user_u:object_r:httpd_sys_content_t
# chcon -v --type=httpd_sys_content_t /html/index.html
context of /html/index.html changed to user_u:object_r:httpd_sys_content_t
# ls -Z /html/index.html
-rw-r--r--  root root user_u:object_r:httpd_sys_content_t    /html/index.html
# ls -Z | grep html
drwxr-xr-x  root root user_u:object_r:httpd_sys_content_t    html 

我們同樣也可以利用 -R 這個迴遞標旗同時將它們的脈絡設定:

# chcon -Rv --type=httpd_sys_content_t /html 

以這個方式更改安全性脈絡在系統重新開機後仍會獲保留,直至該部份檔案系統被重新標籤。這個動作也算頗常,因此正確的解決方法,就是於測試後編寫一條自訂的規則(所謂的政策模塊),並把它與本地的規則進行合併。它將會是原有的 200 多條規則外的一條規則。要作出永久性、能過渡檔案系統重新標籤的安全性脈絡改動,我們可以採用 SELinux 管理工具,或者在指令行執行 semanage 這個指令:

semanage fcontext -a -t httpd_sys_content_t "/html(/.*)?" 

如此便會將 /html 以下的一切加入 httpd_sys_content_t 這個檔案脈絡類型。

5.2. 還原預設的安全性脈絡

restorecon 這個指令可以用來還原為檔案預設的安全性脈絡。

讓我們再次以 Apache 作為範例。設假有位用戶在他的主目錄內編輯了一個 index.html 檔並將該檔案遷移(mv)至 DocumentRoot 的 /var/www/html 內。縱使複製(cp)這個指令普遍會沿用目標目錄或檔案的安全性脈絡,遷移(mv)指令則會保留來源檔的安全性脈絡。我們可以利用 chcon 這個指令來更改問題檔案的安全性脈絡,但由於這些檔案已經位於 Apache 預設的 DocumentRoot(/var/www/html)內,我們只須還原這個目錄或檔案的安全性脈絡便成了。要單單還原 index.html 檔的脈絡,我們可以利用:

# restorecon -v /var/www/html/index.html 

如果要以迴遞的方式還原整個目錄的預設安全性脈絡:

# restorecon -Rv /var/www/html 

除此之外,如果我們只想檢查 /var/www/html 目錄內有哪些檔案的安全性脈絡需要被還原,我們在採用 restorecon 時可以應用 -n 這個標旗來防止重新標籤的行動:

# restorecon -Rv -n /var/www/html 

5.3. 重新標籤整個檔案系統

有時我們也許會需要將整個檔案系統重新標籤,雖然這是在啟用已停用的 SELinux 時,或在 SELinux 政策由預設的針對型改為嚴格型時才有真正需要。要在重新開機後自動將整個檔案系統重新標籤,請執行:

# touch /.autorelabel
# reboot 

假若一個系統在升級至 CentOS-5.2 時停用了 SELinux,然後 SELinux 被啟用,重新標籤整個檔案系統時或許會失敗。如果以上的步驟無法正確地將整個檔案系統重新標籤,請嘗試先執行 genhomedircon 這個指令:

# genhomedircon
# touch /.autorelabel
# reboot 

5.4. 允許存取某個連接埠

我們或許會想容讓 Apache 連結至某個非標準的埠並聆聽對內的連線。SELinux 的政策預設只允許每個服務存取被公認與該服務有關的連接埠。如果我們想容讓 Apache 在 tcp 埠 81 上聆聽,我們可以利用 semanage 這個指令來新增一條規則批準此事:

# semanage port -a -t http_port_t -p tcp 81 

你可以這樣令 SELinux 完整地列出每個服務可存取的連接埠:

# semanage port -l 

6. 自訂 SELinux 政策

透過設定選項的二元值,你可以微調 SELinux 政策而不必重新編譯政策的源代碼。這些選項包括允許用戶在 Samba 下分享他們的主目錄,或者允許 Apache 從用戶的主目錄伺服檔案。否則這些都會被 SELinux 政策所拒絕。

有另一版 Wiki 頁是關於二元值的。

7. 利用 audit2allow 建立自訂 SELinux 政策模塊

在某些情形下,上述方法都無法解決問題,而我們必須通過建立自訂的政策模塊來延伸 SELinux 政策,允許一組特定的狀態出理。其中一個例子就是在 smtp 郵件伺服器上增加 postgrey 服務。我們的 smtp 伺服器須要透過一個 Unix 通訊端與 postgrey 溝通,但預設的 SELinux 政策卻禁止我們的 smtp 伺服器如此做。結果該服務會被 SELinux 所封鎖。這個問題無法透過更改或還原檔案的安全性脈絡來解決,也沒有可供切換二元值。我們可以通過一個二元值來停止 SELinux 保護 smtp 伺服器,這樣總比完全停用 SELinux 好,但不太理想。

如果我們將 SELinux 切換至 Permissive 模式並讓郵件伺服器運行一段時間,我們便可以在允許存取的情況下記錄 SELinux 的問題。檢視日誌時,我們會看見以下 SELinux AVC 信息:

type=AVC msg=audit(1218128130.653:334): avc:  denied  { connectto } for  pid=9111 comm="smtpd" path="/var/spool/postfix/postgrey/socket"
scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket
type=AVC msg=audit(1218128130.653:334): avc:  denied  { write } for  pid=9111 comm="smtpd" name="socket" dev=sda6 ino=39977017
scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=system_u:object_r:postfix_spool_t:s0 tclass=sock_file 

接著我們可以用 audit2allow 來產生一組允許所需行動的政策規則。我們我可建立一個本地的 postgrey 強制類別政策檔(postgreylocal.te):

# grep smtpd_t /var/log/audit/audit.log | audit2allow -m postgreylocal > postgreylocal.te
# cat postgreylocal.te
module postgreylocal 1.0;
require {
        type postfix_smtpd_t;
        type postfix_spool_t;
        type initrc_t;
        class sock_file write;
        class unix_stream_socket connectto;
}
#============= postfix_smtpd_t ==============
allow postfix_smtpd_t initrc_t:unix_stream_socket connectto;
allow postfix_smtpd_t postfix_spool_t:sock_file write; 

在上面我們看見如何從 audit.log 篩選有關 smtp 伺服器的問題,並將這些問題導向 audit2allow,讓它產生一組規則,是它認為可用來允許被 SELinux 政策所封鎖的行動。查閱這些規則時,我們可發現該 smtp 伺服器想連線及寫進一個 Unix 通訊端,而從日誌裡我們看見這個 Unix 通訊端正正是 postgrey 服務所聆聽的那個。既然這一些都合情合理,我們可以續繼用 audit2allow 建立一個自訂的政策模塊,允許這些行動:

# grep smtpd_t /var/log/audit/audit.log | audit2allow -M postgreylocal 

接著我們利用 semodule 這個指令將我們的 postgrey 政策模塊載入現有的 SELinux 政策內:

semodule -i postgreylocal.pp 

如此便會將我們的 postgrey 政策模塊新增到 /etc/selinux/targeted/modules/active/modules/postgreylocal.pp。我們可以透過 semodule -l 來檢查該政策模塊已被正確地載入。

然後我們可以繼續監視 SELinux 的日誌來確定自訂的政策模塊有效用。滿意時,我們便可以重新啟用 SELinux 的 Enforcing 模式,讓功能已全備的 smtp 伺服器再次享有 SELinux 的保障。

7.1. 手動式自訂政策模塊

audit2allow 在大多數情況下都可以自動建立一個自訂政策模塊來解決某個特定問題,但有時它未能完全正確,而我們也許會想以人手編輯並編譯該政策模塊。就以下列的 AVC 審計日誌為例:

Summary:
SELinux is preventing postdrop (postfix_postdrop_t) "getattr" to
/var/log/httpd/error_log (httpd_log_t).
Detailed Description:
SELinux denied access requested by postdrop. It is not expected that this access
is required by postdrop and this access may signal an intrusion attempt. It is
also possible that the specific version or configuration of the application is
causing it to require additional access.
Allowing Access:
Sometimes labeling problems can cause SELinux denials. You could try to restore
the default system file context for /var/log/httpd/error_log,
restorecon -v '/var/log/httpd/error_log'
If this does not work, there is currently no automatic way to allow this access.
Instead, you can generate a local policy module to allow this access - see FAQ
(http://fedora.redhat.com/docs/selinux-faq-fc5/#id2961385) Or you can disable
SELinux protection altogether. Disabling SELinux protection is not recommended.
Please file a bug report (http://bugzilla.redhat.com/bugzilla/enter_bug.cgi)
against this package.
Additional Information:
Source Context                system_u:system_r:postfix_postdrop_t
Target Context                root:object_r:httpd_log_t
Target Objects                /var/log/httpd/error_log [ file ]
Source                        postdrop
Source Path                   /usr/sbin/postdrop
Port                          <Unknown>
Host                          sanitized
Source RPM Packages           postfix-2.3.3-2
Target RPM Packages
Policy RPM                    selinux-policy-2.4.6-137.1.el5
Selinux Enabled               True
Policy Type                   targeted
MLS Enabled                   True
Enforcing Mode                Enforcing
Plugin Name                   catchall_file
Host Name                     sanitized
Platform                      Linux sanitized 2.6.18-53.1.21.el5 #1 SMP Tue
                              May 20 09:35:07 EDT 2008 x86_64 x86_64
Alert Count                   599
First Seen                    Wed Jul  2 08:27:15 2008
Last Seen                     Sun Aug 10 22:47:52 2008
Local ID                      c303a4ea-8e7a-4acc-9118-9cc61c6a2ec8
Line Numbers
Raw Audit Messages
host=sanitized type=AVC msg=audit(1218397672.372:352): avc:  denied  { getattr } for  pid=4262 comm="postdrop"
path="/var/log/httpd/error_log" dev=md2 ino=117005 scontext=system_u:system_r:postfix_postdrop_t:s0
tcontext=root:object_r:httpd_log_t:s0 tclass=file
host=sanitized type=SYSCALL msg=audit(1218397672.372:352): arch=c000003e syscall=5 success=no exit=-13 a0=2
a1=7fffd6febca0 a2=7fffd6febca0 a3=0 items=0 ppid=4261 pid=4262 auid=4294967295 uid=48 gid=48 euid=48 suid=48
fsuid=48 egid=90 sgid=90 fsgid=90 tty=(none) comm="postdrop" exe="/usr/sbin/postdrop"
subj=system_u:system_r:postfix_postdrop_t:s0 key=(null) 

就上述錯誤執行 audit2allow 並查閱所得的 postfixlocal.te 政策檔時,我們會看見:

# grep postdrop /var/log/audit/audit.log | audit2allow -M postfixlocal
# cat postfixlocal.te
    module postfixlocal 1.0;
    require {
            type httpd_log_t;
            type postfix_postdrop_t;
            class dir getattr;
            class file { read getattr };
    }
    #============= postfix_postdrop_t ==============
    allow postfix_postdrop_t httpd_log_t:file getattr; 

我希望第一件引起你關注的事,就是 postdrop 為何須要存取 /var/log/httpd/error_log?這應該在我們預料之外,因此我們便要評估應否允許這個行動。我們有數個選擇:我們可以忽略這個錯誤並容讓 SELinux 繼續封鎖及記錄這些存取的行動,又或者我們可以允許這些行動並建立 audit2allow 所建議的自訂政策模塊。我們也可以選擇編輯這個自訂政策模塊的 .te 檔並防止審計這個特定錯誤,並讓 SELinux 繼續封鎖該存取權。我們可以編輯 allow 這一行,並改為 dontaudit

    #============= postfix_postdrop_t ==============
    dontaudit postfix_postdrop_t httpd_log_t:file getattr; 

現在我們可以手動地編譯及載入已修改的自訂政策模塊:

# checkmodule -M -m -o postfixlocal.mod postfixlocal.te
# semodule_package -o postfixlocal.pp -m postfixlocal.mod
# semodule -i postfixlocal.pp 

postdrop 存取 /var/log/httpd/error_log 時仍然會被 SELinux 封鎖,但我們再不會在發生封鎖時接收到警報,或在日誌內填滿錯誤信息。

8. 總結

這篇文章嘗試向 SELinux 的新用戶簡介如何應用它。SELinux 預設會被安裝及啟用,對多數用戶來說,它會默默地提高系統的安全性。SELinux 適用於各類型的安裝,包括:伺服器、工作台、桌面電腦、及筆記型電腦。

雖然 SELinux 對不熟識它的用戶來說似乎很複雜及令人生畏,但這不是在安裝時停用它的一個理由。若果 SELinux 確實帶來問題,你可以簡單地將它切換至 Permissive 模式,到時它便會將問題記錄下來,而不會進行封鎖。當問題出現時,這篇文章所示範的技巧可以用來排除疑難及解決它們。

9. 額外資源

http://fedoraproject.org/wiki/SELinux

http://docs.fedoraproject.org/en-US/Fedora/13/html-single/Security-Enhanced_Linux/

http://danwalsh.livejournal.com/

Translation of revision 28

zh-tw/HowTos/SELinux (last edited 2012-04-24 06:15:31 by TimothyLee)