SELinux 不是防火牆,大致的目的是在保護由於自己 server 的用戶耍白痴可能造成的錯誤!
說到 SELinux 許多人都很恨它,因為設定方面真的是很點困擾,而且經常還有許多的『例外』狀況需要處理。 不過,近期以來的 SELinux 應該算是比較穩定些,而且,大概只要知道幾個小細節,就很容易找到處理方向。 只是,如果想要用 Linux 進行類似專題、專案開發的話,其實,暫時轉成寬容模式,大概還是需要的...。 總之,對於正規網際網路服務或者是管理企業內部員工的權限來說,這東西目前還是很有用途的! 所以,還是得要讓我們了解一下這東西才行!
請注意,這一章的所有練習環境,都在前一章節建立的 VM 裡面實做喔!不要在 KVM host 裡面動作喔!
SELinux 全名其實是『 Security Enhanced Linux 』的意思,這傢伙最早是由美國國家安全局開發出來的, 會想要做這個東西的原因,其實是早期 Unix 的系統中,如果你將某個目錄設定成為 777 (drwxrwxrwx) 之後, 那麼該目錄就變成所有人都可以存取的情境!對於內部某些敏感資料來說,很可能由於人為的設定錯誤, 導致資料可能被第三人竊取或刪除...管理員想哭都哭不出來的啦!
所以,SELinux 開發的目的,就是在防止上述的自由選定存取控制 (Discretionary access control, DAC) 造成的影響, 取而代之的,是利用強制存取控制 (Mandatory access control, MAC) 的方法來進行檔案的存取。 在 MAC 的存取方法中,並沒有所謂的 root 的使用者概念,而是透過安全本文來限制程序的讀寫能力。
另外,SELinux 並不是取代了傳統的 rwx 權限,而是在通過檔案權限的判定之後,『再加』一層防護, 該層 SELinux 的防護,可以『針對某些網路程序可以讀取的檔案之安全本文類型』進行限制, 因此,如果被讀寫目標檔案的安全本文設計無法與相關程序匹配,那麼該程序就無法讀寫目標檔案了。
上面的說法其實很抽象,我們來畫張圖解釋解釋好了。如下圖所示,要啟動網路服務,總是得要執行某些腳本或程式。 這時 SELinux 就開始進行防護!如果你的設定檔設計錯誤,無法符合當初 SELinux 指定的預設規則 (rule), 那該腳本或程式就無法順利載入到記憶體當中了 (藍色箭頭部份,很可能被 SELinux 抵擋)。若通過預設規則而載入軟體成為網路程序, 那該網路程序想要讀取某些系統上面的檔案時,如果網路程序與檔案的安全本文設計不符,那也無法讀到該檔案! 這也是 SELinux 最主要限制的地方喔!(紅色箭頭部份)
這麼限制的好處是,因為沒有 root 的使用者概念,因此,上圖當中,你的 httpd process 被攻擊而被破解了! 那也沒關係,因為除了原本 httpd process 可以讀寫的位置之外,系統的其他目錄,httpd 是沒有訪問權的! 舉例來說, /etc 目錄的安全本文與 httpd process 能讀取的並不相同,因此你的 /etc/ 目錄,就不可能被有問題的 httpd 訪問了!這樣起碼能夠作到一定程度的保護。
從上面的簡單介紹,我們大概知道每個網路程式應該會有相對的 SELinux 安全本文,然後載入到記憶體之後, 會取得其相對的程序 SELinux 類型,而這個程序能不能讀某個檔案,也得要看該檔案的安全本文類型才行。 那麼,這些安全本文與程序的 SELinux 類型,是紀錄在哪裡呢?基本上,就是紀錄在檔案的 i-node 裡面啦! 回想一下,當初你在救援系統時,如果有進入到 rd.break 這個救援環境,離開 chroot 時,是不是需要『 touch /.autorelabel 』呢? 這個動作不是會搞很久嘛?那就是 SELinux 要重建系統內所有檔案的 SELinux 表頭資料,而需要於各檔案的 i-node 內重新建立 SELinux 安全本文的緣故! 現在懂了吧!
# 前往 VM 系統,查看一下 /usr/sbin/chronyd 的 SELinux 類型 [root@localhost ~]# stat /usr/sbin/chronyd File: /usr/sbin/chronyd Size: 353408 Blocks: 696 IO Block: 4096 regular file Device: fd03h/64771d Inode: 17318583 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Context: system_u:object_r:chronyd_exec_t:s0 Access: 2023-04-17 07:40:35.000000000 +0800 Modify: 2023-04-17 07:40:35.000000000 +0800 Change: 2023-08-04 13:37:38.809963808 +0800 Birth: 2023-08-04 13:37:38.806963845 +0800
如上所示,透過 stat 取出該檔名相關的紀錄,最重要就是那個 Context 行,Context 就是安全本文囉! 因此,這個 chronyd 執行檔,安全本文就是『system_u:object_r:chronyd_exec_t:s0』用冒號 (:) 隔開, 共分數個欄位,現行主要的限制,其實僅有針對第三欄位,也就是 chronyd_exec_t 那個項目而已! 所以,我們知道這個執行檔的安全本文類型就是 chronyd_exec_t 的意思。除了這個 stat 之外,我們其實可以透過 ls 搭配 -Z 同樣可以看到這個安全本文資料:
[root@localhost ~]# ll -Z /usr/sbin/chronyd -rwxr-xr-x. 1 root root system_u:object_r:chronyd_exec_t:s0 353408 Apr 17 07:40 /usr/sbin/chronyd
從圖 3.1-1 可以看到,檔案有安全本文,那麼程序有嘛?其實也是有的! 觀察程序的安全本文,可以透過 ps -Z 的參數來查詢得到!
# 查出 chronyd 程序的 PID 含有的安全本文 [root@localhost ~]# ps auxZ | egrep 'chrony|LABEL' LABEL USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND system_u:system_r:chronyd_t:s0 chrony 616 0.0 0.1 84436 3252 ? S 16:02 0:00 /usr/sbin/chronyd -F 2
可以看到程序也是有安全本文相關資料,只是在 ps 裡面被稱為是標籤 (LABEL) 而已。同樣的, chronyd 程序的 SELinux 類型為 chronyd_t 喔!
上面講的是比較概念性的運作方法,實際上,SELinux 的運作,可以參考下列的流程表。 在傳統的 DAC,亦即是傳統的 rwx 權限存取控制的分析後,若程序可以進行存取,則開始進入到 SELinux 的控制流程中。 若程序無法存取,那當然就不會進入 SELinux 的控制流程啦!
在 SELinux 的控制流程中,一個主體 (subject) 想要取用某個目標 (Object) 時,得要經過完整的 SELinux 流程之後, 最終才能夠實際讀寫到檔案的資料 (file data)。所以,MAC 的目的是增加 DAC 的不足,而不是用來取代 DAC 喔! 事實上,還是得要先通過 rwx 傳統權限的控制才行!
在上面整體的說明中,重點在『主體』如何取得『目標』的資源存取權限!由上圖我們可以發現, 程序的讀寫,還是需要先經過 rwx 的權限分析,如果 rwx 權限原本就被抵擋,那麼後續 SELinux 流程就不會啟動。 若通過 rwx 的權限且確定可以讀寫之後,則:SELinux 在取得主體程序與預備存取的目標資源之安全本文類型後, 開始搜尋兩者在 SELinux 當中的規則,若確認匹配可行,就予以放行,否則就予以抵擋。
再次強調,安全本文的紀錄主要在檔案的 inode 當中喔~程序也會有安全本文的紀錄~讓我們再次回想一下, 如何取得安全本文的資料呢?
# 程序的安全本文取得 [root@localhost ~]# ps -auxZ | egrep 'bash|chronyd|LABEL' LABEL USER ... COMMAND system_u:system_r:chronyd_t:s0 chrony ... /usr/sbin/chronyd -F 2 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root ... -bash # 檔案的安全本文觀察 [root@localhost ~]# ll -Z /usr/sbin/chronyd /etc/chrony.keys -rw-r-----. 1 root chrony system_u:object_r:chronyd_keys_t:s0 540 Aug 29 2022 /etc/chrony.keys -rwxr-xr-x. 1 root root system_u:object_r:chronyd_exec_t:s0 353408 Apr 17 07:40 /usr/sbin/chronyd [root@localhost ~]# ll -Zd /root/anaconda-ks.cfg /home/vbird drwx------. 2 vbird vbird unconfined_u:object_r:user_home_dir_t:s0 113 Aug 4 13:43 /home/vbird -rw-------. 1 root root system_u:object_r:admin_home_t:s0 1204 Aug 4 13:05 /root/anaconda-ks.cfg
大部分只要記得前面三個欄位就好!這三個欄位的功能分別是:
User_Identify:role:type SELinux用戶別:角色:類型
安全本文的類型資料相當多!稍等我們再來查詢安全本文的限制!大致上先了解到這裡即可。
從圖 3.2-1 當中,我們可以知道 SELinux 共有三種模式,分別是 disabled, permissive 與 enforcing 三種,根據圖示的樣子,我們大概可以這樣看這三種模式:
寬容模式與強制模式,可以在不重新開機的情況下直接進行轉換!因此,想要判斷某個主體無法存取目標的原因是 SELinux 或是傳統權限時,就可以將 SELinux 模式暫時轉到寬容模式去測試即可。那如何觀察目前的 SELinux 模式呢? 使用 getenforce 即可。
# 觀察目前的 SELinux 模式 [root@localhost ~]# getenforce Enforcing # 暫時將 SElinux 模式調整成為 permissive [root@localhost ~]# setenforce [0|1] [root@localhost ~]# setenforce 0 [root@localhost ~]# getenforce Permissive # 顯示詳細的 SELinux 設定值 [root@localhost ~]# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: permissive Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 33 # 趕緊將模式轉回 Enforcing [root@localhost ~]# setenforce 1
預設的 SELinux 設定檔為 /etc/selinux/config,但是,我們也可以在開機階段,在 Linux 核心強迫開啟或關閉 SELinux 的。 所以,要觀察 SELinux 的初始設定,可能得要觀察兩個地方呢!分別是上述的設定檔,還有 grub 的設定。 當然,你也可以直接觀察目前的核心參數,以確認 SELinux 是否由核心參數所影響。
# 修改預設的設定檔,指定開機為 SELinux 模式 [root@localhost ~]# vim /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted # 其實,這個設定檔就是指定 SELinux 模式與政策的檔案 # 檢查目前的核心是否有 SELinux 的參數 [root@localhost ~]# cat /proc/cmdline BOOT_IMAGE=(hd0,gpt3)/boot/vmlinuz-5.14.0-284.18.1.el9_2.x86_64 root=UUID=e81ba... # 查詢看看有沒有 selinux=[0|1] 這個關鍵字串! # 檢查 grub.cfg 有沒有 selinux 的關鍵字 [root@localhost ~]# grep selinux /boot/grub2/grub.cfg
在圖 3.2-1當中,進入整個 SELinux 的程序當中的第二關,除了得要參考政策內的規則外, 某些特定功能 (SELinux boolean) 是否啟動,也是相當重要的!舉例來說,看看網頁伺服器能不能提供一般帳號家目錄的讀取權限, 就是透過這裡的規範來額外指定的。如果這裡的功能規範當中,不允許網頁伺服器讀取個人家目錄, 那麼,即使個人家目錄的安全本文類型是正確的,網頁伺服器也會無法讀取喔!
查詢目前所有 SELinux boolean 功能的狀態是開啟還是關閉,最簡單可以透過 getsebool 來查詢即可:
[root@localhost ~]# getsebool -a abrt_anon_write --> off abrt_handle_event --> off ..... zoneminder_anon_write --> off zoneminder_run_sudo --> off # 功能規範真的太多了!透過 grep 來擷取看看 # 查詢看看有沒有 http 開頭,home 後續存在的功能規範 [root@localhost ~]# getsebool -a | grep 'http.*home' httpd_enable_homedirs --> off # 已經知道 httpd_enable_homedirs 功能規範名稱時 [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> off
除了 getsebool 之外,我們可以透過萬用工具,就是 semanage 這個軟體,裡面的 boolean 指令來查詢!
# 需要先安裝 semanage 的軟體!名稱為 policycoreutils-pythone-utils [root@localhost ~]# yum whatprovides '*bin/semanage' policycoreutils-python-utils-3.5-1.el9.noarch : SELinux policy core python utilities Repo : appstream Matched from: Other : *bin/semanage [root@localhost ~]# yum -y install policycoreutils-python-utils # semanage boolean 的簡單 help [root@localhost ~]# semanage boolean -h .... -l, --list List records of the boolean object type -1, --on Enable the boolean -0, --off Disable the boolean [root@localhost ~]# semanage boolean --list SELinux boolean State Default Description .... httpd_enable_homedirs (off , off) Allow httpd to enable homedirs ....
這樣也能很輕鬆的找到需要的 SELinux boolean 說明!基本上,這些功能規範說明目前你可能還看不太懂, 這是因為可能你不具備某些特定的網路服務經驗。沒關係!等到後面許多伺服器章節實做完之後, 對這些功能規範的名稱,你大概就一看就懂了!所以,不用擔心!慢慢來!
如果發現到某些功能規範沒有啟用,想要啟用這些功能時,該怎麼辦呢?既然查看是 getsebool, 想當然爾,設定應該就是 setsebool 囉!
# 將剛剛查詢到的 httpd_enable_homedirs 設定為 on [root@localhost ~]# setsebool -P httpd_enable_homedirs 1 [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> on # 設定值, 1 或 on 都可以啟用, 0 或 off 都可以關閉!
特別注意的是,setsebool 預設修改的是『目前的狀況』,如果想要連同下次開機都使用相同的設定, 那直接加上 -P 的選項來處理即可!所以,將它背下來! setsebool 就是要 -P !!
# 使用 semanage boolean 關閉 httpd_enable_homedirs 測試看看 [root@localhost ~]# semanage boolean httpd_enable_homedirs --modify --off [root@localhost ~]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> off # 雖然比較麻煩,不過,還是稍微熟悉一下 semanage 較佳!
前面我們談到 SELinux 安全本文,在程序上面,可以查看的指令有『 ps -Z 』之類的方式,而檔案的安全本文, 則是透過『 ll -Z 』或者是『 stat 』這個指令來查詢。那麼修改呢?該如何進行安全本文的修改? 注意喔,修改時,請修改安全本文的類型即可,不要更動到身份識別或者是角色欄位喔!
最簡單的修改方式是透過 chcon 來修改即可!例如底下的範例:
[root@localhost ~]# chcon [OPTION]... [-t TYPE] FILE... [root@localhost ~]# chcon [OPTION]... --reference=RFILE FILE... 選項與參數: -t :後面接安全性本文的類型欄位!例如 httpd_sys_content_t --reference=RFILE:拿檔名為 RFILE 當範例來修改後續接的檔案的類型! # 將 /etc/hosts 複製 /dev/shm/hosts,並修改類型為 etc_t [root@localhost ~]# cd /dev/shm [root@localhost shm]# cp -a /etc/hosts . [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:net_conf_t:s0 158 Jun 23 2020 hosts [root@localhost shm]# chcon -t etc_t hosts [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:etc_t:s0 158 Jun 23 2020 hosts # 將類型改成與 /var/spool/mail 相同 [root@localhost shm]# ll -Zd /var/spool/mail/ drwxrwxr-x. 2 root mail system_u:object_r:mail_spool_t:s0 19 Jul 21 15:01 /var/spool/mail/ [root@localhost shm]# chcon --reference=/var/spool/mail hosts [root@localhost shm]# ll -Z hosts -rw-r--r--. 1 root root system_u:object_r:mail_spool_t:s0 158 Jun 23 2020 hosts
不知道你會不會好奇,既然 SELinux 模式可以在 disable 與 enforcing 之間切換,只是轉為 enforcing 時, 可能會花費一段時間,讓核心對全系統的檔案進行重新設定 (relabel) 的動作!那麼表示,每個檔案/目錄, 可能都會有預設值囉!那如何查詢預設值呢?透過 semanage 吧!這樣做看看:
# 找到 /etc/sysconfig 相關的預設 SELinux 安全本文類型 [root@localhost ~]# semanage fcontext --list | grep /etc/sysconfig SELinux fcontext type Context /etc/sysconfig/.*l2tpd regular file system_u:object_r:l2tp_conf_t:s0 /etc/sysconfig/MailScanner regular file system_u:object_r:mscan_etc_t:s0 .....
很簡單就可以查看到某個檔案/目錄的預設 SELinux 安全本文類型了。好!那如果不是在正規目錄, 預設的安全本文類型又是什麼呢?基本上,看一下上述資料最前頭輸出的幾行就知道了!
[root@localhost ~]# semanage fcontext --list | head SELinux fcontext type Context / directory system_u:object_r:root_t:s0 /.* all files system_u:object_r:default_t:s0 /[^/]+ regular file system_u:object_r:etc_runtime_t:s0 .....
原來預設會是 default_t 喔!
假設我們預計建立一個名為 /www 的目錄,這個目錄的內容主要就是給網頁伺服器使用的!因此, 主要的 SELinux 類型應該指定為與 /var/www 這個目錄相同。那該如何處理呢? 基本上,你可以這樣處理看看。
# 基本的設定語法,如下所示,只要改 type 與目錄位置即可 [root@localhost ~]# semanage fcontext -a -t type "/some/dir(/.*)?" # 1. 先找到 /var/www 的類型為何 [root@localhost ~]# semanage fcontext -l | grep '/var/www(' /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 /var/www(/.*)?/logs(/.*)? all files system_u:object_r:httpd_log_t:s0 # 2. 建立所需目錄,並且查看預設值 [root@localhost ~]# mkdir /www [root@localhost ~]# echo check > /www/index.html [root@localhost ~]# ll -Zd /www /www/index.html drwxr-xr-x. 2 root root unconfined_u:object_r:default_t:s0 24 Jul 22 14:40 /www -rw-r--r--. 1 root root unconfined_u:object_r:default_t:s0 6 Jul 22 14:40 /www/index.html # 果然預設值是 default_t 呢! # 3. 增加 /www 預設為 httpd_sys_content_t 的類型 [root@localhost ~]# semanage fcontext -a -t httpd_sys_content_t "/www(/.*)?" [root@localhost ~]# semanage fcontext -l | grep '^/www' /www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
透過這些步驟,很輕鬆的就完成了非正規目錄的預設 SELinux 安全本文類型設定值!
現在,我們已經規劃好了 /www 的預設類型,那,我們還需要使用 chcon 一個一個慢慢調整 SELinux 的規範嘛? 似乎不需要呢!直接透過 restorecon 來復原即可!很輕鬆愉快喔!
# 將剛剛的 /www 安全本文類型重置一下! [root@localhost ~]# restorecon -Rv /www Relabeled /www from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 Relabeled /www/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
加上 -v 之後,連修改的過程都跟你說了!很簡單愉快!如果在某個特別的情況下,你想要復原全系統的 SELinux 類型, 不必進入核心功能,直接使用 restorecon 也是辦得到的!
[root@localhost ~]# restorecon -Rv /
如同圖 3.1-1 裡面提到的,除了主體程序要存取目標檔案需要透過 SELinux 管理之外, 程式要觸發成為程序時,可能也會經過 SELinux 的修改。系統很可能因為被值入木馬,或者是使用者不小心安裝了有問題的服務, 而這些服務很可能會開啟不明的網路埠口!因此,SELinux 確實有針對某些服務來管理預設埠口, 如果想要啟動非正規埠口,還需要這個埠口對應的功能修改正確才行。
雖然我們還沒有談到網路基礎,不過,一般常識來說,你可能會知道,網頁伺服器一般啟動的埠口會是 port 80, port 443, 因此,SELinux 可能會管制你的伺服器埠口,限制 httpd 這個網路服務程式只能開放在 port 80, 443 而已。 讓我們來查詢一下:
[root@localhost ~]# semanage port --list | grep http http_cache_port_t tcp 8080, 8118, 8123, 10001-10010 http_cache_port_t udp 3130 http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
就是 http_port_t 那個項目!基本上使用的埠口好幾個!那如果你想要增加一個 port 98 怎麼辦? 同樣使用 semanage port 來處理!指令方式也不算太困難:
[root@localhost ~]# semanage port -a -t TYPE -p [tcp|udp] port_range -a 新增一筆紀錄 -t 修改的埠口名稱,例如 httpd_port_t -p 使用 tcp 或 udp 協定 port_range 使用的埠口號碼 # 加入 port 98 的支援到 http 當中 [root@localhost ~]# semanage port -a -t http_port_t -p tcp 98 [root@localhost ~]# semanage port --list | grep http http_port_t tcp 98, 80, 81, 443, 488, 8008, 8009, 8443, 9000
很快可以看到 port 98 也加入可讓 httpd 服務啟動的埠口了!
基本上,透過了解 SELinux 的三種模式 (disabled, permissive, enforcing)、功能規範開放與否 (getsebool, setsebool)、 安全本文的修改 (chcon, restorecon, semanage fcontext),以及埠口規範 (semanage port) 的方法, 對於 SELinux 的管理,大概就不會差太多了!不過,有沒有更簡單的方法呢?是有的喔!
事實上,如果你的 SELinux 運作錯誤時,我們可以透過 setroubleshoot 這個軟體的功能, 它會自動分析可能的錯誤,並且將可能的解決方案直接紀錄到 /var/log/messages 裡面! 如此一來,你只要重複犯錯的動作,然後查閱 messages 檔案內容,就可以知道如何解決了!相當愉快!
要使用 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 的運作方式是這樣的:
總之,鳥哥這種老人家,還是比較習慣查詢 /var/log/messages 內的資料,而不是讓日誌直接寫入 systemd-journald 當中! 因為只寫入 systemd-journald 時,當系統重新開機,日誌可能是會遺失的呢!
基本上,http, https 的埠口分別是 port 80, port 443 的 tcp 埠口。那麼當我將這個埠口開啟到非正規的 377 埠口呢? 很可能會無法啟動喔!先來測試看看。我們依序可以這樣做:
我們就實際在虛擬機上面惡搞一下囉!
# 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 的模式中,再次重新啟動服務!可正常啟動才會是正確的!
最後,讓我們使用文字型瀏覽器來看看我們的本機 (http://localhost) 有沒有順利提供服務呢?
[root@localhost ~]# curl http://localhost:377 2> /dev/null | head <!doctype html> <html> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1'> <title>HTTP Server Test Page powered by: Rocky Linux</title> <style type="text/css"> ..... # 有看到資料輸出,就是正確的顯示啦!文字型瀏覽器只能作到這樣解析!
進行這個模擬時,先有個觀念,那就是,整個網頁伺服器的資料預設是放置到 /var/www/html 裡面的! 所以,如果想要讀取 http://localhost/test.txt 時,該檔案需要放置成為 /var/www/html/test.txt 才對! 現在,讓我們模擬一個錯誤!那就是,使用者在自己家目錄建立好網頁資料,然後使用 cp -a 的方式複製到網頁伺服器上! 那個 -a 很厲害啊!可能會連同 SELinux type 都複製過去~如此一來,會變怎樣呢?
# 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 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access this resource.</p> </body></html> # 注意喔,錯誤訊息是『沒有權限』而不是『找不到檔案』 # 意思是,有這個檔案存在,但是你沒有權限讀取的意思!重要重要! [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 # 果然就正常啦!
這邊的兩個練習都是常見的問題!請大家務必實做一次以上啊!會很有幫助喔!加油加油!
從前一章的後半段開始,我們都用 test1.img 做測試,但其實許多的軟體與服務,似乎應該都要在 demo1.img 裡面存在比較好! 否則,未來我們都會以為好像系統都安裝妥當了...結果卻是在測試環境中進行的...那就比較傷腦筋!呵呵! 所以,現在請將 test1 系統的 history 倒出來備份好,將 test1 關機之後,再啟動 demo1 看看!
# 確認 test1 關閉之後,就啟動 demo1 吧! [root@cloud ~]# virsh create /kvm/xml/demo1.xml Domain 'demo1' created from /kvm/xml/demo1.xml [root@cloud ~]# arp -n -i templan Address HWtype HWaddress Flags Mask Iface 192.168.10.53 ether 52:54:00:ed:63:1f C templan # 登入 192.168.10.53,取得控制權之後,就開始將 SELinux 以及前一章需要的軟體裝上吧! [root@cloud ~]# ssh vbird@192.168.10.53 [vbird@localhost ~]$ sudo su - [root@localhost ~]# yum -y install fio iperf3 policycoreutils-python-utils setroubleshoot* [root@localhost ~]# poweroff
很簡單!這樣就好了!未來使用這個 demo1.img 作為 backing_file 所設計出來的 overlay, 裡頭就擁有 SELinux 運作所需要的 debug 軟體了!喔耶!