FR-193 · 需求討論稿 · 2026-11-10
主系統與 Agent 兩邊的安裝包都改成 rootless Docker;程式與 image 不動
FR-180 把容器裡的程式全部改成非 root,這案把開容器的 Docker 引擎本身也改成非 root。要動的全在安裝程式、維運指令與客戶主機的系統設定,後端與 Agent 程式碼不改。這份給 PM 看:做什麼、客戶會感覺到什麼、哪些事要先決定。
客戶主機上的 Docker 引擎(開容器、接網路、掛磁碟的那支常駐程式)現在是用主機最高權限 root 跑。這案把它改成用一個專用的普通帳號 guidant 跑,九顆主系統容器與 Agent 端三顆容器都由這個帳號開。
這是 Docker 官方正式支援的執行模式,官方名稱就叫 Rootless mode。不是外掛、不是自製方案。
為什麼要做:FR-180 做完後,容器裡的程式已經不是 root,但萬一有人靠核心漏洞從容器逃出來,碰到的 Docker 引擎還是 root,等於拿到整台主機。改成 rootless 後,逃出來拿到的只是 guidant 這個普通帳號,動不了主機其他東西。資安稽核問「Docker 有沒有 rootless」時,答案從「沒有、理由如下」變成「有」。
不做什麼:
USER 1000、cap_drop、no-new-privileges 全部沿用。這一節是 PM 最需要知道的。
客戶打 https://主機 進站,不帶 port。登入、上傳、掃描、報表全部一樣。升級照樣 sudo ./install.sh --upgrade。
主機上多一個 guidant 系統帳號。客戶 IT 以 root 下 docker ps 會是空的(容器屬於 guidant 的 Docker),要改用 guidantai status/guidantai logs,或切到 guidant 帳號再下 docker 指令。手冊要明寫,否則客戶第一反應是「服務沒起來」。
安裝程式會動六項主機 OS 設定(見 §4),客戶 IT 會問。全部自動做、移除時自動還原,但要列在手冊讓他們知道。
rootless 的容器網路走使用者態 TCP/IP 堆疊,Docker 官方自己標「慢」。大檔上傳、Agent 回傳掃描結果的吞吐會降。第一棒要量實際數字,看是否要換官方列的較快驅動。
程式碼與 image 零改動。要動的分四層。
| # | 設定 | 為了什麼 | 不做會怎樣 |
|---|---|---|---|
| 1 | 建 guidant 系統帳號,並在 /etc/subuid、/etc/subgid 配一段子 uid(65536 個) |
rootless Docker 的前提,容器內身分對映到這段 | rootless Docker 裝不起來 |
| 2 | 開啟 guidant 的 linger(loginctl enable-linger) |
沒人登入時這個帳號的 Docker 也要開機自起 | 重開機後服務不回來,要有人登入才起 |
| 3 | 給 rootlesskit 執行檔加 CAP_NET_BIND_SERVICE |
讓 guidant 的 Docker 能綁 443 |
容器起不來,報 cannot expose privileged port |
| 4 | sysctl net.ipv4.ping_group_range=0 2147483647 |
讓容器裡的 ping 能開 ICMP socket | Agent 的 ping 永遠「無法連線」,網路診斷誤判 |
| 5 | cgroup v2 委派檔 /etc/systemd/system/user@.service.d/delegate.conf |
compose 的記憶體與行程數上限才生效 | 上限全部靜默失效,吃爆記憶體拖垮主機 |
| 6 | 裝 uidmap、slirp4netns(或 pasta)、dbus-user-session 套件 |
對映工具與使用者態網路 | rootless Docker 裝不起來 |
第 3 項有兩個官方做法:改 sysctl ip_unprivileged_port_start(全機所有帳號都能綁低 port)或只給 rootlesskit 這支執行檔加能力。建議後者,影響範圍只有我們自己,客戶資安問起好答。第 4 項放寬的只是「核心代填的 ICMP echo」,無法偽造來源、無法嗅探,且 Ubuntu 與 RHEL 預設本來就是全開,等於把 Debian 對齊到其他發行版的預設。
這六項在 Agent 端的被掃主機上要再做一次。
guidantai 54 處,每處改成以 guidant 帳號、指向它的 socket 執行。做法是改一支共用函式讓所有呼叫走它,不逐行改。require_root 拆段:建目錄、裝 systemd 單元、碰 TPM 仍要 root;起容器、看狀態、看 log 切到 guidant。/srv/guidant-ai 底下目錄 owner 換算:現在 chown 給 999(資料庫)與 1000(應用),rootless 下要改成「guidant 的子 uid 起點 + 999/1000」,每台主機的數字可能不同,安裝程式要讀 /etc/subuid 算。算錯的症狀是容器起不來或寫入靜默失敗、log 只有 permission denied。/run/guidant 解密設定目錄:現在 root 700,guidant 的 Docker 要讀得到才能把 guidant.env 掛進容器。密碼檔仍只有 root 與 guidant 能讀。guidant-secrets(要碰 TPM,留 root,但解完的檔要給 guidant)、guidant-upgrade-boot-check、guidant-upgrade-order(兩支都叫 docker,要能叫到 guidant 的 Docker)。guidantai uninstall 補還原:sysctl 檔、委派檔、linger、帳號、subuid 一起清掉。require_root 拆段、owner 換算、/run/guidant-agent。cap_add: [NET_RAW] 拿掉:rootless 下 cap_add 只在容器自己的 namespace 內有效(官方文件明寫),這行不會報錯但沒有作用。ping 改靠主機 sysctl 第 4 項。-I 走 ICMP 模式,那需要 raw socket。rootless 下要實測會不會壞,壞就只走預設 UDP 模式。rootless 要 cgroup v2 加 systemd 才有資源上限。目前支援清單 Ubuntu 22.04 以上、Debian 12 以上、RHEL 9 以上剛好都符合,門檻不用改。
四項全部定案(2026-11-10)。Agent 先出安裝包、主系統後續;D1 只有主系統要面對。
D1 主系統對外一律 8443(PM 2026-11-10 定案,取代同日稍早的路三)
不貼 rootlesskit 能力、不改 sysctl,compose 對外只映射 8443;80 的 http 轉址拿掉。客戶網址 https://<主機>:8443。理由:還沒出給客戶,改預設沒有相容成本;省掉 capability 升級重貼與主機掃描解釋。被排除:預設 443 貼能力(路三);全機 sysctl。只影響主系統(FR-193.1),Agent 對外本來就是 8444。
D2 ping sysctl 全開 0 2147483647(決策者 2026-11-10 定案)
與 Ubuntu、RHEL 出廠預設相同,等於把 Debian 對齊主流。放寬的只有核心代填的 ICMP echo,無法偽造來源、無法嗅探。被排除:只開 guidant 子 gid 區段——每台數字不同、客戶日後動 sysctl 可能蓋掉;客戶明確要求時再改。
D3 網路驅動用預設 slirp4netns(決策者 2026-11-10 定案)
Agent 流量是單向上傳掃描結果,對吞吐不敏感。第一棒在乾淨機量上傳 100 MB 的秒數留紀錄,真的影響到才換 pasta。bypass4netns 要自訂 seccomp,不考慮。
D4 離線安裝包連 Docker Engine 本體一起包(決策者 2026-11-10 定案)
離線包內含 Docker Engine 完整一組(docker-ce、cli、containerd、compose plugin、rootless extras、uidmap、slirp4netns 與相依),每個支援的 OS 版本一份,照 tpm2-tools 先例由 fetch_docker_debs.sh 抓。安裝包約多 400 到 500 MB。
install.sh 三段式處理:①主機沒有 Docker → 從離線包裝完整一組;②主機已有 Docker 且版本不低於我們釘的最低版 → 沿用,只補裝 rootless 附加套件;③主機 Docker 太舊 → 停下印訊息請客戶自行升級或移除,不代客戶動既有 Docker。
定案理由:客戶拿到包就能裝、不必先自己裝 Docker,這個理由比體積重要。釘版對落地版客戶是優點,底層不會被自動更新。代價是 SBOM 與弱點掃描範圍多了 Docker 本體,有 CVE 要跟著出包。
被排除:只包 rootless 附加三支、Docker 本體客戶自備——省 500 MB,但客戶要先查怎麼裝 Docker 官方套件庫,離線主機更麻煩。
順序:Agent 先出、主系統後續(決策者 2026-11-10 裁示)。Agent 安裝程式較小、沒有資料庫、沒有 443 問題,先拿它把 rootless 的坑踩完;主系統照抄做法。Agent 的拆卡在 design.md。
第一棒不寫程式。乾淨的 Ubuntu 24.04 一台,手動照官方文件裝 rootless Docker,把現有安裝包用 guidant 帳號裝一次,記錄每一步壞在哪、怎麼修好的,順便量網路吞吐、驗 D1 到 D3。產出是一份「實際壞了什麼」的清單。
有了清單再開後面的卡。這樣卡是按實測開,不是按這份文件的推測開。預估整案 15 到 20 張卡,跨 BE、Agent、test 三個 repo,Agent 端的卡等主系統那套做法定了再開。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
A[第一棒<br>乾淨機手動裝<br>列壞掉清單] --> B[主系統<br>安裝程式改 rootless]
B --> C[主系統<br>升級與回滾重驗]
B --> D[Agent<br>安裝程式改 rootless]
C --> E[190 重裝<br>e2e 回歸]
D --> E
E --> F[手冊<br>資安報告]