實體主機 SELinux 拒絕存取排查:從 AVC 紀錄到標籤修復
網頁檔案從原目錄移到 /srv 後,httpd 回傳 403;檔案可讀取,服務也正在執行,此時不應直接放寬權限或停用 SELinux。一般檔案權限、服務的存取授權設定,以及強制存取控制,是三個不同層次的檢查。本文以 AlmaLinux 9 上既有的 httpd 測試虛擬主機為例,先將失敗請求與 AVC 紀錄對照,再修復持久化標籤,最後驗證服務與存取邊界。
一、確立測試範圍與原始狀態
CloudDDR 實體主機可選用 AlmaLinux 9。付款後可在控制台查看登入憑證,透過 SSH 連線;若有多個位址,請使用第一個位址。以下操作需要管理員權限,僅限在隔離的測試環境中執行,不覆寫現有業務目錄,也不變更監聽位址或防火牆。
前提是已安裝 httpd,且測試虛擬主機與存取入口均已設定完成。先備份該虛擬主機的設定,並記錄原本的 DocumentRoot。若缺少排查工具,先安裝對應套件,再確認安全模式與稽核服務:
sudo dnf install policycoreutils-python-utils
getenforce
sestatus
sudo systemctl status auditd --no-pager
sudo semanage fcontext -l -C
sudo semanage permissive -l
預期全域模式為 Enforcing,且 httpd 所在的網域未被單獨設為 permissive。若稽核服務未正常執行,應先恢復證據蒐集,不能以「日誌沒有拒絕紀錄」證明存取未受限制。請保存本機標籤對應規則清單,後續才能區分原有規則與本次新增的規則。
setenforce 0不代表修復完成。停用強制檢查後請求成功,只能作為排查線索,不能證明恢復 enforcing 後仍可正常運作。
二、先排除一般權限與設定錯誤
建立專用測試目錄,內容只放一行驗收標記。目錄允許遍歷,檔案允許讀取;這些權限僅適用於公開的測試內容,不應套用到金鑰、備份或業務資料。
sudo install -d -m 0755 /srv/selinux-lab
printf 'selinux-lab-ok\n' | sudo tee /srv/selinux-lab/index.html
sudo chmod 0644 /srv/selinux-lab/index.html
namei -l /srv/selinux-lab/index.html
ls -ldZ /srv/selinux-lab
ls -lZ /srv/selinux-lab/index.html
在既有的測試虛擬主機中,將文件根目錄及對應目錄的存取授權改為:
DocumentRoot "/srv/selinux-lab"
<Directory "/srv/selinux-lab">
AllowOverride None
Options -Indexes
Require all granted
</Directory>
修改前先確認設定檔的引入關係,避免其他目錄設定區段覆蓋授權。使用 httpd -t 檢查語法,再用 httpd -S 確認請求會由哪個虛擬主機處理;檢查通過後才重新載入設定。
將 Shell 變數 CHECK_URL 設為實際測試入口,路徑指向 /index.html。入口必須對應該虛擬主機;不要將登入憑證寫入 URL。發出請求時,應檢查回應本文,而不是只確認連線成功:
sudo httpd -t
sudo httpd -S
sudo systemctl reload httpd
curl --fail --show-error "$CHECK_URL"
若請求失敗,先查看 httpd 錯誤日誌。上層目錄缺少執行權限、請求落到錯誤的虛擬主機,或授權規則拒絕存取,都可能造成類似現象。此時以遞迴方式大幅放寬權限,既無法解決標籤問題,也會擴大暴露範圍。
三、透過 AVC 找出主體、物件與動作
記錄請求發生的時間,並只重現一次失敗操作,再查詢同一時段的拒絕紀錄:
sudo ausearch -m AVC,USER_AVC -ts recent -i
ps -eZ | grep '[h]ttpd'
matchpathcon -V /srv/selinux-lab/index.html
AVC 紀錄中應重點查看 scontext、tcontext、tclass 及遭拒的權限。scontext 代表發起存取的程序網域,tcontext 代表目標物件的安全性上下文;讀取檔案與搜尋目錄是不同的動作,不能只憑程序名稱判斷。
應將紀錄中的路徑、程序及時間與失敗請求對照。若遭拒的是目錄搜尋,還需檢查路徑上的每一層目錄;只修復最終檔案可能無效。若日誌中沒有路徑資訊,可利用稽核事件序號查看同一事件的其他紀錄。
沒有 AVC 紀錄,不代表一定與 SELinux 無關。先確認時間範圍、稽核狀態,以及服務實際執行的程序網域;若仍找不到相關證據,應回到應用程式設定與一般權限排查,不要盲目新增允許規則。也不要將所有歷史拒絕紀錄送進政策產生工具,因為其中可能包含其他業務的事件。
四、持久化修復網頁內容標籤
對於供 httpd 讀取的一般靜態內容,httpd_sys_content_t 是既有政策中對應此用途的類型。只有在證據顯示測試路徑的標籤不符合用途時,才新增精確的對應規則:
sudo semanage fcontext -a -t httpd_sys_content_t \
'/srv/selinux-lab(/.*)?'
sudo restorecon -Rv /srv/selinux-lab
matchpathcon -V /srv/selinux-lab/index.html
ls -lZ /srv/selinux-lab/index.html
若完全相同的運算式已存在,應先確認原規則,必要時使用 -m 修改,而不是繼續疊加規則。運算式只涵蓋測試目錄,不應將整個 /srv 改成網頁內容類型。semanage 儲存路徑與類型的對應規則,restorecon 才會將預期的安全性上下文套用到現有檔案。
chcon 會直接修改目前的標籤,但不會建立持久化對應規則,因此不能取代這一步。後續重新標記時,暫時的修改可能被還原。
靜態目錄不需要讓服務寫入。不要為了消除一次拒絕,就將全部內容改成可寫入類型,也不要順手啟用無關的布林值。若確實有上傳、快取等寫入需求,應劃分獨立目錄,依實際動作分別設計權限與標籤。
五、驗證修復結果並保留回復路徑
維持 enforcing 模式,再次存取同一入口,確認回應本文包含 selinux-lab-ok,並檢查是否出現與該請求相關的新 AVC 紀錄。接著再次執行 restorecon 並重新啟動測試服務,重複發出請求,驗證修復不依賴暫時標籤或舊的程序狀態。
getenforce
sudo restorecon -Rv /srv/selinux-lab
sudo systemctl restart httpd
curl --fail --show-error "$CHECK_URL"
sudo ausearch -m AVC,USER_AVC -ts recent -i
驗收也應包含反向檢查:請求不存在的檔案應失敗;測試目錄不應允許目錄索引;服務未新增非預期的監聽;本機標籤變更僅涵蓋約定路徑。這些檢查分別驗證應用程式行為、設定與變更範圍,不能互相取代。
結束實驗時,先恢復原本的虛擬主機設定,通過語法檢查並重新載入後,再清理測試目錄。本次確實新增的對應規則可以刪除;若修改了原有規則,則應恢復為先前記錄的類型。清理後,對保留的路徑執行 restorecon。
最後應保留失敗請求、相關 AVC 紀錄、修改前後的標籤、對應規則運算式,以及重新啟動後的回應結果。一次請求成功,只代表功能恢復;安全模式未改變、對應規則可重建、存取範圍未擴大,才算完成完整的修復驗收。
常見問題
網頁回傳 403 就能確定是 SELinux 嗎?
不能。還須檢查目錄通行權限、檔案讀取權限與 httpd 授權設定,並將失敗請求對應到同一時段的 AVC 紀錄;沒有關聯證據時,不應視為 SELinux 故障。
為何不用 chcon 或直接產生允許策略?
chcon 設定的標籤可能被 restorecon 重設。一般網頁目錄應使用 semanage fcontext 搭配 restorecon 持久化標籤;產生允許策略前,應先排除標籤、路徑設定與既有策略開關的問題。