最後更新日期: 2025 年 4 月 26 日
引言
你有沒有想過,為什麼有些 Linux 伺服器被駭客搞亂了還是穩如泰山?這全靠 SELinux(Security-Enhanced Linux),一個超厲害的「安全保鑣」!它不像普通檔案權限(誰能讀、寫、跑程式),SELinux 是「強制訪問控制」(MAC),就像給每個檔案和程式貼上專屬「VIP通行證」。沒這張通行證,就算你是 root
,也別想亂來!
SELinux 是 Red Hat Enterprise Linux (RHEL) 的標準安全武器,特別適合銀行、醫療、雲端這類需要超高安全的伺服器。這篇是 Linux 訪問控制系列的第二篇,專門用簡單語言,從頭教你怎麼用 SELinux。我們從基本概念講起,一步步進階到保護網站、資料庫,甚至自己寫安全規則。本文特別花心思解釋了自訂規則,適合 RHEL 管理員、想學安全的新手,或進階玩家。第一篇講了基本權限和 ACL,第三篇會聊 Ubuntu 的 AppArmor。
這篇會講:
- SELinux 是什麼?為啥這麼強
- SELinux 的三種模式:怎麼開關
- 上下文標籤:VIP通行證的秘密
- 安全策略:怎麼寫規則(特別詳細自訂規則)
- 實務範例:從簡單到高階的玩法
- 問題怎麼解?快速排查技巧
- 結論與進階資源
1. SELinux 是什麼?為啥這麼強
1.1 SELinux 基本概念
SELinux 是美國國家安全局(NSA)搞出來的一個安全工具,2000 年開始用在 Linux,現在是 RHEL 的標準配備。它的全名是「Security-Enhanced Linux」,簡單說就是幫 Linux 穿上防彈衣。
普通檔案權限(像 chmod 755
或 chown
)是「自主訪問控制」(DAC),檔案主人說了算。但這有個問題:如果駭客拿到了 root
權限,或者程式被搞亂,這些權限可能就沒用了。SELinux 是「強制訪問控制」(MAC),用一套系統規則管每個程式和檔案的行為,就算你是 root
,也得乖乖聽話。
1.2 SELinux 怎麼運作?
把你的伺服器想成一棟豪華大樓,每個檔案、程式、網路埠都有張「VIP通行證」(上下文標籤)。SELinux 是大樓保安,會拿你的通行證對照「規則手冊」(安全策略)。如果對不上,就算你有大樓鑰匙(Linux 權限),也進不了房間。
SELinux 的核心有兩塊:
- 上下文標籤:通行證,告訴 SELinux 這是什麼東西(像「網頁檔案」或「Apache 程式」)。
- 安全策略:規則手冊,說誰能碰誰(像「只有 Apache 能讀網頁檔案」)。
1.3 為什麼用 SELinux?
SELinux 在企業伺服器超好用,因為:
- 保護機密資料:銀行資料、醫療紀錄,絕對不能亂碰。
- 擋住駭客:就算駭客拿到權限,SELinux 也能把他們關在小房間裡。
- 符合規範:像 PCI-DSS、HIPAA 這類安全標準,SELinux 幫你過關。
2. SELinux 的三種模式:怎麼開關
2.1 SELinux 的三種模式
SELinux 有三種模式,像電燈開關的三個檔:
- Enforcing(強制模式):嚴格檢查通行證,沒證就擋。
- Permissive(寬鬆模式):只記錄誰沒證,不擋(適合試驗)。
- Disabled(關閉):把 SELinux 關掉(不建議,除非你有特殊理由)。
檢查現在是哪個模式:
getenforce
2.2 臨時切換模式
想看看 SELinux 怎麼影響系統?試試臨時切換:
setenforce 0 # 切到 Permissive
setenforce 1 # 切回 Enforcing
這只在當前開機有效,重啟就恢復原設定。
2.3 永久改模式
要永久改,編輯 /etc/selinux/config
:
SELINUX=enforcing
SELINUXTYPE=targeted
SELINUX
:選enforcing
、permissive
或disabled
。SELINUXTYPE
:通常是targeted
,只管特定程式(像 Apache、MySQL)。
改完重啟:
reboot
2.4 開啟 SELinux
如果 SELinux 是關的,開啟要:
-
- 改
/etc/selinux/config
,設SELINUX=enforcing
。 - 幫檔案重新貼通行證:
- 改
touch /.autorelabel
reboot
2.5 簡單試試
檢查 SELinux 狀態:
getenforce
如果顯示 Enforcing
,試切到 Permissive
:
setenforce 0
getenforce # 顯示 Permissive
3. 上下文標籤:VIP通行證的秘密
上下文標籤是 SELinux 的心臟,像是給每個檔案、程式、網路埠貼上的專屬「VIP通行證」。它決定誰能碰誰,是 SELinux 強制訪問控制(MAC)的核心。想像你在大樓裡,保安不只看你的鑰匙(Linux 權限),還要檢查你的通行證(上下文標籤)。如果標籤不對,就算你有鑰匙也進不了房間!
上下文標籤不只是身份標記,它還是 SELinux 執行「類型強制」(Type Enforcement, TE)和「基於角色的訪問控制」(RBAC)的基礎。每個標籤都像一張詳細的身份證,告訴 SELinux 這個東西的角色、用途和安全等級。標籤錯了,程式可能被擋,比如 Apache 讀不了網頁檔案,或 MySQL 存不了資料。這節我們會深入講標籤的結構、作用,還會詳細拆解兩個改標籤的關鍵工具:chcon
和 semanage fcontext
,教你怎麼讓程式順利跑!
3.1 標籤的四個部分
每個上下文標籤長這樣:
unconfined_u:object_r:httpd_sys_content_t:s0
它有四部分,像是通行證上的四欄資料,各自在 SELinux 策略中扮演不同角色:
- SELinux 用戶(unconfined_u):這是 SELinux 自己的用戶身份,不是 Linux 的
alice
或root
。它標記資源或程式的「安全身份」,與 Linux 用戶通過映射關聯(用semanage login
管理)。常見的有: unconfined_u
:普通用戶或不受限程式,適合一般場景,權限較寬鬆。system_u
:系統服務(如 Apache、MySQL),用於關鍵任務,限制較嚴。root
:對應 Linux 的 root,但仍受 SELinux 策略約束。- 角色(object_r):像是「職位」,定義資源或程式的角色,支援 SELinux 的基於角色訪問控制(RBAC)。常見角色有:
object_r
:檔案、目錄等靜態資源,標記它們是「物件」。system_r
:系統程式,如 Apache 或 MySQL,標記它們是「進程」。unconfined_r
:不受限角色,給普通用戶或程式,限制較少。- 類型(httpd_sys_content_t):最重要的部分,決定存取規則,是 SELinux 類型強制(Type Enforcement, TE)的核心。類型像「部門標籤」,標記資源或進程的用途。常見類型有:
httpd_sys_content_t
:網頁檔案,給 Apache 讀。httpd_t
:Apache 程式本身。mysqld_db_t
:MySQL 資料庫檔案。user_home_t
:用戶家目錄檔案,給普通用戶用。- 級別(s0):用於「多級安全」(MLS, Multi-Level Security)或「多類別安全」(MCS, Multi-Category Security),標記資源或進程的安全等級或類別。在普通 RHEL 環境(
targeted
策略),通常是s0
,可以先不用管。但在高安全場景(像軍事或金融系統),可能用s1
(高敏感)或類別(c0.c1023
)來隔離資料,比如分開不同容器的檔案。
SELinux 用戶決定進程或資源的「安全範圍」,比如 system_u
通常只給系統級服務,防止普通用戶濫用。
角色像個中間層,限制哪些 SELinux 用戶能用哪些類型,確保進程和資源的交互符合策略。比如,system_r
通常只給 system_u
的進程,防止普通用戶跑服務。
SELinux 策略主要靠類型決定「誰能碰誰」,比如允許 httpd_t
讀 httpd_sys_content_t
,但不許讀 mysqld_db_t
。類型錯了,存取就被擋!
級別讓 SELinux 能做進階隔離,比如確保機密檔案(s1
)只能被高權限進程讀,或用 MCS 隔開容器資料。
為啥重要? 上下文標籤是 SELinux 安全的基石。它們不只是身份標記,而是 SELinux 執行策略的關鍵。標籤決定了進程(像 httpd_t
)和資源(像 httpd_sys_content_t
)的交互是否允許。如果標籤錯了,SELinux 會無情擋下,即使 Linux 權限(rwxr-xr-x
)允許。比如,Apache 想讀一個標成 user_home_t
的檔案,SELinux 會說:「通行證不對,滾!」這確保了即使 root
或駭客入侵,系統仍受保護。
3.2 怎麼看標籤?
想知道檔案、程式或網路埠的標籤?用以下命令檢查:
- 檔案:用
ls -Z
看檔案的上下文標籤:
ls -Z /var/www/html/index.html
輸出:
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
這表示 index.html
是網頁檔案,Apache 可以讀。
ps -Z
看運行程式的標籤:ps -Z | grep httpd
輸出:
system_u:system_r:httpd_t:s0 5678 ? 00:00:00 httpd
這表示 Apache 程式跑在 httpd_t
類型。
semanage port -l
看埠的標籤:semanage port -l | grep http
輸出:
http_port_t tcp 80, 443, 488, 8008, 8009, 8443
這表示 HTTP 相關埠(80、443 等)屬於 http_port_t
。
這些標籤就像身份證,告訴 SELinux 每個東西的角色和權限。如果標籤不對(比如檔案標成 default_t
而不是 httpd_sys_content_t
),程式可能被擋,這時候就得改標籤!
3.3 怎麼改標籤?深入 chcon 與 semanage fcontext
當標籤不對(比如檔案標成 default_t
,Apache 讀不了),你得幫它換張正確的通行證。SELinux 提供了兩個主力工具:chcon
和 semanage fcontext
,搭配 restorecon
,讓你靈活改標籤。這兩個命令是管理 SELinux 標籤的關鍵,但用法和效果差很大。讓我們來深入拆解它們的功能、選項、場景,還會教你怎麼避免踩坑!
3.3.1 chcon:快速臨時改標籤
什麼是 chcon? chcon
(change context)是一個快速修改 SELinux 上下文標籤的命令,像是在通行證上蓋個臨時章。它能改檔案、目錄或程式的用戶、角色、類型、級別,操作簡單,適合快速測試或緊急修問題。但它的改變是臨時的,系統重啟或檔案系統重新標記(用 restorecon
或系統自動還原)會恢復預設標籤。
語法:
chcon [選項] [標籤] [檔案或目錄]
最常用的選項是改類型(-t
),因為類型是 SELinux 存取控制的核心:
chcon -t httpd_sys_content_t /custom/web/index.html
這把 index.html
的類型改成 httpd_sys_content_t
,讓 Apache 能讀。
常用選項:
-t [類型]
:改類型(如httpd_sys_content_t
)。-u [用戶]
:改 SELinux 用戶(如system_u
)。-r [角色]
:改角色(如object_r
)。-l [級別]
:改安全等級(如s0
或s0:c0.c1023
)。-R
:遞迴改,適用於目錄和所有子檔案。--reference=[參考檔案]
:複製另一檔案的標籤,超方便。
範例 1:簡單改類型:假設 Apache 讀不了 /custom/web/index.html
:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
這檔案的類型是 default_t
,Apache(httpd_t
)沒權限讀。改標籤:
chcon -t httpd_sys_content_t /custom/web/index.html
檢查:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
Apache 現在應該能讀了!
範例 2:遞迴改目錄:假設整個 /custom/web
目錄都要給 Apache 用:
chcon -R -t httpd_sys_content_t /custom/web
這會把目錄和所有子檔案的類型改成 httpd_sys_content_t
。
範例 3:複製標籤:想讓 /custom/web/index.html
的標籤跟 /var/www/html/index.html
一樣:
ls -Z /var/www/html/index.html
輸出:
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
複製標籤:
chcon --reference=/var/www/html/index.html /custom/web/index.html
檢查:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
這招超快,適合標籤已知的場景!
進階用法:改完整標籤(用戶、角色、類型、級別):
chcon -u system_u -r object_r -t httpd_sys_content_t -l s0 /custom/web/index.html
這把標籤全改,但大多數情況只改類型(-t
)就夠,因為類型是 SELinux 存取控制的核心。
性能考量:
chcon
對單檔案很快,但遞迴改大目錄(-R
)可能慢,尤其在檔案多時。測試時先改單檔案,確認 OK 再遞迴。- 改標籤會馬上生效,無需重啟服務,但程式可能需要重新載入(像
systemctl restart httpd
)。
注意事項:
- 臨時性:用
chcon
改的標籤不持久,系統重啟、跑restorecon
,或檔案系統自動還原(像relabel
)會恢復預設標籤。 - 適合快速測試:想試標籤對不對?
chcon
是最佳選擇,但正式環境要用semanage fcontext
做永久設定。 - 小心亂改:改錯標籤可能讓程式跑不了,比如把網頁檔案標成
mysqld_db_t
,Apache 會被擋。改前記下原標籤(ls -Z
)。 - 權限需求:通常需要
root
或sudo
。
3.3.2 semanage fcontext:永久改標籤
什麼是 semanage fcontext? semanage fcontext
是 SELinux 的高級管理工具,用來永久設定檔案或目錄的上下文標籤。它不像 chcon
的臨時蓋章,而是把標籤規則寫進 SELinux 的「上下文資料庫」(通常在 /etc/selinux/targeted/contexts/files/
),確保系統重啟或檔案系統重新標記也不會丟。搭配 restorecon
,能將規則套用到檔案,實現長期穩定的標籤管理。
語法:
semanage fcontext [選項] [檔案路徑模式]
最常用的操作是新增規則,指定類型:
semanage fcontext -a -t [類型] [檔案路徑模式]
-a
表示新增規則,-t
指定類型,路徑模式用正規表達式(像 (/.*)?
匹配所有子檔案)。新增後,用 restorecon
套用:
semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
restorecon -R -v /custom/web
這把 /custom/web
和所有子檔案的類型永久設成 httpd_sys_content_t
。
常用選項:
-a
:新增規則(最常用)。-m
:修改現有規則(而不是新增)。-d
:刪除規則。-s [用戶]
:指定 SELinux 用戶(如system_u
)。-r [角色]
:指定角色(如object_r
)。-l
:列出所有上下文規則。
範例 1:單檔案永久標籤:假設你把一個網頁檔案 /custom/web/index.html
移到非標準路徑,Apache 讀不了:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
永久設標籤:
semanage fcontext -a -t httpd_sys_content_t "/custom/web/index.html"
restorecon -v /custom/web/index.html
輸出:
Restored context on /custom/web/index.html
檢查:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
Apache 現在能讀了,且標籤永久有效!
範例 2:目錄遞迴標籤:假設 MySQL 資料庫搬到 /data/mysql
,但標籤錯了:
ls -Zd /data/mysql
輸出:
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /data/mysql
永久設標籤:
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
restorecon -R -v /data/mysql
輸出:
Restored context on /data/mysql
Restored context on /data/mysql/data1
...
檢查:
ls -Zd /data/mysql
輸出:
drwxr-xr-x. root root system_u:object_r:mysqld_db_t:s0 /data/mysql
MySQL 現在能正常跑了!
範例 3:容器場景:假設你用 Podman 跑一個容器,資料放在 /container/data
,需要標成 container_file_t
:
semanage fcontext -a -t container_file_t "/container/data(/.*)?"
restorecon -R -v /container/data
這確保容器能存取資料,且標籤與 SELinux 的 MCS 隔離相容。
進階用法:
- 列出規則:檢查現有上下文規則:
semanage fcontext -l | grep /custom/web
輸出:
/custom/web(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
-m
:semanage fcontext -m -t httpd_sys_rw_content_t "/custom/web(/.*)?"
restorecon -R -v /custom/web
這改成可讀寫的標籤(httpd_sys_rw_content_t
)。
semanage fcontext -d "/custom/web(/.*)?"
restorecon -R -v /custom/web
這會還原預設標籤(可能回到 default_t
)。
semanage fcontext -a -s system_u -r object_r -t httpd_sys_content_t "/custom/web(/.*)?"
性能考量:
semanage fcontext
只改資料庫,不影響檔案,直到跑restorecon
。對大目錄,restorecon -R
可能慢,建議先改單檔案測試。- 規則資料庫(
/etc/selinux/
)很小,新增規則幾乎不影響性能,但大量規則可能增加管理複雜度。
注意事項:
- 必須用 restorecon:用
semanage fcontext
設規則後,需跑restorecon
套用到檔案,否則標籤不會變。 - 正規表達式:路徑模式要寫對,比如
(/.*)?
匹配所有子檔案,單檔案不用加。錯了可能影響其他檔案。 - 權限需求:需要
root
或sudo
,因為改的是系統級規則。 - 備份規則:改前備份 SELinux 資料庫,萬一搞亂可以還原:
semanage export -f selinux_backup.conf
semanage fcontext -l
檢查,確保不加重複規則,否則可能報錯。3.3.3 chcon vs. semanage fcontext:怎麼選?
chcon
和 semanage fcontext
都是改標籤,但用途和效果差別很大。以下是它們的比較:
比較點 | chcon | semanage fcontext |
---|---|---|
持久性 | 臨時,重啟或 restorecon 還原 |
永久,寫進 SELinux 資料庫 |
速度 | 快,立即生效 | 稍慢,需搭配 restorecon |
適用場景 | 快速測試、緊急修復 | 正式環境、長期設定 |
靈活性 | 可改單檔案或目錄,支援複製標籤 | 用正規表達式,適合批量管理 |
複雜度 | 簡單,一行命令 | 稍複雜,需兩步(設規則+套用) |
建議流程:
- 用
chcon
測試標籤,確認程式能跑(像 Apache 能讀檔案)。 - 用
semanage fcontext
和restorecon
設永久規則,確保長期穩定。
範例:測試到永久:Apache 讀不了 /custom/web
:
# 先用 chcon 測試
chcon -R -t httpd_sys_content_t /custom/web
curl http://localhost/index.html # 確認能讀
# 設永久規則
semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
restorecon -R -v /custom/web
curl http://localhost/index.html # 再確認
這流程結合兩者的優點:快速試驗(chcon
)+穩定設定(semanage fcontext
)。
3.3.4 進階場景:複雜標籤管理
在一些高階場景,標籤管理可能更複雜,比如容器、第三方軟體或多用戶環境。以下是兩個例子:
- 容器隔離:假設你用 Podman 跑多個容器,資料在
/container/app1
和/container/app2
,需要不同 MCS 類別隔離:
semanage fcontext -a -t container_file_t -l s0:c1 "/container/app1(/.*)?"
semanage fcontext -a -t container_file_t -l s0:c2 "/container/app2(/.*)?"
restorecon -R -v /container/app1
restorecon -R -v /container/app2
這用 MCS 類別(c1
、c2
)隔開容器資料,確保 app1 和 app2 互不干擾。
/opt/custom_service
,需要標成 bin_t
:chcon -t bin_t /opt/custom_service # 先測試
/opt/custom_service # 確認能跑
semanage fcontext -a -t bin_t "/opt/custom_service"
restorecon -v /opt/custom_service
3.3.5 排查標籤問題
標籤設錯可能讓程式跑不了,怎麼修?
- 檢查日誌:看 SELinux 為啥擋(AVC 拒絕):
ausearch -m avc --since today
輸出可能顯示類型不對(像 default_t
而不是 httpd_sys_content_t
)。
setenforce 0
curl http://localhost/index.html # 能讀?是標籤問題
setenforce 1
restorecon -R -v /custom/web
這會根據系統上下文資料庫還原標籤(可能回到 default_t
)。
semanage fcontext
的規則對不對:semanage fcontext -l | grep /custom/web
df -T /custom/web
3.3.6 安全最佳實踐
改標籤是門技術活,別亂搞!以下是最佳實踐:
- 最小權限原則:只改必要標籤,別把所有檔案標成
httpd_sys_content_t
,可能讓不該讀的程式也能讀。 - 記錄變更:用日誌或腳本記下改了什麼標籤,方便追蹤:
echo "chcon -t httpd_sys_content_t /custom/web" >> selinux_changes.log
semanage fcontext -l | grep -v "/usr\|/var\|/etc"
3.3.7 chcon 和 semanage fcontext 的工作流程
為了讓你徹底搞懂,這裡是個標準工作流程:
- 程式跑不了(像 Apache 讀不了
/custom/web
),檢查標籤: - 用
chcon
臨時改,測試效果: - 確認 OK 後,用
semanage fcontext
設永久規則: - 再次測試,確保長期生效:
- 記錄變更,備份規則:
ls -Z /custom/web
chcon -R -t httpd_sys_content_t /custom/web
curl http://localhost/index.html
semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
restorecon -R -v /custom/web
curl http://localhost/index.html
semanage export -f selinux_backup.conf
3.4 簡單範例
讓我們用一個實例,把這些知識用起來!假設 Apache 讀不了 /custom/web/index.html
:
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
這檔案是 default_t
,Apache(httpd_t
)沒權限讀。先試臨時修:
chcon -t httpd_sys_content_t /custom/web/index.html
curl http://localhost/index.html # 確認能讀
成功後設永久規則:
semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
restorecon -R -v /custom/web
ls -Z /custom/web/index.html
輸出:
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
現在 Apache 能穩定讀檔案,標籤也不會丟!
4. 安全策略:怎麼寫規則
安全策略是 SELinux 的「規則手冊」,決定哪些程式能碰哪些東西。RHEL 常用的策略是 targeted,只限制特定程式(像 Apache、MySQL),其他程式不受影響。
4.1 什麼是安全策略?
策略是一堆規則,告訴 SELinux 誰能幹什麼。比如:
- Apache(
httpd_t
)能讀httpd_sys_content_t
檔案。 - MySQL(
mysqld_t
)能寫mysqld_db_t
資料。
檢查策略:
sestatus
4.2 預設規則
RHEL 內建了很多規則,給常見程式用,比如:
- Apache 能讀
/var/www/html
。 - MySQL 能寫
/var/lib/mysql
。
檢查規則:
sepolicy network -t httpd_t
4.3 自訂規則:教 SELinux 學新招
有時候,預設規則不夠用。比如,你把網頁檔案放到了 /data/web
(不是標準的 /var/www/html
),或者你寫了一個新程式,跑在怪怪的埠上,SELinux 會跳出來擋,說:「這不在我的規則手冊裡,滾!」這時候,你得自己加一頁新規則,教 SELinux 怎麼處理。
簡單說:自訂規則就像給 SELinux 的規則手冊加筆記,告訴它「這個程式可以做這個事」。這需要三個超重要的工具:ausearch
、audit2allow
和 semodule
。讓我們來深入拆解它們怎麼用,幫你打造完美的自訂規則!
4.3.1 為什麼需要自訂規則?
RHEL 的 targeted
策略已經幫 Apache、MySQL 等程式寫好規則,但如果你:
- 用非標準目錄(像
/data/web
而不是/var/www/html
)。 - 跑自訂程式(像
/opt/myapp
)。 - 需要特殊權限(像 Apache 連資料庫的 5432 埠)。
SELinux 可能會擋住,記錄在日誌(/var/log/audit/audit.log
)的「AVC 拒絕」(Access Vector Cache Denial),像是說:「嘿,你想幹的事,手冊沒寫可以!」這時候,你可以:
- 搬回標準路徑(像
/var/www/html
)。 - 改標籤(用
chcon
或semanage fcontext
)。 - 寫自訂規則,補充手冊。
自訂規則是用 ausearch
找問題、audit2allow
寫規則、semodule
裝規則的完美組合。下面我們來詳細講這三個工具!
4.3.2 ausearch:找出誰被 SELinux 擋
什麼是 ausearch? ausearch
是 SELinux 的「偵探」,專門從日誌(/var/log/audit/audit.log
)挖出 AVC 拒絕訊息,告訴你哪個程式被擋、為什麼被擋。它是自訂規則的第一步,因為你得先知道問題出在哪!
怎麼用? ausearch
的基本語法是:
ausearch [選項]
最常用的選項是找 AVC 拒絕:
ausearch -m avc --since today
-m avc
指只看 AVC 拒絕,--since today
限今天的事件,方便過濾。
其他選項:
-ts [時間]
:指定時間範圍(像-ts yesterday
)。-p [PID]
:只看某個程式的 PID。-c [程式名]
:只看某程式(像-c httpd
)。-se [類型]
:過濾特定 SELinux 類型(像-se httpd_t
)。
範例:假設 Apache(httpd
)想讀 /data/web/index.html
,但被 SELinux 擋住:
ausearch -m avc --since today
輸出可能像:
type=AVC msg=audit(1743350400.123:456): avc: denied { read } for pid=5678 comm="httpd" path="/data/web/index.html" dev="sda1" ino=123456 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0
白話解釋:
httpd
(Apache,標籤httpd_t
)想讀/data/web/index.html
(標籤default_t
)。- SELinux 說:「規則手冊沒說
httpd_t
能讀default_t
,擋!」 permissive=0
表示在 Enforcing 模式,真的被擋了。
進階用法:想只看 Apache 的拒絕事件:
ausearch -m avc -c httpd --since today
或看某個時間點的事件:
ausearch -m avc -ts "2025-04-26 10:00:00"
注意事項:
-
- 需要 auditd:沒開
auditd
服務,沒日誌可查。確認:
- 需要 auditd:沒開
systemctl status auditd
-
- 精準過濾:日誌可能很長,用
-c
或-p
縮小範圍,省時間。 - Permissive 模式:如果 Enforcing 模式沒日誌,切到 Permissive 再試:
- 精準過濾:日誌可能很長,用
setenforce 0
4.3.3 audit2allow:把日誌變成規則
什麼是 audit2allow? audit2allow
是 SELinux 的「翻譯官」,把 ausearch
挖出的 AVC 拒絕日誌轉成 SELinux 能懂的規則。它會告訴你:「這問題可以這樣修!」然後幫你生成一個模組檔案,準備裝進 SELinux。
怎麼用? audit2allow
的基本語法是:
audit2allow [選項]
最常用是直接轉日誌:
ausearch -m avc --since today | audit2allow
這會把日誌變成規則建議。想生成模組檔案,加 -M
:
ausearch -m avc --since today | audit2allow -M customweb
-M customweb
指生成名為 customweb
的模組,會輸出兩個檔案:
-
-
customweb.te
:規則的原始碼(純文字,可編輯)。customweb.pp
:編譯好的模組(給 SELinux 用)。
-
其他選項:
-
-
-a
:讀全部日誌(不只輸入的日誌)。-R
:自動包含相關規則,生成更完整的模組。-o [輸出檔案]
:指定輸出檔案名。-w
:顯示為什麼需要這條規則(解釋拒絕原因)。
-
範例:延續上面的 Apache 問題,假設日誌顯示 Apache 被擋讀 /data/web/index.html
:
ausearch -m avc -c httpd --since today | audit2allow
輸出:
#============= httpd_t ==============
allow httpd_t default_t:file read;
這說:「讓 httpd_t
能讀 default_t
檔案。」
生成模組:
ausearch -m avc -c httpd --since today | audit2allow -M customweb
看看生成的 customweb.te
:
cat customweb.te
內容:
module customweb 1.0;
require {
type httpd_t;
type default_t;
class file read;
}
allow httpd_t default_t:file read;
白話解釋:
-
-
module customweb 1.0
:這是新模組,版本 1.0。require
:列出需要的類型(httpd_t
、default_t
)和權限(file read
)。allow
:說「Apache(httpd_t
)可以讀default_t
檔案」。
-
進階用法:想知道為什麼被擋,加 -w
:
ausearch -m avc -c httpd --since today | audit2allow -w
輸出可能像:
This avc is caused by httpd_t attempting to read a file labeled default_t, which is not allowed by the current policy.
或生成更完整的規則:
ausearch -m avc -c httpd --since today | audit2allow -R -M customweb
-R
會自動拉相關規則,可能生成更複雜的模組,適合多重拒絕。
注意事項:
-
-
- 規則可能太寬鬆:像允許
httpd_t
讀default_t
,可能不安全。檢查.te
檔案,改用具體類型(像httpd_sys_content_t
)。 - 日誌品質:沒日誌就沒規則,確保
auditd
開著,程式有跑。 - 手動編輯:生成的
.te
可能不完美,手動改前備份。
- 規則可能太寬鬆:像允許
-
4.3.4 semodule:把規則裝進 SELinux
什麼是 semodule? semodule
是 SELinux 的「安裝工」,把 audit2allow
生成的模組(.pp
檔案)裝進 SELinux 的規則手冊。它讓你的自訂規則正式生效,成為 SELinux 策略的一部分。
怎麼用? semodule
的基本語法是:
semodule [選項]
最常用是安裝模組:
semodule -i [模組檔案].pp
延續上面的例子,安裝 customweb
模組:
semodule -i customweb.pp
這會把規則加進 SELinux,馬上生效。
其他選項:
-
-
-r [模組名]
:移除模組(像semodule -r customweb
)。-l
:列出已安裝模組。-e [檔案]
:匯出所有模組備份。-u [模組檔案].pp
:更新現有模組。
-
範例:安裝 customweb
模組後,檢查是否生效:
semodule -l | grep customweb
輸出:
customweb 1.0
重啟 Apache,確認能讀 /data/web/index.html
:
systemctl restart httpd
curl http://localhost/index.html
進階用法:管理多個模組或修問題:
-
-
- 移除舊模組(如果衝突):
-
semodule -r customweb
-
-
- 匯出備份:
-
semodule -e selinux_backup.conf
-
-
- 匯入備份:
-
semodule -i selinux_backup.conf
注意事項:
-
-
- 模組衝突:新模組可能跟舊的打架,檢查現有模組:
-
semodule -l
-
-
- 權限需求:需要
root
或sudo
。 - 備份策略:安裝新模組前備份,萬一亂了可以還原。
- 測試效果:裝完模組後重啟相關服務,確認問題解決。
- 權限需求:需要
-
4.3.5 完整範例:從日誌到規則
讓我們把這三個工具串起來,解決 Apache 連 PostgreSQL(5432 埠)的問題!假設 Apache 被擋住連線:
-
-
- 找問題(ausearch):看日誌:
-
ausearch -m avc -c httpd --since today
輸出:
type=AVC msg=audit(1743350400.789:789): avc: denied { connect } for pid=5678 comm="httpd" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:postgresql_port_t:s0 tclass=tcp_socket permissive=0
這說 Apache(httpd_t
)想連 postgresql_port_t
(5432 埠),但被擋。
-
-
- 想辦法(audit2allow):生成規則:
-
ausearch -m avc -c httpd --since today | audit2allow -M httpd_db
看看 httpd_db.te
:
cat httpd_db.te
內容:
module httpd_db 1.0;
require {
type httpd_t;
type postgresql_port_t;
class tcp_socket connect;
}
allow httpd_t postgresql_port_t:tcp_socket connect;
這說:「讓 Apache 能連 PostgreSQL 埠。」
-
-
- 裝規則(semodule):安裝模組:
-
semodule -i httpd_db.pp
檢查模組:
semodule -l | grep httpd_db
輸出:
httpd_db 1.0
-
-
- 試試看:重啟 Apache,確認連線:
-
systemctl restart httpd
如果連線成功,恭喜!問題解決。
4.3.6 進階場景:複雜規則
假設你有個自訂程式 /opt/myapp
,要讀寫 /data/app
和用 8080 埠,但被多重拒絕擋住:
-
-
- 設標籤(先試標籤修法):
-
semanage fcontext -a -t bin_t "/opt/myapp(/.*)?"
restorecon -R -v /opt/myapp
semanage fcontext -a -t myapp_data_t "/data/app(/.*)?"
restorecon -R -v /data/app
semanage port -a -t http_port_t -p tcp 8080
-
-
- 找日誌:檢查剩哪些問題:
-
ausearch -m avc -c myapp --since today
-
-
- 生成完整規則:用
-R
拉相關規則:
- 生成完整規則:用
-
ausearch -m avc -c myapp --since today | audit2allow -R -M myapp
檢查 myapp.te
,可能像:
module myapp 1.0;
require {
type myapp_t;
type myapp_data_t;
type http_port_t;
class file { read write };
class tcp_socket { connect };
}
allow myapp_t myapp_data_t:file { read write };
allow myapp_t http_port_t:tcp_socket connect;
-
-
- 安裝並測試:
-
semodule -i myapp.pp
/opt/myapp # 跑程式,確認正常
4.3.7 別踩坑:常見問題怎麼避
-
-
- 太寬鬆:生成的規則可能允許過多權限(像
default_t
),不安全。
- 太寬鬆:生成的規則可能允許過多權限(像
-
解法:檢查 .te
,用具體類型(像 httpd_sys_content_t
),必要時手動改。
-
-
- 多重拒絕:修了一個問題,又冒出新的。
-
解法:用 audit2allow -R
或多跑幾次 ausearch
,抓所有問題。
-
-
- 沒日誌:沒開
auditd
或程式沒跑。
- 沒日誌:沒開
-
解法:確認 auditd
和程式狀態:
systemctl status auditd
ps aux | grep myapp
-
-
- 模組衝突:新模組跟舊的打架。
-
解法:檢查現有模組,移除衝突:
semodule -l
semodule -r old_module
4.3.8 怎麼寫出好規則?
-
-
- 先試寬鬆模式:新伺服器設
setenforce 0
,收集日誌再加規則。 - 具體規則:用明確類型和權限,別用泛型(像
default_t
)。 - 取好名字:模組叫
httpd_custom
或myapp_policy
,方便管理。 - 備份:用
semodule -e
存備份,亂了能還原。 - 試驗場:用虛擬機試,別搞亂正式伺服器。
- 先試寬鬆模式:新伺服器設
-
5. 實務範例:從簡單到高階的玩法
5.1 簡單範例:修 Apache 讀檔
場景:Apache 讀不了 /var/www/html/custom/index.html
。
mkdir /var/www/html/custom
echo "Test" > /var/www/html/custom/index.html
ls -Z /var/www/html/custom/index.html
輸出:
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
修復:
chcon -t httpd_sys_content_t /var/www/html/custom/index.html
semanage fcontext -a -t httpd_sys_content_t "/var/www/html/custom(/.*)?"
restorecon -v /var/www/html/custom/index.html
systemctl restart httpd
5.2 中階範例:保護 MySQL 資料庫
場景:MySQL 資料搬到 /data/mysql
,啟動失敗。
mkdir /data/mysql
chown mysql:mysql /data/mysql
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
restorecon -R -v /data/mysql
systemctl restart mysqld
systemctl status mysqld
5.3 高階範例:自訂應用程式
場景:程式 /opt/myapp
用 8080 埠,讀寫 /data/app
。
semanage fcontext -a -t bin_t "/opt/myapp(/.*)?"
restorecon -R -v /opt/myapp
semanage fcontext -a -t myapp_data_t "/data/app(/.*)?"
restorecon -R -v /data/app
semanage port -a -t http_port_t -p tcp 8080
ausearch -m avc -ts today | audit2allow -M myapp
semodule -i myapp.pp
6. 問題怎麼解?快速排查技巧
6.1 常見麻煩
-
-
- 程式跑不動:SELinux 可能擋住檔案或埠。
- 檔案讀不了:標籤可能錯了。
- 怪錯誤:Linux 權限 OK,但 SELinux 說不行。
-
6.2 排查步驟
-
-
- 看日誌:
-
ausearch -m avc --since today
-
-
- 試寬鬆模式:
-
setenforce 0
問題沒了?就是 SELinux 的鍋。
-
-
- 分析原因:
-
ausearch -m avc -ts today | audit2why
-
-
- 修好後切回強制模式:
-
setenforce 1
6.3 範例:修 Apache 錯誤
Apache 讀不了 /data/web
:
journalctl -xe | grep avc
ausearch -m avc -ts today
chcon -R -t httpd_sys_content_t /data/web
semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?"
restorecon -R -v /data/web
6.4 避免麻煩
-
-
- 新伺服器用
Permissive
模式,先看日誌。 - 只給必要權限,別開大門。
- 常看
/var/log/audit/audit.log
。 - 改規則前備份。
- 用 Ansible 自動化管理。
- 新伺服器用
-
7. 結論與進階資源
SELinux 就像一個超嚴的保安,雖然一開始可能有點麻煩,但它能讓你的 RHEL 伺服器變成安全堡壘!這篇用簡單語言,從什麼是 SELinux、怎麼設標籤、怎麼寫規則(特別是自訂規則),到怎麼修問題,帶你從新手到進階。第一篇講了基礎權限和 ACL,幫你了解「自主控制」;這篇專注 SELinux 的「強制控制」;下一篇會講 Ubuntu 的 AppArmor,教你另一種簡單的安全方式。
想學更多?這裡有些好資源:
-
-
- SELinux 官方文件:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux
- SELinux 維基:https://selinuxproject.org/page/Main_Page
- 好書:《SELinux by Example》by Frank Mayer 等
- Red Hat 安全指南:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/security_hardening
-
希望這篇讓你對 SELinux 從害怕變成愛用!有問題或想看更多範例,隨時留言。下一篇文章會講 AppArmor,別錯過哦!