目錄

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 DockerPodman
常駐程式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 失敗了,有哪兩種做法可以解決?

答案
  1. 改用 1024 以上的連接埠,例如 -p 8080:80,再用主機上的反向代理或防火牆規則把 80 轉到 8080。
  2. 調整系統設定允許一般使用者綁定 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日