SELinux旅程Part2-啟動SELinux

先附上自製影片, 之後會再持續更新哦!
SELinux操作教學[Youtube] : 

用 Ubuntu PPA 開啟 SELinux 且運行在 enforcing mode[Youtube] :
Ubuntu PPA(Personal Package Archives)可以讓我們自行製作 Package 並透過 apt 套件管理工具下載。有鑒於社群最近修掉了一個 issue,此 PPA 未來會再進行更新,到時候就不必修改核心(Kernel)囉。

前言

Security-Enhanced Linux(SELinux)早在 2003 年時被納入 Linux Kernel 2.6,也就是說,此版本後你可以從 Kernel 設定檔中開啟 SELinux 的安全模組功能,SELinux 的安全模組建立在 Linux 安全模組框架(Linux Security Modules)上,此框架也支援其他安全模組,例如 AppArmor、Smack、TOMOYO Linux 等等。

從 Kernel 設定檔中開啟 SELinux 的安全模組功能就代表開啟了 SELinux 防護嗎? 很可惜並不是!
使用者空間(Userspace)中存在著一些幫手,SELinux-aware Applications,在系統建置時期,也就是開發階段,SELinux-aware 程式依照我們預先設定的 SELinux 設定檔(SELinux Configuration Files)來將系統資源標上標籤(Label),系統運行時,init 或 systemd 之類的 init system,會將 SELinux 政策二進位檔(SELinux Policy Binary)透過讀寫 SELinux Filesystem(selinuxfs)釋出的界面載入 Kernel 之中,最後根據設定檔中要開啟的 SELinux 模式(SELinux Mode)來運行至此,我們才能說 SELinux 防護已啟動

補充說明,在 SELinux 世界中存取主體(Subject)和資源(Object)會被標上標籤(Label),而 SELinux 政策二進位檔中的內容就是在描述存取主體標籤和資源標籤之間的存取關係,我們這邊先統稱 SELinux 政策代表存取控制規則的集合,關於 SELinux 存取控制的設計我們之後會專門寫一篇來分享。

圖1來自官方手冊,The SELinux Notebook 4th Edition,展示了整個 SELinux 的系統架構,我們接下來會分三部分,核心空間、使用者空間、SELinux 政策與 SELinux 設定檔,分享開啟 SELinux 防護所需的要件,了解內部運作後,對於使用以及除錯上一定會有所幫助。



圖 1, SELinux 架構


核心空間(Kernel Space)

圖 2, SELinux 核心空間

概念


前面有提到 SELinux 的安全模組建立在 Linux 安全模組框架(Linux Security Modules)上,何謂 Linux 安全模組框架呢? 起初的設計理念即是為了支援多種安全模組,以達到小幅度改動核心且實行更進一步的存取控制功能,例如委任式存取控制(MAC)。
Linux 安全模組框架在核心中提供許多的插入點(LSM Hooks),這些插入點應存在於核心中有安全疑慮之處,當程式流程執行到此插入點時,即會跳轉到載入的安全模組(e.g. SELinux)中進行檢查。

以使用者開啟檔案為例,當使用者開啟檔案時,會透過系統呼叫進入核心使用核心服務(Linux Kernel Services),如圖2,執行過程中遇到插入點就會先跳轉到對應的 SELinux 核心服務(SELinux Kernel Services)進行檢查,檢查過程中會去查詢是否此存取行為(Action)符合預先載入的 SELinux 政策規範,檢查完後會回到跳轉來的地方繼續執行。 這邊要重點提到,在 SELinux 核心服務中進行檢查時,會根據設定來選擇是否要將檢查結果紀錄成稽核訊息(Audit Message)並透過 Audit Services 記錄在 Audit Log 中,因此,並不是所有的檢查都會被記錄成訊息哦!

從圖2上方可以看到 selinuxfs 檔案系統,負責使用者空間中的程式與核心中 SELinux 模組的溝通,程式可以透過讀寫 selinuxfs 檔案系統釋出的介面,來與 SELinux 模組交換資訊,例如獲取當前 SELinux 狀態、載入政策等等。
詳細有關 SELinux 核心內容我們以後會專門做一篇來講解。

實作

這邊使用 Linux Kernel 5.1 版本,在核心中 SELinux 功能需要透過核心設定(Kernel Configuration)來開啟。
在核心程式碼中執行以下指令,啟動
核心設定選單。
make menuconfig
為了開啟 SELinux 我們必須先開啟其它功能,例如 audit,因為 SELinux 模組本身需要使用到們,選項如下。
CONFIG_NET
CONFIG_INET
CONFIG_AUDIT
CONFIG_SECURITY
CONFIG_SECURITY_NETWORK
到這邊 CONFIG_SECURITY_SELINUX 就會出現在選單上,可以再針對自身需求微調 SELinux 功能。
另外,SELinux 需要檔案系統支援擴展文件屬性(Extended Attribute),也就是圖中的xattr,以我本身來說,我的 root filesystem 使用 ext4 檔案系統,所以需要 ext4 檔案系統支援擴展文件屬性。
CONFIG_EXT4_FS
CONFIG_EXT4_FS_SECURITY
如果有使用到其他檔案系統,例如 tmpfs,也可以選擇開啟支援 Extended Attribute。
CONFIG_TMPFS
CONFIG_TMPFS_XATTR
擴展文件屬性(Extended Attribute)簡單來說就是檔案系統中,每個檔案用來儲存關於自身額外資訊的地方,例如 SELinux 的標籤(Label)就以 key-value 的形式被記錄下來。
最後,我們可以把 SELinux 設定成預設的安全模組
CONFIG_DEFAULT_SECURITY_SELINUX
CONFIG_LSM="selinux"
或是透過設定核心參數,使得在開機時指定使用 SELinux 模組,記得開啟 CONFIG_SECURITY_SELINUX_BOOTPARAM 才能使用 selinux 核心參數
security=selinux selinux=1

使用者空間(User Space)


圖 3, SELinux 使用者空間

概念

如果 SELinux 只開啟了核心部分的功能,並無法發揮它的功力,必須配合使用者空間中所謂 SELinux-aware Application 一起運作。
什麼是 SELinux-aware Application 呢? 間單來說就是知道 SELinux 的存在並與之互動的程式,打個比方就是能感知到幽靈的人我們就稱他為"幽靈-aware"!
根據感知的程度我們提出了一個分類,如下圖。
圖 4, SELinux-aware 程度
圖4中,最低的感知程度即為"不知道" SELinux 的存在,例如 rm。 緊接著是"知道" SELinux,但不是必須的程度 ,例如使用 ps 或是 ls 我們可以知道程序或是系統資源被標上什麼標籤(Label),但沒有這些功能也不影響 SELinux 的運作,只是管理者可能會難過。

有些程式會透過讀寫 selinuxfs 中的介面去跟核心中的 SELinux 模組交換資訊,舉例來說 getenforce 程式會讀取 selinuxfs 底下的 enforce 檔案,即可獲得當前 SELinux 是以什麼模式(e.g. permissive mode)運行,此種會存取 selinuxfs 介面但並不需要特殊存取權限(e.g. setenforce 權限,所謂的特殊權限是我們在分類時,自己認為會影響系統運行的權限)的程式歸為一個程度。

最高程度的 SELinux-aware Application 運行會涉及到特殊權限,也就是會一定程度上影響系統的運行,例如我們可以透過 setenforce 工具(需要 setenforce 權限)動態改變 SELinux 目前的運行模式,此種 SELinux-aware Application 中經典的例子為開機流程中 init system 和使用者登入。

Init System 與使用者登入

圖 5, Linux 開機流程
開機過程中,當核心載入且初始化完成後,執行流程來到使用者空間中,此時 init system 開始運行,根據使用情境的不同,init system 可能為 SysV-style init、OpenRC、Systemd 等等,不管是哪一種都必須讓 SELinux 政策在所有程式跑起來前載入核心中,因此如圖5,init system 會將政策載入且重新執行自己來讓自己被納入 SELinux 的規範中 (會有一對應的 init 政策)。
接著 init system 開啟服務且為使用者登入做準備,這邊以 Getty 和 Login 為例, 系統要求使用者輸入使用者名稱和密碼,進行使用者認證後會去讀取 SELinux 設定檔,以查詢此 Linux 使用者所對應的 SELinux 使用者名稱,再經由此 SELinux 使用者名稱找出該使用者後續程序(e.g. bash)應該用什麼標籤(Label)來運行。

標上標籤(Label)

當使用者登入後會去存取系統資源,此時如何為系統資源標上標籤(Label)也是很重要的一件事情,在系統建制時期,透過使用 setfiles 等相關的工具,依據 SELinux 的設定檔(e.g. file_contexts)來進行標上標籤的動作。

由前面的敘述我們來複習一個重要的概念,管理員必須知道他想怎麼保護系統資源,因此管理員必須了解系統運作且經過風險評估來知曉資源的重要程度,以在系統建置時期幫資源標上適合的標籤,也透過調整 SELinux 設定檔來讓使用者登入時運行在最適合的標籤,使程序主體(Subject)運行在最小權限(Least Privilege)中

實作

通常 SELinux-aware Application 運作時會使用到 SELinux library,例如 libselinuxlibsepollibsemanage 等等,這些 library 提供的函式使程式與 selinuxfs 介面互動更加方便,我們可以透過下面的指令簡單來看看程式是否需要使用到 SELinux library
ldd /bin/ls
系統建置時期,我們需要安裝這些與 SELinux 相關的 library 以及輔助工具,例如 setfiles、getenforce 等等,除此之外一些 Linux 程式也需要開啟支援 SELinux,例如 init system 和使用者登入。
如果正在使用的 Linux 發行版(distribution 簡稱 distro) 有幫我們維護這些 library 和工具,就可以直接透過 distro 的套件管理工具(e.g. apt-get or yum)安裝下來,鑑於網路上已經有蠻多關於這方面的教學,這邊就不再一一贅述。
那如果我們想從頭開始建置一個系統,核心到使用者空間都自己調整,要怎麼自己編譯這些東西呢? 沒問題!程式碼可以在官方的 Github 找到。 進到裡頭後,我們基本需要的 library 和工具都包含在 SELinuxProject/selinux,至於編譯以及安裝的方式可以參考 README,編譯前要注意相依性的部分,需要先安裝編譯時所需的軟體,官方是以 yum 為例子,下面提供我在 Ubuntu 上所需的軟體。
apt-get install flex libpcre3-dev 
libglib2.0-dev libbz2-dev libaudit-dev bison xmlto gettext libcap-dev 
libdbus-glib-1-dev libcap-ng-dev swig libpython3-dev gawk libpam0g-dev
安裝完上述軟體後即可按照 README 內容進行編譯。
本文的最後會附上從頭開始在 Ubuntu 上開啟 SELinux 的 shell script,直接執行後並重新開機即可讓 Ubuntu 開啟 SELinux 且運行在 permissive mode,如果想運行在 enforcing mode 則必須要自行設定政策的內容哦!

SELinux 政策(SELinux Policy)與 SELinux 設定檔(SELinux Configuration Files)

不管是在系統建置時期抑或是 SELinux 運行時期,SELinux 提供了許許多多的設定檔來供我們便利的調整其功能,分為通用設定檔(Global Configuration Files)和政策設定檔(Policy Configuration Files),以使存取控制的結果符合我們心中的樣貌,例如 /etc/selinux/config 這個通用設定檔中就描述了 SELinux 將以什麼模式運行以及想要使用的政策目錄名稱,而相關的政策設定檔都會放在 /etc/selinux/政策目錄名稱 之下。
# This file controls the state of SELinux on the system on boot.

# SELINUX can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=permissive

# SELINUXTYPE can take one of these four values:
# targeted - Only targeted network daemons are protected.
# strict   - Full SELinux protection.
# mls      - Full SELinux protection with Multi-Level Security
# mcs      - Full SELinux protection with Multi-Category Security 
#            (mls, but only one sensitivity level)
SELINUXTYPE=targeted
另外,前面提到過 setfiles 會依據 SELinux 設定檔來為檔案系統上的資源標上標籤,此設定檔即為 file_contexts 和 file_contexts.homedirs 政策設定檔,其內容如下所示。
...
/mnt(/[^/]*)    -l      system_u:object_r:mnt_t
/mnt(/[^/]*)?   -d      system_u:object_r:mnt_t
/dev/.* system_u:object_r:device_t
/etc/.* system_u:object_r:etc_t
/opt/.* system_u:object_r:usr_t
...
基本上格式由 "檔案路徑"、"檔案類型"、"標籤" 所組成,檔案路徑可以包含正規表示式(Regular Expression)。 setfiles 運行時會將兩個檔案合併後分為 包含 以及 不包含 正規表示式的路徑,一開始會從 "不包含正規表示式的路徑" 找起,找到後會直接使用並不會繼續往後找,因此通常在除錯時得小心是否因為此原因而無法標上預期的標籤。

除了這些與政策相關的基本設定檔外,最重要的就是 SELinux 政策二進位檔,管理者需構思與撰寫 SELinux 政策原始檔(SELinux Policy Source),並將其轉換為 SELinux 政策二進位檔,此政策二進位檔一般存放於 /etc/selinux/政策目錄名稱/policy/ 中,可以透過 selinuxfs 釋出的介面載入核心之中,SELinux 政策二進位檔由 SELinux 核心模組進行解析並不依賴於任何平台,但會依賴於核心的版本,根據核心版本不同能支援的政策語法也不同,我們可以透過 sestatus 工具,來看看核心最高支援的政策版本。
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             refpolicy
Current mode:                   permissive
Mode from config file:          permissive
Policy MLS status:              disabled
Policy deny_unknown status:     denied
Memory protection checking:     requested (insecure)
Max kernel policy version:      31
最後一行即為核心最高支援的政策版本,至於該如何撰寫以及如何產生自己的 SELinux 政策呢?有待之後的篇章來分享,在這邊我們使用官方提供的 Reference Policy,此政策 旨在提供一般來說系統上最通用的存取控制規範,每個 distro 會修改 Reference Policy 使其符合自身的需求

最後附上從頭開始如何在 Ubuntu 18.04 開啟 SELinux 的方法(政策以及工具都是自行編譯),請在家目錄下直接執行後並重新開機即可讓 Ubuntu 開啟 SELinux 且運行在 permissive mode
apt-get udpate
apt-get install git vim make build-essential flex libpcre3-dev 
libglib2.0-dev libbz2-dev libaudit-dev bison xmlto gettext 
libcap-dev libdbus-glib-1-dev libcap-ng-dev swig libpython3-dev 
gawk libpam0g-dev

git clone https://github.com/SELinuxProject/refpolicy.git
git clone https://github.com/SELinuxProject/selinux.git

cd ~/selinux

sed -i -e "s/\(\$(PYTHON) setup.py install.*\)/\1 \`grep -qxF ID=ubuntu 
\/etc\/os-release \&\& echo --install-layout deb\`/" 
libselinux/src/Makefile      #unresolved issue

make LIBDIR=/usr/lib/x86_64-linux-gnu SHLIBDIR=/lib/x86_64-linux-gnu \
install install-pywrap relabel

cat > /etc/selinux/config <<EOF
# This file controls the state of SELinux on the system on boot.
# SELINUX can take one of these three values:
#       enforcing - SELinux security policy is enforced.
#       permissive - SELinux prints warnings instead of enforcing.
#       disabled - No SELinux policy is loaded.

SELINUX=permissive

# SELINUXTYPE can take one of these four values:
#       targeted - Only targeted network daemons are protected.
#       strict   - Full SELinux protection.
#       mls      - Full SELinux protection with Multi-Level Security
#       mcs      - Full SELinux protection with Multi-Category Security 
#                  (mls, but only one sensitivity level)

SELINUXTYPE=refpolicy
EOF

sed -i -e 's/^GRUB_CMDLINE_LINUX=\"\(.*\)\".*/GRUB_CMDLINE_LINUX=\"\1 
security=selinux selinux=1\"/g' /etc/default/grub

update-grub

cd ~/refpolicy

sed -i -e 's/^#DISTRO.*$/DISTRO = debian/g' build.conf
sed -i -e 's/^DIRECT_INITRC.*$/DIRECT_INITRC = y/g' build.conf
sed -i -e 's/^SYSTEMD.*$/SYSTEMD = y/g' build.conf
sed -i -e 's/^MONOLITHIC.*$/MONOLITHIC = y/g' build.conf
git checkout RELEASE_2_20190201

make conf
make policy
make install
make resetlabels
#sudo make load (This line is used in moduldar mode)

#Kill apparmor
systemctl stop apparmor
systemctl disable apparmor
apt-get purge apparmor

留言