3.6、利用 SELinux trouble shoot 服務
基本上,透過了解 SELinux 的三種模式 (disabled, permissive, enforcing)、功能規範開放與否 (getsebool, setsebool)、
安全本文的修改 (chcon, restorecon, semanage fcontext),以及埠口規範 (semanage port) 的方法,
對於 SELinux 的管理,大概就不會差太多了!不過,有沒有更簡單的方法呢?是有的喔!
事實上,如果你的 SELinux 運作錯誤時,我們可以透過 setroubleshoot 這個軟體的功能,
它會自動分析可能的錯誤,並且將可能的解決方案直接紀錄到 /var/log/messages 裡面!
如此一來,你只要重複犯錯的動作,然後查閱 messages 檔案內容,就可以知道如何解決了!相當愉快!
確認 setroubleshoot 與 rsyslog 是有安裝的
要使用 SELinux 自動錯誤克服的功能,就得要安裝 setroubleshoot 軟體才行!而且,
初次安裝完畢時,可能得要重新開機才會有作用。另外,RockyLinux 8 預設似乎沒有啟動 rsyslog,不過,RockyLinux 9 倒是預設安裝的。
如果你無法確認 rsyslog 有沒有啟動以及 setroubleshoot 有沒有安裝,沒關係,就讓我們來手動測試看看即可。
[root@localhost ~]# yum -y install setroubleshoot*
[root@localhost ~]# rpm -qa | grep setrouble
setroubleshoot-plugins-3.3.14-4.el9.noarch
setroubleshoot-server-3.3.31-2.el9_2.x86_64
setroubleshoot-3.3.31-2.el9_2.x86_64
[root@localhost ~]# systemctl status rsyslog
● rsyslog.service - System Logging Service
Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; preset: enabled)
Active: active (running) since Fri 2023-08-04 16:02:11 CST; 6h ago
Docs: man:rsyslogd(8)
https://www.rsyslog.com/doc/
Main PID: 609 (rsyslogd)
Tasks: 3 (limit: 12243)
Memory: 3.0M
CPU: 521ms
CGroup: /system.slice/rsyslog.service
└─609 /usr/sbin/rsyslogd -n
你可能會覺得很怪異,上面安裝的軟體名稱當中有 setroubleshoot-server 這個關鍵字,但是,使用 systemctl
去檢查相關的服務時,卻找不到任何 setrouble 相關的服務名稱!這是因為 setrouble 已經整合到稽核模組 auditd 服務中!
因此, setroubleshoot 的運作方式是這樣的:
先由 auditd 去呼叫 audispd 服務
然後 audispd 服務去啟動 sedispatch 程式
sedispatch 再將原本的 auditd 訊息轉成 setroubleshootd 的訊息,進一步儲存下來的
總之,鳥哥這種老人家,還是比較習慣查詢 /var/log/messages 內的資料,而不是讓日誌直接寫入 systemd-journald 當中!
因為只寫入 systemd-journald 時,當系統重新開機,日誌可能是會遺失的呢!
1. 模擬狀況,當 port 出問題時:讓 httpd 開啟在非正規埠口
基本上,http, https 的埠口分別是 port 80, port 443 的 tcp 埠口。那麼當我將這個埠口開啟到非正規的 377 埠口呢?
很可能會無法啟動喔!先來測試看看。我們依序可以這樣做:
1. 網頁伺服器的軟體所需名稱為 httpd,請安裝好這個軟體
2. 軟體主設定檔為 /etc/httpd/conf/httpd.conf ,內部的 Listen 設定,請改為 377
3. 啟動名為 httpd 的服務,並且查看有沒有出問題?
4. 若出問題,將 SELinux 模式由 enforcing 改為 permissive 測試一下
5. 再次重新啟動 httpd 服務,是否能正常啟動?若可以,代表就是 SELinux 的問題。
6. 前往 /var/log/messages 查詢是否有 setrouble 的關鍵字?若有,取出查閱
7. 根據 sealert 的解釋,將問題克服
我們就實際在虛擬機上面惡搞一下囉!
# 1. 先安裝軟體
[root@localhost ~]# yum -y install httpd
# 2. 修改設定檔,大約在 47 行處,修改埠口號碼
[root@localhost ~]# vim /etc/httpd/conf/httpd.conf
Listen 377
# 3. 嘗試啟動 httpd 服務
[root@localhost ~]# systemctl start httpd
Job for httpd.service failed because the control process exited with error code.
See "systemctl status httpd.service" and "journalctl -xeu httpd.service" for details.
# 如上所示,系統會提示出現錯誤了!
# 4. 嘗試將 SELinux 模式改為 permissive
[root@localhost ~]# getenforce
Enforcing
[root@localhost ~]# setenforce 0
[root@localhost ~]# getenforce
Permissive
# 5. 確認一下能不能順利啟動?用來判斷問題是否出在 SELinux 的情況
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# netstat -tlunp | grep httpd
tcp6 0 0 :::377 :::* LISTEN 8324/httpd
# 出現 LISTEN 關鍵字!代表服務有正常啟動了!所以,問題一定是 SELinux 造成的!
# 6. 確認 /var/log/messages 有沒有因為啟動 httpd 而記載錯誤解決方案
[root@localhost ~]# grep setrouble /var/log/messages | grep sealert
Aug 4 22:13:38 localhost setroubleshoot[3553]: SELinux is preventing /usr/sbin/httpd
from name_bind access on the tcp_socket port 377. For complete SELinux messages
run: sealert -l 525fe157-a16b-47c4-ad8d-11bad86c9e9a
# 重點是找到 sealert 這個關鍵字!後續的指令直接執行就是答案!
# 7. 將找到的 sealert 指令執行,並依據提示處理問題
[root@localhost ~]# sealert -l 525fe157-a16b-47c4-ad8d-11bad86c9e9a
SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 377.
***** Plugin bind_ports (99.5 confidence) suggests ************************
If you want to allow /usr/sbin/httpd to bind to network port 377
Then you need to modify the port type.
Do
# semanage port -a -t PORT_TYPE -p tcp 377
where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, ...
***** Plugin catchall (1.49 confidence) suggests **************************
....
其實解決問題的方案不止一種,因此,上述的 sealert 提供的方式中,會有好幾個解決方案,不過,解決方案總是有輕重緩急!
所以,最好選擇信賴度最高的方案來解決較佳!所以,當然是選上面 99.5% 信賴度的啊!然後,又看到底下的指令,
就是『 semanage port -a -t PORT_TYPE -p tcp 377 』這一段,你應該會覺得很開心!因為剛剛才學過啊!
只是, PORT_TYPE 必須要選擇正確的項目才行!因為我們是在處理 http 的埠口,當然最終選擇 http_port_t!
所以,整個解決方案的處理會是這樣:
[root@localhost ~]# semanage port -a -t http_port_t -p tcp 377
[root@localhost ~]# setenforce 1
[root@localhost ~]# getenforce
Enforcing
[root@localhost ~]# systemctl restart httpd
# 最終,在 Enforcing 的模式中,再次重新啟動服務!可正常啟動才會是正確的!
範例的思考也是很重要的!上面的範例中,原本鳥哥屬意的埠口是 538 (台語諧音:有三八),
但是,這個埠口竟然已經被 semanage port 所管理了!因此無法順利找到正確的解決方案!差點崩潰!
所以,未來想要找某個非正規埠口來練習時,還是得要先用底下的方法確認,不要使用已記載的埠口為佳!
『semanage port -l | grep 538』(結果沒出現任何訊息,該埠口才好應用!)
最後,讓我們使用文字型瀏覽器來看看我們的本機 (http://localhost) 有沒有順利提供服務呢?
[root@localhost ~]# curl http://localhost:377 2> /dev/null | head
.....
# 有看到資料輸出,就是正確的顯示啦!文字型瀏覽器只能作到這樣解析!
2. 模擬狀況,當主體程序與目標資源的安全本文類型不符時
進行這個模擬時,先有個觀念,那就是,整個網頁伺服器的資料預設是放置到 /var/www/html 裡面的!
所以,如果想要讀取 http://localhost/test.txt 時,該檔案需要放置成為 /var/www/html/test.txt 才對!
現在,讓我們模擬一個錯誤!那就是,使用者在自己家目錄建立好網頁資料,然後使用 cp -a 的方式複製到網頁伺服器上!
那個 -a 很厲害啊!可能會連同 SELinux type 都複製過去~如此一來,會變怎樣呢?
1. 先以 root 的身份,去自己家目錄建立名為 test.txt 的檔案
2. 使用 cp -a 的方式,將該檔案複製到 /var/www/html 網頁主目錄下
3. 使用 curl 去瀏覽,看看瀏覽的結果為何,再來判斷!
4. 搜查 /var/log/messages 看看有沒有最近的 setrouble 輸出結果?
5. 執行 sealert 之後,依據建議修改錯誤。
# 1. 先建立名為 test.txt 的檔案
[root@localhost ~]# vim ~/test.txt
I am VBird
Today: 2023/08/04
# 2. 用 cp -a,注意,記得加上 -a 喔!在這個練習底下!不要用 -r
[root@localhost ~]# cp -a ~/test.txt /var/www/html
# 3. 使用 curl 去瀏覽,記得我們的埠口在非正規喔!
[root@localhost ~]# curl http://localhost:377/test.txt
Forbidden
You don't have permission to access this resource.
# 注意喔,錯誤訊息是『沒有權限』而不是『找不到檔案』
# 意思是,有這個檔案存在,但是你沒有權限讀取的意思!重要重要!
[root@localhost ~]# ll /var/www/html/test.txt
-rw-r--r--. 1 root root 29 Aug 4 22:20 /var/www/html/test.txt
# 但是權限是合理的!所有人都可以讀取(r)!所以,直接懷疑是 SELinux 囉!
# 4. 檢查有沒有 SELinux 的 log 錯誤!?
[root@localhost ~]# grep setrouble /var/log/messages | grep sealert
Aug 4 22:20:44 localhost setroubleshoot[3942]: SELinux is preventing /usr/sbin/httpd
from getattr access on the file /var/www/html/test.txt. For complete SELinux messages
run: sealert -l 95e5f3a3-e946-4f45-9560-5787ff5083f9
# 果然在最接近的時間就有一個 SELinux 的警告訊息出現了!
# 5. 執行看看 sealert 之後,設法解決問題!
[root@localhost ~]# sealert -l 95e5f3a3-e946-4f45-9560-5787ff5083f9
SELinux is preventing /usr/sbin/httpd from getattr access on the file /var/www/html/test.txt.
***** Plugin restorecon (99.5 confidence) suggests ************************
If you want to fix the label.
/var/www/html/test.txt default label should be httpd_sys_content_t.
Then you can run restorecon. The access attempt may have been stopped due to insufficient permissions
to access a parent directory in which case try to change the following command accordingly.
Do
# /sbin/restorecon -v /var/www/html/test.txt
....
[root@localhost ~]# /sbin/restorecon -v /var/www/html/test.txt
Relabeled /var/www/html/test.txt from unconfined_u:object_r:admin_home_t:s0
to unconfined_u:object_r:httpd_sys_content_t:s0
# 6. 最後,讓我們測試一下,到底能不能成功瀏覽到資訊了?
[root@localhost ~]# curl http://localhost:377/test.txt
I am VBird
Today: 2023/08/04
# 果然就正常啦!
這邊的兩個練習都是常見的問題!請大家務必實做一次以上啊!會很有幫助喔!加油加油!
總之,你要記住,無法啟動某個服務或者是某服務無法存取某個檔案資源,先查看一下 rwx 是否正確?若正確,
先將 SELinux 變更為 permissive,然後『重複一次剛剛發生錯誤的動作』,看看有沒有正常?若有正常,
那表示問題一定來自 SELinux 了!再將 SELinux 變更為 Enforcing,之後依據 sealert 提供的方式處理錯誤,
最終,一定要『再次的重複一次剛剛發生錯誤的動作』,確定在 Enforcing 的狀態下也是正常啟動處理的!
那就沒問題了!