目錄
Rootless Docker 與 Podman:不用 root 執行容器
安全最佳實務 實測過:能使用 Docker 的人,就能把主機的根目錄掛進容器、讀寫任何檔案,等同擁有主機的 root 權限。原因是 Docker Engine(dockerd)本身以 root 執行,容器裡的 root 就是主機的 root(只是被各種隔離機制限制住)。
這一節介紹兩種不需要 root 的做法:Docker 的 Rootless 模式,以及另一個容器工具 Podman。
Rootless Docker:Docker Engine 以一般使用者執行
Rootless 模式讓 dockerd 和所有容器都以一般使用者執行。它使用 Linux 的 user namespace:容器裡看起來是 root(uid 0),在主機上其實對應到你的帳號或一段不具特權的 uid。
本節用官方的 docker:29.9-dind-rootless 映像檔實測,不會影響主機設定:
$ docker run -d --name rl --privileged docker:29.9-dind-rootless
93e609c8808360d7e63bdc214ab2ff0e76af520fb0fb9dec5b5852d57a4881fe
$ docker logs rl 2>&1 | tail -n 5
time="2026-10-13T05:22:59.775914576Z" level=info msg="Initializing buildkit"
time="2026-10-13T05:22:59.918099929Z" level=info msg="Completed buildkit initialization"
time="2026-10-13T05:22:59.929895038Z" level=info msg="Daemon has completed initialization"
time="2026-10-13T05:22:59.930093633Z" level=info msg="API listen on /run/user/1000/docker.sock"
time="2026-10-13T05:22:59.930231753Z" level=info msg="API listen on [::]:2376"
$ docker exec rl id
uid=1000(rootless) gid=1000(rootless) groups=1000(rootless)
日誌顯示 Docker Engine 監聽的是 /run/user/1000/docker.sock(使用者 1000 專屬的路徑),而不是一般的 /var/run/docker.sock;執行 dockerd 的使用者是 rootless(uid 1000),不是 root。
容器裡的 root 是誰
讓 docker 指令連到 rootless 的 socket(DOCKER_HOST),執行一個容器:
$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker info --format '{{json .SecurityOptions}}'
["name=seccomp,profile=builtin","name=rootless"]
$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker run -d --name sleeper alpine:3.23 sleep 300
18986d442e9b9282178d96e7187534b9ac19a83dd42e3b4671a5243b55bcf1ff
$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker exec sleeper id
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
$ docker exec rl ps -o user,pid,args | grep 'sleep 300' | grep -v grep
rootless 382 sleep 300
docker info的安全選項多了name=rootless。- 容器裡的
id顯示uid=0(root),對容器裡的程式來說它就是 root。 - 從主機(外層)看同一個
sleep 300程序,擁有者是rootless這個一般使用者。
實測:掛載主機根目錄也讀不到 /etc/shadow
/etc/shadow 存放密碼雜湊,只有 root 能讀取。分別用一般 Docker 和 Rootless Docker 掛載主機的 / 並讀取它:
$ docker run --rm -v /:/host alpine:3.23 sh -c "id -u; head -n 1 /host/etc/shadow"
0
root:*::0:::::
$ docker exec -e DOCKER_HOST=unix:///run/user/1000/docker.sock rl docker run --rm -v /:/host alpine:3.23 sh -c 'id -u; head -n 1 /host/etc/shadow'
0
head: /host/etc/shadow: Permission denied
- 一般 Docker:容器內的 root 直接讀到了主機的
/etc/shadow。 - Rootless Docker:容器內一樣是 root(
id -u為 0),但讀取時得到Permission denied。因為它在主機上只是一般使用者,權限不會超過啟動 Docker 的那個帳號。
所以即使容器被入侵、或有人誤用了 -v /:/host,影響範圍也只限於那個使用者帳號。
在 Linux 上安裝 Rootless Docker
以下步驟依 Docker 官方文件整理。本教學沒有在主機上實際安裝(安裝會修改主機的系統設定),實際操作時請以官方文件為準。
# 1. 安裝需要的套件(Ubuntu / Debian)
sudo apt-get install -y uidmap dbus-user-session
# 2. 已經安裝的一般 Docker 建議先停用
sudo systemctl disable --now docker.service docker.socket
# 3. 以一般使用者執行安裝腳本(隨 Docker Engine 套件安裝)
dockerd-rootless-setuptool.sh install
# 4. 讓 docker 指令連到 rootless 的 socket(加進 ~/.bashrc)
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
# 5. 讓它在開機時啟動(使用者登出後也繼續執行)
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
Rootless 的限制
| 限制 | 說明與解法 |
|---|---|
| 1024 以下的連接埠 | 一般使用者預設不能綁定 80、443,-p 80:80 會失敗。改用 8080 等連接埠再由反向代理轉發,或調整系統設定 net.ipv4.ip_unprivileged_port_start |
| 資源限制 | --memory、--cpus 需要主機使用 cgroup v2,並由 systemd 委派權限給使用者 |
| 網路效能 | 透過使用者空間的網路元件轉發,吞吐量比一般模式低 |
| 資料位置 | 映像檔和 Volume 存在使用者的家目錄(~/.local/share/docker),與一般模式的資料不共用 |
| 部分功能 | 例如 AppArmor 設定、部分儲存與網路驅動程式不支援 |
本教學的測試環境調整過
ip_unprivileged_port_start,所以-p 80:80沒有出錯,上表第一點無法在這裡重現。
Podman:不需要常駐程式的容器工具
Podman 是 Red Hat 主導的開源容器工具,在 Fedora、RHEL 等發行版是預設的容器工具。它和 Docker 最大的不同:
- 沒有常駐程式(daemonless):Docker 的指令要透過 dockerd 執行;Podman 的指令直接建立容器,容器是你這個程序的子程序。
- 預設 rootless:一般使用者執行
podman,就是 rootless 模式。 - 指令幾乎與 Docker 相同:
run、build、ps、images、exec、logs等參數都一樣。
實測:用 Docker 的方式使用 Podman
在 quay.io/podman/stable 映像檔中執行(測試主機的 cgroup v1 會讓 Podman 顯示棄用警告,所以加上 PODMAN_IGNORE_CGROUPSV1_WARNING=1 隱藏):
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman --version
podman version 5.8.7
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run --rm docker.io/library/alpine:3.23 echo hello from podman
hello from podman
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run --rm alpine:3.23 echo hi
hi
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman build -q -t express-app /tmp/express-app
npm notice
npm notice New major version of npm available! 11.19.0 -> 12.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
npm notice To update run: npm install -g npm@12.2.0
npm notice
npm warn using --force Recommended protections disabled.
a5a0b2b95ea756ad73b92ead62ece1770fdb26033c69749933fe03e3988dcb8d
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman run -d --name app -p 3000:3000 express-app
74aeae727d68aacae32aee45d0705881fbea9b449ac1a5ee07c365b1490f42b3
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman podman exec app wget -qO- localhost:3000
{"message":"Hello from Express","node":"v24.21.0"}
$ docker exec -e PODMAN_IGNORE_CGROUPSV1_WARNING=1 podman su podman -c 'podman run --rm docker.io/library/alpine:3.23 id -u; podman info --format {{.Host.Security.Rootless}}'
0
true
podman run的用法與 Docker 相同,--rm、-d、-p等參數都能直接使用。podman build直接使用 Node.js 應用 的 Dockerfile,不需要任何修改就建置成功,容器也正常回應。- 映像檔名稱建議寫完整,例如
docker.io/library/alpine:3.23。Podman 的短名稱(alpine:3.23)會依系統設定決定從哪個 Registry 下載,不同發行版的設定可能不同。 - 最後切換成一般使用者
podman執行:podman info顯示rootless為true,容器內依然是 uid 0,但和 Rootless Docker 一樣只對應到主機的一般使用者。
安裝
# Ubuntu / Debian
sudo apt-get install -y podman
# Fedora / RHEL
sudo dnf install -y podman
Windows 和 macOS 可以安裝 Podman Desktop,它和 Docker Desktop 一樣在虛擬機中執行 Linux。
Docker、Rootless Docker、Podman 怎麼選
| Docker(一般) | Rootless Docker | Podman | |
|---|---|---|---|
| 常駐程式 | dockerd(root) | dockerd(一般使用者) | 沒有 |
| 容器內 root 的實際權限 | 主機 root(受隔離限制) | 一般使用者 | 一般使用者(rootless 時) |
| 與本教學指令的相容性 | 完全相同 | 幾乎相同 | 大部分相同 |
| Docker Compose | 內建 | 內建 | podman compose 呼叫外部實作 |
| 生態系工具(Portainer、Traefik 等) | 完整支援 | 大多支援 | 需要額外設定 socket |
| 適合 | 一般開發、Docker Desktop、大部分伺服器 | 多人共用的伺服器、安全要求高 | RHEL / Fedora 環境、不想要常駐程式 |
對大多數初學者與一般專案,照本教學使用 Docker 即可;如果伺服器有多人登入、或資安要求較高,再考慮 Rootless Docker 或 Podman。
重點回顧
- 一般 Docker 的 dockerd 以 root 執行,能使用 Docker 就等同擁有主機 root。
- Rootless 模式用 user namespace,讓容器內的 root 對應到主機的一般使用者,實測無法讀取主機的
/etc/shadow。 - Rootless 的主要限制是 1024 以下的連接埠、資源限制需要 cgroup v2,以及網路效能。
- Podman 沒有常駐程式、預設 rootless,指令與 Docker 幾乎相同,可以直接建置 Dockerfile。
動手練習
練習 1:在一般 Docker 中執行 docker run --rm alpine:3.23 id,再執行 docker run --rm -u 1000:1000 alpine:3.23 id。這和 Rootless 模式有什麼不同?
答案
-u 1000:1000 只是讓容器內的程式以 uid 1000 執行,dockerd 仍然是 root,能使用 Docker 的人仍然可以不加 -u 再啟動一個 root 容器。Rootless 模式則是整個 Docker Engine 都以一般使用者執行,沒有任何容器能拿到主機的 root 權限。兩者可以同時使用。
練習 2:在 Rootless Docker 中執行 docker run -d -p 80:80 nginx:1.30-alpine 失敗了,有哪兩種做法可以解決?
答案
- 改用 1024 以上的連接埠,例如
-p 8080:80,再用主機上的反向代理或防火牆規則把 80 轉到 8080。 - 調整系統設定允許一般使用者綁定 80 埠:
sudo sysctl net.ipv4.ip_unprivileged_port_start=80(寫進/etc/sysctl.conf才會在重開機後保留)。
常見問題
Rootless 模式會比較慢嗎?
執行容器內程式的速度差不多。網路(透過使用者空間的網路轉發)和部分檔案系統操作會比一般模式慢一些,大多數網站與開發用途感覺不出差別。
Docker Desktop 需要 Rootless 嗎?
不需要。Docker Desktop 在 Windows、macOS 上把 Docker Engine 放在虛擬機裡執行,本身就和你的電腦隔開了。Rootless 主要用於 Linux 伺服器和 Linux 桌面。
可以把 docker 指令直接換成 podman 嗎?
大部分可以,podman run、podman build、podman ps 的參數幾乎相同,很多人直接設定 alias docker=podman。差異在於沒有常駐程式、預設 rootless,以及 Compose、Docker socket 相關的工具需要另外設定。
Podman 可以用 compose.yaml 嗎?
可以。podman compose 會呼叫系統上安裝的 Compose 實作(docker-compose 或 podman-compose)來處理 compose.yaml,大部分設定都能使用。
更新於 2026年10月13日