Contents

Linux - 安全增強式 Linux ( SELinux )

安裝好系統後,第一件事情 disable selinux,然後 reboot ,這也是大多是網路上軟體安裝的教學,不過什麼是 SELinux ? 到底有什麼好處呢 ? 關掉是為何 ?

1. SELinux 介紹

SELinux 是一個Linux核心的安全模組,最早是由美國國家安全局(NSA)開發的專案,後續由 Redhat 編譯至 Linux 核心內, 所有的 RHEL 系列 (Redhat、Fedora、CentOS、Rocky Linux、 Oracle Linux、AlmaLinux 等) 均內建此功能。

SELinux 主要就是在每一個程序畫一個小圈圈,並對程序及檔案貼上 Type (類似一種標籤概念) ,只有符合該 Type 才能存取,就算是 root 啟動的程序也不能隨便存取,限制程序管控在最小權限範圍內,如下圖 Redhat 官方對比 Apache 及 MariaDB 限制

https://i.imgur.com/Sro54MN.png

透過上圖明顯說明,只有 httpd 才能存取 httpd_sys_content_t 的 type, 確保就算 Apache 有安全漏洞也不會影響 mariadb 安全性。

SELinux 是遷入在核心內的,只要開啟後,所有要訪問檔案前,會先透過 SELinux 檢查是不是合法,一旦並非在規則裡的檔案,會被拒絕。另外規則已經由 Redhat 建立數萬筆規則,不過是可以由使用者另外自定義所要的規則並寫入 DB 內。


2. SELinux 工作模式

SELinux 有三種工作模式:

  1. Enforcing 啟用 - 預設模式
  2. Permissive 啟用 - 不會限制存取,只會透過 log 輸出警告
  3. disabled 禁用

2.1 查看現在的工作模式

1
2
[root@servera ~]# getenforce 
Enforcing

2.2 工作模式切換

1
setenforce < 0 | 1 >

2.2.1 Permissive 模式

1
2
3
[root@servera ~]# setenforce 0
[root@servera ~]# getenforce 
Permissive

2.2.2 Enforcing 模式

1
2
3
[root@servera ~]# setenforce 1
[root@servera ~]# getenforce 
Enforcing

2.2.3 disabled 模式

停用 SELinux 並需修改 /etc/selinux/config ,將模式改為 disabled ,並重新開機才會生效

1
2
3
...
SELINUX=disabled
...

3. SELinux 權限查看

與一般查看檔案及目錄方式一樣,參數加上一個大寫 Z 即可看到 SELinux 的 type

1
2
3
4
5
[root@servera html]# ls -lZ
total 4
-rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0 14 Feb 12 14:51 index.html
[root@servera html]# ls -lZd /var/www/html/
drwxr-xr-x. 2 root root system_u:object_r:httpd_sys_content_t:s0 24 Feb 12 14:51 /var/www/html/

4. SELinux 權限限制

4.1 測試準備

    1. 安裝 httpd 並啟動
    1. 複製 /etc/shadow 檔案 至 /var/www/html/ ,並賦予 777 權限
    1. 確認 rsyslog.service 已經啟動 ( 如果沒有該服務,使用 yum install rsyslog -y 安裝)
    1. 確認已經安裝 setroubleshoot-server 套件 ( 如果沒有,使用 yum install setroubleshoot-server -y 安裝)
    1. 防火牆開放 http 服務
Tip
rsyslog 及 setroubleshoot-server 兩個套件可以很方便的把 SELinux 攔截記錄在 audit.log 中的檔案,轉譯比較容易閱讀的 log
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 安裝 httpd
yum install -y httpd
# 啟動 httpd 服務
systemctl start httpd
# 複製 /etc/shadow 至 /var/www/html/ 目錄
cp -a /etc/shadow /var/www/html/
# 賦予權限
chmod 777 /var/www/html/shadow
# 確認 rsyslog 啟動
systemctl status rsyslog.service
# 確認已經安裝 setroubleshoot-server 套件
rpm -q setroubleshoot-server
# 防火牆開放 http 服務
firewall-cmd --add-service=http

4.2 驗證 SELinux

  1. 使用 ls -lZ 檢查SELinux /var/www/html 內檔案的 type ,會發現 shadow 檔案的 type 為 shadow_t ,並且 shadow 檔案是所有人都有 r,w,x 最高權限
1
2
3
4
[root@servera ~]# ls -lZ /var/www/html/
total 8
-rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0   14 Feb 12 14:51 index.html
-rwxrwxrwx. 1 root root system_u:object_r:shadow_t:s0                1970 Feb 12 14:49 shadow
  1. curl 127.0.0.1 確認 http 的 index.html 是可以正常訪問後,嘗試查看 shadow 檔案,會顯示 403 被系統拒絕。
1
2
3
4
5
6
7
8
[root@servera ~]# curl 127.0.0.1/shadow
<!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>
  1. 查看 /var/log/messages ,最新的 log 會看到一段 SELinux is preventing /usr/sbin/httpd ... ,表示 SELinux 已經成功阻擋該檔案被讀取
1
2
3
4
[root@servera ~]# tail /var/log/messages 
...
Feb 12 17:03:48 servera setroubleshoot[3706]: SELinux is preventing /usr/sbin/httpd from getattr access on the file /var/www/html/shadow. For complete SELinux messages run: sealert -l 5bf90ec7-0c4c-4484-bdf2-a6926a49c814
...
Note
SELinux 是非常強大,就算權限是 777 也因 type 規則不合法被拒絕,此方案也可以防止因系統管理員人為疏忽開較大權限或是使用到不規範檔案,被 SELiunx 攔截擋住漏洞

5. SELinux 權限編輯

上述我們看到了 SELinux 可以攔截非法 type ,但是如果要讓 shadow 可以在 http 頁面上讀取該如何設定呢? 這時候就可以透過工具來編輯 type ,但是我們要怎麼知道哪一個 type 才是合法的呢? 藉由查看該目錄的其它可以正常訪問的 type 來對照是一種方式,另一種就是查看 SELinux 存在放 DB 的規則。

5.1 對照同一個目錄的 type

index.html 是可以正常訪問的,該檔案的 type 為 httpd_sys_content_t ,藉由 chcon 工具把 shadow 貼上新的 type

1
2
3
4
5
6
7
8
9
[root@servera ~]# ls -lZ /var/www/html/
total 8
-rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0   14 Feb 12 14:51 index.html
-rwxrwxrwx. 1 root root system_u:object_r:shadow_t:s0                1970 Feb 12 14:49 shadow
[root@servera ~]# chcon  -t httpd_sys_content_t /var/www/html/shadow 
[root@servera ~]# ls -lZ /var/www/html/
total 8
-rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0   14 Feb 12 14:51 index.html
-rwxrwxrwx. 1 root root system_u:object_r:httpd_sys_content_t:s0     1970 Feb 12 14:49 shadow
Tip
chcon 工具修改的是存放在 RAM 的 SELinux ,修改完後可以直接嘗試訪問 curl 127.0.0.1/shadow ,不過缺點就是只要重新開機後,RAM 的資料會被消除會重新載入在 DB 設定的標籤

5.2 查看 SELinux DB 規則

使用 semanage fcontext -l 工具,並篩選查看 /var/www 底下的目錄 type 規則,可以看到在 /var/www/ 底下的所有檔案都適用 httpd_sys_content_t 的 type,就可以透過 chcon 工具重新貼 type了

1
2
3
[root@servera ~]# semanage fcontext -l | grep /var/www
/var/www(/.*)?                                     all files          system_u:object_r:httpd_sys_content_t:s0 
...

5.4 修改 SELinux DB 規則

上述說到 chcon 工具修改的是存放在 RAM 的 SELinux 的規則, 如果今天 httpd 讀取的目錄是並非預設 /custom 每次重新開機就要重新執行一次,不過我們可以手動寫入規則, 讓 /custom 目錄合法化

5.4.1 測試準備

    1. 創建 /custom 目錄,寫一個 index.html 檔案內容為 “example” ,並賦予檔案及資料夾為 777 權限
    1. 編輯 httpd 設定檔,將虛擬伺服器路徑改為 /custom
    1. 重啟 httpd
1
2
3
4
5
6
# 創建 /tmp/example 目錄
mkdir -p /custom
# 創建一個 index.html 頁面
echo "example" > /custom/index.html
# 賦予 777 權限
chmod -R 777 /custom

編輯 /etc/httpd/conf/httpd.conf

1
2
3
4
5
6
7
8
# 將原本的 /var/www/html 改成 /custom
...
DocumentRoot "/custom"
...
<Directory "/custom">
...
# 註解最下方 IncludeOptional conf.d/*.con
# IncludeOptional conf.d/*.con
1
2
# 重啟 httpd
systemctl restart httpd

5.4.2 驗證 SELinux

至瀏覽器查看或是使用 curl 127.0.0.1 ,會出現 403 禁止,被 SELinux 認定為不合法規則

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[root@servera custom]# curl 127.0.0.1
<!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@servera custom]# ls -lZ /custom/
total 4
-rwxrwxrwx. 1 root root unconfined_u:object_r:default_t:s0 8 Feb 12 19:10 index.html
Tip
如果不確定 httpd 配置是否正確,可以先使用 setenforce 0 將 SELinux 僅使用警告不攔截規則,確保不是 httpd 配置錯才顯示 403 沒有權限

5.4.3 自訂規則並寫入 DB

囿於 /custom 這個目錄本來就不存在預設規則內,所以創建的檔案會自動加上 default_t 的 type ,現在要寫入新規則告訴 SELinux 在 /custom 目錄是屬於 httpd_sys_content_t type

使用 semanage fcontext 工具將規則寫入 DB

1
2
# 寫入規則至 DB
[root@servera custom]# semanage fcontext -a -t httpd_sys_content_t  '/custom/(.*)?'
Note
最後面的 ‘/custom/(.)?’ 這是正規化表示法,表示任何在 /custom 底下的任意目錄都符合* 。

5.4.4 查看新寫入至 DB 規則

1
2
[root@servera custom]# semanage fcontext -l | grep /custom
/custom/(.*)?                                      all files          system_u:object_r:httpd_sys_content_t:s0 

5.4.5 將新規則覆蓋至 /custom

透過 restorecon 工具可以將存放在 DB 的規則倒出來,並驗證 curl 127.0.0.1 可以正常顯示

1
2
3
4
5
6
7
8
# 將新規則覆蓋至 /custom 底下全部目錄及檔案上
[root@servera custom]# restorecon -Rv /custom
Relabeled /custom/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
[root@servera custom]# ls -lZ /custom/
total 4
-rwxrwxrwx. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0 8 Feb 11 19:10 index.html
[root@servera custom]# curl 127.0.0.1
example

6. SELinux - boolean 權限規則

httpd 再啟動時,除了 root 指定的目錄顯示頁面之外,也能顯示一般 user 在家目錄的檔案。

使用 getsebool 查看 boolean 權限規則 , 預設 httpd_enable_homedirs 規則是關閉的。

1
2
[root@servera test]# getsebool -a | grep  httpd_enable_homedirs
httpd_enable_homedirs --> off

6.1 測試準備

    1. 安裝 httpd 並啟動
    1. 使用 student 帳號 (一般 user) 創建 /home/student/public_html 目錄
    1. 在 /home/student/public_html 創建一個 index.html 頁面
    1. 賦予一般使用者在 /home/student 有 r 的權限
    1. 編輯 httpd 的 /etc/httpd/conf.d/userdir.conf 設定
    1. 重啟 httpd
1
2
3
4
5
6
7
8
# 安裝 httpd 並啟動
sudo systemctl start httpd
# 使用 student 帳號 (一般 user) 創建 /home/student/public_html 目錄
mkdir -p ~/public_html
# 賦予權限
chmod 711 ~
# 創建一個 index.html 頁面
echo "This is student home" > /home/student/public_html/index.html

使用 root 編輯 httpd 的 /etc/httpd/conf.d/userdir.conf 設定

1
2
3
4
5
6
# 將 UserDir disabled 註解, 下方的 UserDir public_html 開啟
...
# UserDir disabled
...
UserDir public_html
...
1
2
# 重啟 httpd
systemctl restart httpd
Warning
如果在前一次實驗有把 /etc/httpd/conf/httpd.conf 最下方的 IncludeOptional conf.d/*.conf 註解起來的話,記得要把他打開!!

6.2 驗證 SELinux

至瀏覽器查看或是使用 curl 127.0.0.1/~student/index.html ,會出現 403 禁止,被 SELinux 認定為不合法規則

1
2
3
4
5
6
7
8
[student@servera ~]$ curl 127.0.0.1/~student/index.html
<!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>
Tip
如果不確定 httpd 配置是否正確,可以先使用 setenforce 0 將 SELinux 僅使用警告不攔截規則,確保不是 httpd 配置錯才顯示 403 沒有權限

6.3 設定 boolean 權限規則

使用 setsebool 工具修改規則,並驗證 curl 127.0.0.1/~student/index.html 可以正常顯示

1
2
3
4
5
[root@servera ~]# setsebool -P httpd_enable_homedirs on
[root@servera ~]# getsebool -a  | grep httpd_enable_homedirs
httpd_enable_homedirs --> on
[root@servera ~]# curl 127.0.0.1/~student/index.html
This is student home
Note
如果只有setsebool httpd_enable_homedirs on,僅修改 RAM 規則,開機就會失效,需要加上 -P 參數才會寫入DB並永久生效

7. SELinux - port 權限規則

httpd 再啟動時,除了預設port 之外也可以任意指定未使用的 port,假設把 port 改成開放在 82 port 要怎麼讓其它人訪問服務?

使用 semanage port -l 並篩選 http_port_t 查看預設有開放哪些 port , 82 Port 並沒有在預設清單內

1
2
[root@servera ~]# semanage port -l | grep http_port_t 
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

7.1 測試準備

    1. 安裝 httpd 並啟動
    1. 編輯 httpd 的 /etc/httpd/conf/httpd.conf 設定
    1. 重啟 httpd
1
2
# 安裝 httpd 並啟動
sudo systemctl start httpd

編輯 httpd 的 /etc/httpd/conf/httpd.conf 設定

1
2
3
4
# 將 Listen 80 改成 82
...
Listen 82
...
1
2
# 重啟 httpd
systemctl restart httpd
Note
重啟httpd會出現錯誤訊息是正常的,因為 SELinux 會禁止非法 port 啟動
Tip
如果不確定 httpd 配置是否正確,可以先使用 setenforce 0 將 SELinux 僅使用警告不攔截規則,確保不是 httpd 配置錯誤才顯示啟動錯誤

7.2 設定 port 權限規則

使用 semanage port 工具修改規則,並重啟 http 後驗證 curl 127.0.0.1:82 可以正常訪問

1
2
3
4
[root@servera ~]# semanage port -a -t http_port_t -p tcp 82
[root@servera ~]# systemctl restart httpd
[root@servera ~]# curl 127.0.0.1:82
example

8. 如何判斷是哪一個 SELinux 出錯?

從上述可以看出 SELinux 範圍很廣,有些是 type 權限規則、有些是 boolean 權限規則還有 port 權限規則,怎麼能快速定位到哪是哪一個規則出錯呢? 這時我們就可以透過 /var/log/message 訊息快速產生建議方式作處理。

Note

必須開啟的設定

    1. 確認 rsyslog.service 已經啟動 ( 如果沒有該服務,使用 yum install rsyslog -y 安裝)
    1. 確認已經安裝 setroubleshoot-server 套件 ( 如果沒有,使用 yum install setroubleshoot-server -y 安裝)

8.1 例如: httpd port 開放到非預設規則查看方式

  1. 將 httpd 修改至非規則內的 port ,重新啟動後查看 /var/log/message ,找到顯示 tcp_socket port 82 ... 那一行,最後面會提示執行 sealert -l e58f3dd4-a39e-4c37-b34c-054239a266e8 會顯示詳細訊息。
1
2
3
4
5
6
7
[root@servera ~]# systemctl restart httpd
Job for httpd.service failed because the control process exited with error code.
See "systemctl status httpd.service" and "journalctl -xe" for details.
[root@servera ~]# tail /var/log/messages 
...
Feb 12 21:08:57 servera setroubleshoot[5819]: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 82. For complete SELinux messages run: sealert -l e58f3dd4-a39e-4c37-b34c-054239a266e8
...
Warning
sealert -l ,後面的 id 是隨機的,大家的都不一樣,以 log 為顯示為準
  1. 查看 SELinux messages
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[root@servera ~]# sealert -l e58f3dd4-a39e-4c37-b34c-054239a266e8
SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 82.

*****  Plugin bind_ports (99.5 confidence) suggests   ************************

If you want to allow /usr/sbin/httpd to bind to network port 82
Then you need to modify the port type.
Do
# semanage port -a -t PORT_TYPE -p tcp 82
    where PORT_TYPE is one of the following: http_cache_port_t, http_port_t, jboss_management_port_t, jboss_messaging_port_t, ntop_port_t, puppet_port_t.
...

從上述就能看出 SELinux 會提示解決方式, semanage port -a -t PORT_TYPE -p tcp 82,就可以很容易定位到是哪一個規則錯誤,要如何進行修改

8. 小結

SELinux 可以 Linux 系統變成真正的高規格安全性的作業系統,雖然比較複雜也比較麻煩,但是如果伺服器是放在 DMZ 要供 Public 外部網路使用的話,就一定要開啟 SELinux ,等同啟動軍用規格電腦一樣安全。



如果你還沒有註冊 Like Coin,你可以在文章最下方看到 Like 的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下 Like,而 Like 最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!