SELinux專業指南 打造RHEL伺服器的終極安全堡壘

最後更新日期: 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 755chown)是「自主訪問控制」(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:選 enforcingpermissivedisabled
  • SELINUXTYPE:通常是 targeted,只管特定程式(像 Apache、MySQL)。

改完重啟:

reboot

2.4 開啟 SELinux

如果 SELinux 是關的,開啟要:

    1. /etc/selinux/config,設 SELINUX=enforcing
    2. 幫檔案重新貼通行證:
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 存不了資料。這節我們會深入講標籤的結構、作用,還會詳細拆解兩個改標籤的關鍵工具:chconsemanage fcontext,教你怎麼讓程式順利跑!

3.1 標籤的四個部分

每個上下文標籤長這樣:

unconfined_u:object_r:httpd_sys_content_t:s0

它有四部分,像是通行證上的四欄資料,各自在 SELinux 策略中扮演不同角色:

  • SELinux 用戶(unconfined_u):這是 SELinux 自己的用戶身份,不是 Linux 的 aliceroot。它標記資源或程式的「安全身份」,與 Linux 用戶通過映射關聯(用 semanage login 管理)。常見的有:
    • unconfined_u:普通用戶或不受限程式,適合一般場景,權限較寬鬆。
    • system_u:系統服務(如 Apache、MySQL),用於關鍵任務,限制較嚴。
    • root:對應 Linux 的 root,但仍受 SELinux 策略約束。

    SELinux 用戶決定進程或資源的「安全範圍」,比如 system_u 通常只給系統級服務,防止普通用戶濫用。

  • 角色(object_r):像是「職位」,定義資源或程式的角色,支援 SELinux 的基於角色訪問控制(RBAC)。常見角色有:
    • object_r:檔案、目錄等靜態資源,標記它們是「物件」。
    • system_r:系統程式,如 Apache 或 MySQL,標記它們是「進程」。
    • unconfined_r:不受限角色,給普通用戶或程式,限制較少。

    角色像個中間層,限制哪些 SELinux 用戶能用哪些類型,確保進程和資源的交互符合策略。比如,system_r 通常只給 system_u 的進程,防止普通用戶跑服務。

  • 類型(httpd_sys_content_t):最重要的部分,決定存取規則,是 SELinux 類型強制(Type Enforcement, TE)的核心。類型像「部門標籤」,標記資源或進程的用途。常見類型有:
    • httpd_sys_content_t:網頁檔案,給 Apache 讀。
    • httpd_t:Apache 程式本身。
    • mysqld_db_t:MySQL 資料庫檔案。
    • user_home_t:用戶家目錄檔案,給普通用戶用。

    SELinux 策略主要靠類型決定「誰能碰誰」,比如允許 httpd_thttpd_sys_content_t,但不許讀 mysqld_db_t。類型錯了,存取就被擋!

  • 級別(s0):用於「多級安全」(MLS, Multi-Level Security)或「多類別安全」(MCS, Multi-Category Security),標記資源或進程的安全等級或類別。在普通 RHEL 環境(targeted 策略),通常是 s0,可以先不用管。但在高安全場景(像軍事或金融系統),可能用 s1(高敏感)或類別(c0.c1023)來隔離資料,比如分開不同容器的檔案。
  • 級別讓 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 提供了兩個主力工具:chconsemanage 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 [級別]:改安全等級(如 s0s0: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)。
  • 權限需求:通常需要 rootsudo

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 套用到檔案,否則標籤不會變。
  • 正規表達式:路徑模式要寫對,比如 (/.*)? 匹配所有子檔案,單檔案不用加。錯了可能影響其他檔案。
  • 權限需求:需要 rootsudo,因為改的是系統級規則。
  • 備份規則:改前備份 SELinux 資料庫,萬一搞亂可以還原:
  • semanage export -f selinux_backup.conf
  • 避免重複規則:用 semanage fcontext -l 檢查,確保不加重複規則,否則可能報錯。

3.3.3 chcon vs. semanage fcontext:怎麼選?

chconsemanage fcontext 都是改標籤,但用途和效果差別很大。以下是它們的比較:

比較點 chcon semanage fcontext
持久性 臨時,重啟或 restorecon 還原 永久,寫進 SELinux 資料庫
速度 快,立即生效 稍慢,需搭配 restorecon
適用場景 快速測試、緊急修復 正式環境、長期設定
靈活性 可改單檔案或目錄,支援複製標籤 用正規表達式,適合批量管理
複雜度 簡單,一行命令 稍複雜,需兩步(設規則+套用)

建議流程

  1. chcon 測試標籤,確認程式能跑(像 Apache 能讀檔案)。
  2. semanage fcontextrestorecon 設永久規則,確保長期穩定。

範例:測試到永久: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 類別(c1c2)隔開容器資料,確保 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)。

  • 試 Permissive 模式:臨時關強制模式,確認問題是標籤:
  • setenforce 0
    curl http://localhost/index.html  # 能讀?是標籤問題
    setenforce 1
  • 還原預設標籤:如果標籤亂了,恢復預設:
  • restorecon -R -v /custom/web

    這會根據系統上下文資料庫還原標籤(可能回到 default_t)。

  • 驗證規則:確認 semanage fcontext 的規則對不對:
  • semanage fcontext -l | grep /custom/web
  • 檢查檔案系統:確保檔案系統支援 SELinux 標籤(像 ext4、XFS):
  • 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"
  • 測試環境:先在虛擬機或測試伺服器試,別直接搞正式環境。
  • 整合工具:用 Ansible 或 Puppet 自動化標籤管理,減少手動錯誤。

3.3.7 chcon 和 semanage fcontext 的工作流程

為了讓你徹底搞懂,這裡是個標準工作流程:

  1. 程式跑不了(像 Apache 讀不了 /custom/web),檢查標籤:
  2. ls -Z /custom/web
  3. chcon 臨時改,測試效果:
  4. chcon -R -t httpd_sys_content_t /custom/web
    curl http://localhost/index.html
  5. 確認 OK 後,用 semanage fcontext 設永久規則:
  6. semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
    restorecon -R -v /custom/web
  7. 再次測試,確保長期生效:
  8. curl http://localhost/index.html
  9. 記錄變更,備份規則:
  10. 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 的規則手冊加筆記,告訴它「這個程式可以做這個事」。這需要三個超重要的工具:ausearchaudit2allowsemodule。讓我們來深入拆解它們怎麼用,幫你打造完美的自訂規則!

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)。
  • 改標籤(用 chconsemanage 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 服務,沒日誌可查。確認:
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_tdefault_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_tdefault_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
      • 權限需求:需要 rootsudo
      • 備份策略:安裝新模組前備份,萬一亂了可以還原。
      • 測試效果:裝完模組後重啟相關服務,確認問題解決。

4.3.5 完整範例:從日誌到規則

讓我們把這三個工具串起來,解決 Apache 連 PostgreSQL(5432 埠)的問題!假設 Apache 被擋住連線:

      1. 找問題(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 埠),但被擋。

      1. 想辦法(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 埠。」

      1. 裝規則(semodule):安裝模組:
semodule -i httpd_db.pp

檢查模組:

semodule -l | grep httpd_db

輸出:

httpd_db    1.0
      1. 試試看:重啟 Apache,確認連線:
systemctl restart httpd

如果連線成功,恭喜!問題解決。

4.3.6 進階場景:複雜規則

假設你有個自訂程式 /opt/myapp,要讀寫 /data/app 和用 8080 埠,但被多重拒絕擋住:

      1. 設標籤(先試標籤修法):
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
      1. 找日誌:檢查剩哪些問題:
ausearch -m avc -c myapp --since today
      1. 生成完整規則:用 -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;
      1. 安裝並測試
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_custommyapp_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 排查步驟

      1. 看日誌:
ausearch -m avc --since today
      1. 試寬鬆模式:
setenforce 0

問題沒了?就是 SELinux 的鍋。

      1. 分析原因:
ausearch -m avc -ts today | audit2why
      1. 修好後切回強制模式:
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 從害怕變成愛用!有問題或想看更多範例,隨時留言。下一篇文章會講 AppArmor,別錯過哦!