前提
- K8s でなんらかのサービスを提供しているときに、そのサービス内でユーザ権限によるコンテナ実行をしたい
- ユーザは winbind により ActiveDirectory で認証
手段としては以下のいずれかが考えられる。
- DinD:Docker-inside-Docker による rootless コンテナ実行
- DooD: Docker-outside-of-Docker による rootless コンテナ実行
- podman によるユーザ権限でのコンテナ実行
DinD による rootless コンテナ実行の試行
結論から言うとできない。
DinD を行う場合は docker 公式イメージの docker:dind
タグなどを用いることが多いが、これは主に GitHub Actions におけるコンテナ内でイメージビルドを行う際に使うもの。
独自サービス内でコンテナ起動をしたいため、ベースイメージは好きなものを使いたい。
CentOSイメージを使用したDinD
を参考に、dockerインストールを試みる。
docker インストール
https://docs.docker.com/engine/install/rhel/
に従えば、インストールはすんなりできた。
[root@xxxxxxxx /]# dnf -y install dnf-plugins-core
[root@xxxxxxxx /]# dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
[root@xxxxxxxx /]# dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
が、systemd が入っていないため systemctl による docker サービス起動ができない。
[root@xxxxxxxx /]# systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
dockerd 直接起動を実施したが、エラーになる。
[root@xxxxxxxx /]# dockerd
INFO[******************] Starting up
INFO[******************] containerd not running, starting managed containerd
INFO[******************] started new containerd process address=/var/run/docker/containerd/containerd.sock module=libcontainerd pid=205
...
INFO[******************] unable to detect if iptables supports xlock: 'iptables --wait -L -n': `iptables v1.8.5 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)` error="exit status 4"
INFO[******************] stopping event stream following graceful shutdown error="<nil>" module=libcontainerd namespace=moby
INFO[******************] stopping healthcheck following graceful shutdown module=libcontainerd
INFO[******************] stopping event stream following graceful shutdown error="context canceled" module=libcontainerd namespace=plugins.moby
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to register "bridge" driver: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER: iptables v1.8.5 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)
(exit status 4)
ということで rootless 設定以前にdockerサービス実行できない。
DooD による rootless コンテナ実行の試行
DinD では docker サービス起動時に iptables によるブリッジインタフェースの生成に失敗している様子だったため、 DooD で回避できるかどうかを試行。
$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock ... --rm [service-container] bash
## docker インストール用リポジトリ追加
[root@xxxxxxxx /]# dnf -y install dnf-plugins-core
[root@xxxxxxxx /]# dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
## docker-cli のみインストール
[root@xxxxxxxx /]# dnf install -y docker-ce-cli
確かに docker ps
コマンドで実行ホストのコンテナ群が見えた (見えてしまった)。
[root@xxxxxxxx /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
*********** busybox "sleep 3600" 41 minutes ago Up 41 minutes k8s_pod-01a_pod-01a_default_************
*********** busybox "sleep 3600" 41 minutes ago Up 41 minutes k8s_pod-01_pod-01_default_**********
...
rootless docker インストール
この状態で rootless docker ができるかを試したが、失敗。
ローカルユーザ追加
[root@xxxxxxxx /]# adduser testuser
この状態で testuser 指定でコンテナに入り rootless を設定。
$ docker exec -it -u testuser xxxxxxxx bash
[testuser@xxxxxxxx /]$ curl -fsSL https://get.docker.com/rootless | sh
# Installing stable version 27.3.1
# Executing docker rootless install script, commit: 6b775c4
# Existing rootless Docker detected at /home/testuser/bin/dockerd
# To reinstall or upgrade rootless Docker, run the following commands and then rerun the installation script:
systemctl --user stop docker
rm -f /home/testuser/bin/dockerd
# Alternatively, install the docker-ce-rootless-extras RPM/deb package for ease of package management (requires root).
# See https://docs.docker.com/go/rootless/ for details.
https://docs.docker.com/engine/security/rootless/#errors-when-starting-the-docker-daemon
に "could not get XDG_RUNTIME_DIR" という項目があり、環境変数 XDG_RUNTIME_DIR がセットされていなかったため強引にセットして起動してみたが、権限エラーになる。
[testuser@xxxxxxxx /]$ export XDG_RUNTIME_DIR=$HOME/.docker/xrd
[testuser@xxxxxxxx /]$ rm -rf $XDG_RUNTIME_DIR
[testuser@xxxxxxxx /]$ mkdir -p $XDG_RUNTIME_DIR
[testuser@xxxxxxxx /]$ dockerd-rootless.sh
...
+ exec rootlesskit --state-dir=/home/testuser/.docker/xrd/dockerd-rootless --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /home/testuser/bin/dockerd-rootless.sh
WARN[0000] [rootlesskit:parent] The host root filesystem is mounted as "master:1575". Setting child propagation to "rslave" is not supported.
[rootlesskit:parent] error: failed to start the child: fork/exec /proc/self/exe: operation not permitted
rootless docker は DooD でもできなさそうな感じ。
podman によるユーザ権限でのコンテナ実行
コンテナに入り podmanを dnf インストールする。
$ docker run -it --rm [service-container] bash
[root@xxxxxxx /]# dnf install -y podman
だが podmanで適当なコンテナを実行しようとするとエラーになる。
[root@xxxxxxx /]# podman run hello-world
WARN[0000] "/" is not a shared mount, this could cause issues or missing mounts with rootless containers
cannot clone: Operation not permitted
Error: cannot re-exec process
によれば、 docker 実行時に --security-opt seccomp=unconfined
を付けると良いらしい。
$ docker run -it --security-opt seccomp=unconfined --rm [service-container] bash
[root@xxxxxxxxx /]# dnf install -y podman
[root@xxxxxxxxx /]# podman run hello-world
WARN[0000] "/" is not a shared mount, this could cause issues or missing mounts with rootless containers
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull quay.io/podman/hello:latest...
Getting image source signatures
Copying blob 81df7ff16254 done |
Copying config 5dd467fce5 done |
Writing manifest to image destination
ERRO[0003] Unmounting /var/lib/containers/storage/overlay/885a8ae8337b760ac777ab466e2a72974d258477107a5594ebb927a716e89b54/merged: invalid argument
WARN[0003] Failed to load cached network config: network podman not found in CNI cache, falling back to loading network podman from disk
ERRO[0003] Preparing container d6b42ea4963f7264ab229bab8b4c201639cfca2330c840308b7c90ce9dfc9169: plugin type="bridge" failed (add): cni plugin bridge failed: failed to create bridge "cni-podman0": could not add "cni-podman0": operation not permitted
Error: mounting storage for container d6b42ea4963f7264ab229bab8b4c201639cfca2330c840308b7c90ce9dfc9169: creating overlay mount to /var/lib/containers/storage/overlay/885a8ae8337b760ac777ab466e2a72974d258477107a5594ebb927a716e89b54/merged, mount_data="lowerdir=/var/lib/containers/storage/overlay/l/SBCUSNZOXDGNIQ6SDMWKBGA7VK,upperdir=/var/lib/containers/storage/overlay/885a8ae8337b760ac777ab466e2a72974d258477107a5594ebb927a716e89b54/diff,workdir=/var/lib/containers/storage/overlay/885a8ae8337b760ac777ab466e2a72974d258477107a5594ebb927a716e89b54/work,nodev": using mount program /usr/bin/fuse-overlayfs: unknown argument ignored: lazytime
fuse: device not found, try 'modprobe fuse' first
fuse-overlayfs: cannot mount: No such file or directory
: exit status 1
podman コマンドは実行できたが、"/" が shared でないという警告や、cni-podman0 インタフェースを作れないなどのエラーでコンテナが動かない。
priviledged モードによるコンテナ実行
https://developers.redhat.com/e-books/podman-action
https://www.redhat.com/en/blog/podman-inside-container
によれば、 security-opt ではなくて --privileged
モードで動かすと良いとある。
$ docker run -it --privileged --rm [service-container] bash
[root@xxxxxxxx /]# dnf install -y podman
[root@xxxxxxxx /]# podman run hello-world
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull quay.io/podman/hello:latest...
Getting image source signatures
Copying blob 81df7ff16254 done |
Copying config 5dd467fce5 done |
Writing manifest to image destination
!... Hello Podman World ...!
.--"--.
/ - - \
/ (O) (O) \
~~~| -=(,Y,)=- |
.---. /` \ |~~
~/ o o \~~~~.----. ~~
| =(X)= |~ / (O (O) \
~~~~~~~ ~| =(Y_)=- |
~~~~ ~~~| U |~~
Project: https://github.com/containers/podman
Website: https://podman.io
Desktop: https://podman-desktop.io
Documents: https://docs.podman.io
YouTube: https://youtube.com/@Podman
X/Twitter: @Podman_io
Mastodon: @Podman_io@fosstodon.org
"/" のマウント警告も出ず、実行できた。 アザラシかわゆす。 (´ω`っ )3
ユーザ権限での実行確認
ユーザ権限でも実行できるか確認。
[root@xxxxxxx /]# adduser testuser
としてユーザ追加し、ユーザ名指定でコンテナに入り、コンテナ実行。
[testuser@xxxxxxx /]$ podman run hello-world
!... Hello Podman World ...!
.--"--.
/ - - \
/ (O) (O) \
~~~| -=(,Y,)=- |
.---. /` \ |~~
~/ o o \~~~~.----. ~~
| =(X)= |~ / (O (O) \
~~~~~~~ ~| =(Y_)=- |
~~~~ ~~~| U |~~
Project: https://github.com/containers/podman
Website: https://podman.io
Desktop: https://podman-desktop.io
Documents: https://docs.podman.io
YouTube: https://youtube.com/@Podman
X/Twitter: @Podman_io
Mastodon: @Podman_io@fosstodon.org
警告も出ず実行できた。
イメージビルド
ADユーザのホームディレクトリ領域が NFSマウントされている場合のために、コンテナ保存先をホームディレクトリ以外の場所に設定する。
storage.conf
:
[storage]
rootless_storage_path = "/var/tmp/$USER"
ADユーザのuid, gid に対して subuid, subgid 設定をコンテナにも反映。
subuid,subgidについては rootless 用プロセス設定 を参照。
ADユーザで実行できるようwinbindを入れておく。
...
RUN dnf update -y && \
dnf install -y \
...
authselect samba-winbind-clients \
podman
## AD設定
ENV SECRET_AD_USER=""
ENV SECRET_AD_PASSWD=""
COPY smb.conf /etc/samba
RUN authselect select winbind --force
## podman設定
COPY ./storage.conf /etc/containers/storage.conf
COPY ./subuid /etc/subuid
COPY ./subgid /etc/subgid
## サービス実行用スクリプト
COPY ./run.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/run.sh
CMD ["/usr/local/bin/run.sh"]
最後のサービス実行用スクリプトには AD認証できるよう winbindd 実行する。
run.sh
:
#!/bin/sh
### AD参加
net ads join -U "${SECRET_AD_USER}%${SECRET_AD_PASSWD}"
winbindd -D
### サービスプログラム実行
xxxx
K8s サービスを privileged モードで動かす
privileged モードで pod を動かすには spec.containers.securityContext.privileged=true にすればよい。
マニフェストに以下を追記して適用。
---
apiVersion: v1
kind: Secret
metadata:
name: my-service-sercret
type: Opaque
data:
SECRET_AD_USER: *************
SECRET_AD_PASSWD: ***********
---
apiVersion: apps/v1
kind: StatefulSet
spec:
template:
spec:
containers:
- name: my-service
securityContext:
privileged: true
env:
- name: SECRET_AD_USER
valueFrom:
secretKeyRef:
name: my-service-sercret
key: SECRET_AD_USER
- name: SECRET_AD_PASSWD
valueFrom:
secretKeyRef:
name: my-service-sercret
key: SECRET_AD_PASSWD
...
AD参加時のユーザ・パスワードを Secret リソースとして登録し、環境変数 SECRET_AD_USER, SECRET_AD_PASSWD でコンテナに渡すようにしている。
上記イメージに更新してpod起動しなおすことで、ADユーザでコンテナ実行ができる。
### 更新したイメージで起動しなおすよう既存 pod を停止 → StatefulSet により自動再起動
$ kubectl delete pods [service-continer]