systemd/沙箱
systemd 允許用戶加固並把系統服務單元放到沙箱中運行。由於技術限制,以及諷刺的是,出於安全方面的考量,用戶服務單元不能被加固或放到沙箱中,因為這可能導致權限提升相關的問題。不過這不會影響使用 User= 的系統單元。
從傳統意義上來說,只有服務單元可以被加固或沙箱化處理。詳情請參考 systemd.exec(5) 以及 systemd.resource-control(5)。
概述
由於加固或沙箱化處理會限制應用行為,因此不可能應用所有沙箱化指令。例如網絡伺服器不應該使用 PrivateNetwork=true,因為它一般需要網絡訪問權限。
可以使用 systemd-analyze security unit 來列出所有沙箱化指令並生成一個評分,有助於幫您決定接下來可以嘗試開啟什麼設置。
Hello world 可以獲得一個接近完美的分數。沒有應用可以應用所有沙箱化設置。不幸的是,systemd 在沙箱化相關的配置有誤時給出的錯誤信息有時是模糊的,甚至具有誤導性。臨時將日誌等級設為 debug 可以獲得真正有價值的信息。
# systemctl log-level debug
能力 (Capabilities)
capabilities(7) 可用於授予進程特定的特權。例如,CAP_NET_BIND_SERVICE 可以讓非特權進程綁定低於 1024 的埠,從而避免以 root 特權啟動進程。另一個顯著的例子是可以授予備份程序 CAP_DAC_READ_SEARCH 以允許其讀取任意文件。
- 建議始終同時使用
CapabilityBoundingSet和AmbientCapabilities。 -
systemd-analyze capabilities可以列出 systemd 可使用的能力。當您運行新增了能力的實驗性內核時,這個命令會很有用。
要想採用上述建議,您可以在編輯服務單元時使用 AmbientCapabilities= 來授予其能力,並使用 CapabilityBoundingSet= 來確保沒有預期外的能力被授予:
example.service
[Service] AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE
還可以在 /etc/systemd/system.conf 中設置 CapabilityBoundingSet= 或在 /etc/systemd/systemd.conf.d 中創建 drop-in 配置文件來移除繼承自 PID 1 以及其子進程的能力。不過大部分能力會被實際使用而不應該被移除。
常見指令
大部分指令可以在不引起太多問題的情況下應用到大多數應用中。
沒有特殊配置的指令
簡單的布爾設置,可以為啟用或禁用。它們不能配置細節。
| 指令 | 影響1 | 破壞性2 | 備註 |
|---|---|---|---|
LockPersonality |
中 | 低 | |
MemoryDenyWriteExecute |
中 | 中 | 不兼容運行時動態生成代碼的程序,例如 JIT、可執行棧[1]以及 C 編譯器生成的代碼「蹦床」。可以使用 SystemCallFilter 增強。
|
NoNewPrivileges |
高 | 低 | |
PrivateDevices |
中 | 低 |
/dev/null 等類似設備不會被禁用。
|
PrivateNetwork |
高 | 非常高 | 不允許任何網絡訪問。 |
PrivateTmp |
中 | 低 | |
PrivateUsers |
高 | 高 | |
RestrictRealtime |
低 | 低 | 可能有助於防止拒絕服務情況出現。 |
RestrictSUIDSGID |
中 | 低 | 建議和 NoNewPrivileges 一起使用。
|
- 指令產生的效果
- 指令破壞程序正常運行的可能性
可配置的指令
這些指令大部分都很有效而不會產生過多的影響。推薦考慮使用一些以下的指令。
| 指令 | 值 | 影響1 | 破壞性2 | 備註 |
|---|---|---|---|---|
ProtectSystem |
strict |
非常高 | 非常高 | 通常和 ReadWritePaths= 一起使用。
|
full |
高 | 中 | 可能會破壞如使用 ACME 來更新密鑰的網絡伺服器等應用,因為它們需要的文件可能位於 /etc。
|
|
true |
高 | 中 | 理論上基本沒有程序會往 /boot 和 /usr 寫入。
|
|
ProtectHome |
true |
高 | 中 | 一些應用可能需要將持久化數據存儲於 XDG_CONFIG_HOME3。
|
tmpfs |
高 | 中 | 家目錄包含大量敏感數據,應用 tmpfs 或 true 也許能夠避免洩露。4
|
|
read-only |
低 | 低 | 非常合適用於備份服務。 | |
ProtectProc5
|
invisible |
高 | 中 | |
noaccess |
中 | 中 | ||
RestrictAddressFamilies |
例如 AF_UNIX AF_INET AF_INET6
|
低 | 低 | 填寫用空格分隔的地址族,詳情請參考 address_families(7)。 |
- 指令產生的效果
- 指令破壞程序正常運行的可能性
-
StateDirectory=可以用於緩解一些負面後果。 - 這也會使
/run/user/不可訪問,避免使用中的 IPC socket 洩露。理論上來說,sockets 也有可能在另外地方,例如/tmp。 - 當指令被省略時,默認為
/proc的 hidepid 掛載選項的值。通常來說為0(無限制)。
進階指令
這些指令對多數單元來說不是很有用,因此很少用到。
| 指令 | 值 | 影響 | 破壞性 | 備註 |
|---|---|---|---|---|
DynamicUser |
布爾值,例如 true
|
中 | 中 | 需要和 StateDirectory=、RuntimeDirectory=、CacheDirectory=、LogsDirectory=、ConfigurationDirectory= 結合使用。[1]
|
ProtectClock1,2
|
布爾值 | 低 | 中 | 有些用戶返回當該指令啟用時,smartctl 不能運行,不過該指令應該是安全的。
|
ProtectControlGroups1
|
布爾值 | 中 | 低 | |
ProtectHostname1,2
|
布爾值 | 低 | 低 | |
ProtectKernelLogs1
|
布爾值 | 低 | 低 | 所有官方內核都將 SECURITY_DMESG_RESTRICT 設置為 y,不過這個選項仍然可以達成縱深防禦。
|
ProtectKernelModules1
|
布爾值 | 中 | 低 | |
ProtectKernelTunables1
|
布爾值 | 低 | 低 | |
RestrictFileSystems |
例如 ext4 tmpfs
|
中 | 中 | 填寫用空格分隔的文件系統列表或指定集合,如 @network。詳情請運行 systemd-analyze filesystemd。
|
SystemCallArchitectures |
例如 native
|
低 | 低 | 另請參閱 #禁止非原生架構系統調用;對單元文件設置這個指令可以覆蓋系統默認配置,從而把特定單元排除在系統限制之外。 |
SystemCallFilter |
例如 @system-service
|
中 | 高 | 另請參閱 systemd.exec(5) § SYSTEM CALL FILTERING;少寫哪怕一個系統調用都可能使您的應用在不合時宜的情況下崩潰。 |
SocketBindAllow/Deny |
例如 SocketBindAllow=ipv4:22、SocketBindDeny=any
|
中 | 中 | 建議和 CAP_NET_BIND_SERVICE 一起設置以確保特權上下文只能綁定預期的埠。
|
- 如果服務單元未以 root 運行或通過
AmbientCapabilities=等指令被授予特權,則設置該指令是多餘且不必要的。如果服務單元以另一個低權限普通User=運行,則這些設置完全是多餘的並可以被安全地忽略。 - 僅限制應用執行相關的系統調用,而不限制應用通過 systemd 提供的 IPC 服務(即 systemd-timedated 以及 systemd-hostnamed)來訪問。[2]如果需要絕對安全,務必注意同時限制涉及 D-Bus/Varlink 的方法。
chroot jail
通過指定 TemporaryFileSystem=/:ro 並只掛載需要的路逕到這種類 chroot jail 以激進地限制進程能看到的文件是可能的。
RootDirectory 要求路徑必須存在,而 TemporaryFileSystem 則不需要,且會無縫修改 /。由於沒有使用 chroot 系統調用,兩者,尤其是後者,看起來都屬於安全的類 chroot 指令,無法被輕易逃逸。
ProtectSystem 和 ProtectHome 這兩者與 TemporaryFileSystem=/:ro 不兼容且會導致後者失效,導致 / 再次可見。不過這兩個指令都不需要,因為可見的路徑是由白名單決定的。請參閱上面的警告,出現這個問題的原因是這些指令之間的相互作用並不被支持或納入考量。所有需要的路徑都需要通過 BindReadOnlyPaths 和 BindPaths 掛載到 jail 中:
example_jailed_unit.service
[Unit] Description=示例單元 [Service] ExecStart=/home/someuser/executable User=someuser Group=someuser TemporaryFileSystem=/:ro PrivateTmp=true BindReadOnlyPaths=/usr/lib /lib64 /lib BindPaths=/home/someuser/executable
以上只是一個最小的示例而大多數應用需要更多的白名單路徑。常見的有:
-
/etc/ca-certificates、/etc/ssl /etc/resolv.conf/usr/share/zoneinfo- 任何需要使用的 sockets,如
/var/run/mysqld/mysqld.sock
首次嘗試把一個單元放到沙箱中運行往往需要調試。如果它完全不能啟動或以 status=203/EXEC 失敗,則說明無法訪問可執行文件或需要的庫文件。可以從寬泛的路徑(如允許整個 /usr)開始,逐步縮小可訪問的路徑範圍。
system.conf
/etc/systemd/system.conf 的更改是全局性的,因此會影響所有單元。另請參見 systemd-system.conf(5)。
禁止非原生架構系統調用
非原生架構二進制文件,大部分情況下是 32 位二進制文件,可能一定程度上損害系統安全性,因為部分加固並不能良好工作。此前也有過影響非原生系統調用的低危害漏洞,例如 CVE-2009-0835。
file not found 錯誤。/etc/systemd/system.conf
SystemCallArchitectures=native
這個配置在大多數系統上都可以正常工作,但如果安裝了 multilib 的軟體包的話,就需要在一部分情況下禁用。尤其注意使用 Wine 玩遊戲可能會受影響。使用 systemd-run 或修改會話切片可以按需覆蓋 SystemCallArchitectures 配置以禁用限制。
啟用更多單元統計
systemd 默認情況下不會跟蹤一個單元的所有資源使用情況。開啟 Default*Accounting 可以在 systemctl status 輸出和日誌中查看更多統計信息。嚴格意義上來說,這不是一個安全設置,但是它確實可以使調試更簡單,並有助於深入了解資源使用情況。
/etc/systemd/system.conf
DefaultIOAccounting=yes DefaultIPAccounting=yes DefaultMemoryAccounting=yes DefaultTasksAccounting=yes
參考資料
https://docs.rockylinux.org/10/guides/security/systemd_hardening/
- ↑ 譯者註:真有程序還在用這玩意嗎?