LXDにしろDockerにしろsystemd-nspawnにしろ、
Bubblewrapとunshare
LXDやDockerといったコンテナー管理ツールは、
しかしながらRed Hatが開発し、
Bubblewrapは、
実はデスクトップ版のUbuntuであれば、
同じようにユーザー権限でコンテナーを構築するツールとしてunshareコマンドが存在します。こちらはutil-linuxパッケージに所属しているため、unshare(2)
システムコールのラッパーという位置づけです。ただし基本的にやっていることは同じなので、
Bubblewrapの基本的な使い方
デスクトップ版のUbuntuならBubblewrapは最初から入っているはずです。サーバー版のようにまだインストールされていなかったら、
$ sudo apt install bubblewrap
ソフトウェア名は
$ ls -l $(which bwrap) -rwxr-xr-x 1 root root 68032 1月 3 2021 /usr/bin/bwrap $ getcap $(which bwrap)
このように、
ちなみにUbuntuのbwrapは以前からsetuidが外されていましたが、
$ sysctl kernel.unprivileged_userns_clone kernel.unprivileged_userns_clone = 1
実際にbashを別のユーザー名前空間の別の存在しないUIDで動かしてみましょう。
$ id 2000 id: `2000': no such user $ bwrap --ro-bind / / --dev /dev --unshare-user --uid 2000 --gid 2000 bash 私は名前がありません!@nuc:~$ id uid=2000 gid=2000 groups=2000,65534(nogroup)
「--ro-bind / /
」--bind
」
「--dev /dev
」/dev/
」
私は名前がありません!@nuc:~$ ls /dev/ console core fd full null ptmx pts random shm stderr stdin stdout tty urandom zero
もしホストのdevtmpfsをコンテナーから見えるようにして、--dev-bind /dev /dev
」--unshare-user
」
「--uid 2000 --gid 2000
」
最後に実行するコマンドとして
私は名前がありません!@nuc:~$ ps --pid $$ -f n UID PID PPID C STIME TTY STAT TIME CMD 2000 3287247 3287246 0 19:37 ? S 0:00 bash $ ps --pid 3287247 -f n UID PID PPID C STIME TTY STAT TIME CMD 1000 3287247 3287246 0 19:37 pts/12 S+ 0:00 bash
前者はUID=2000のプロセスになっていますが、
今回は読み込み専用でバインドしているため、--bind / /
」
bwrapではユーザー名前空間以外にも次のようなオプションで各名前空間を隔離可能です。
-
--unshare-user
-
ユーザー名前空間を作成し、
UID/ GIDをホストから隔離する。 -
--unshare-user-try
-
可能ならユーザー名前空間を作成するものの、
できなかったら無視する。 -
--unshare-ipc
-
IPC
(プロセス間通信) 名前空間を作成し、 共有メモリーやセマフォなどをホストから隔離する。 -
--unshare-pid
-
PID名前空間を作成し、
プロセス情報をホストから隔離する。 -
--unshare-net
-
ネットワーク名前空間を作成し、
ネットワークインターフェース等をホストから隔離する。 -
--unshare-uts
-
UTS
(Unixt Time Sharing) 名前空間を作成し、 ホスト名やドメイン名をホストから隔離する。 -
--unshare-cgroup
-
cgroup名前空間を作成し、
cgroup機能をホストから隔離する。 -
--unshare-cgroup-try
-
可能ならcgroup名前空間を作成するものの、
できなかったら無視する。 -
--unshare-all
-
ユーザー・
IPC・ PID・ ネットワーク・ UTS・ cgroup名前空間を作成する。
必要に応じて隔離度を変えると良いでしょう。
たとえば先ほど例だとユーザー名前空間だけ隔離していたため、
私は名前がありません!@nuc:~$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp5s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000 link/ether d4:5d:df:1d:f8:93 brd ff:ff:ff:ff:ff:ff 3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether d4:5d:df:1d:f8:92 brd ff:ff:ff:ff:ff:ff altname enp0s31f6 (以下略)
これが--unshare-net
」
私は名前がありません!@nuc:~$ exit exit shibata@nuc:~$ bwrap --ro-bind / / --dev /dev --unshare-user --unshare-net --uid 2000 --gid 2000 bash 私は名前がありません!@nuc:~$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
ちなみに--bind-ro
」--bind
」/run
」
ルートファイルシステムごとコンテナー化する
最後にbwrapコマンドを使って、--bind
」
まずはベースとなるルートファイルシステムをダウンロードしておきます。wgetにしろcurlにしろ方法はなんでもかまいません。LTS向けならUbuntu Baseを使うとサイズを最小にできます。ただし実際のUbuntu環境に合わせるためにはそれなりの追加パッケージが必要です。たとえばvimやnanoはおろか、
その点、
」
ここではUbuntu Baseをダウンロードしてbwrapで切り替えてみましょう。
$ wget http://cdimage.ubuntu.com/ubuntu-base/releases/20.04/release/ubuntu-base-20.04.3-base-amd64.tar.gz $ mkdir rootfs $ tar xvf ubuntu-base-20.04.3-base-amd64.tar.gz -C rootfs $ echo 'APT::Sandbox::User "root";' > rootfs/etc/apt/apt.conf.d/90run-as-root
最後のコマンドだけ説明が必要です。最近のAptはパッケージのインターネットからのダウンロードや署名の検証などを、_apt
」_apt
」
これでルートファイルシステムの作成は完了です。最後にそのルートファイルシステムにbwrapで
$ bwrap --bind rootfs/ / --ro-bind /etc/resolv.conf /etc/resolv.conf \ --dev /dev --chdir / --uid 0 --gid 0 --unshare-pid --unshare-user /bin/bash root@nuc:/# cat /etc/os-release NAME="Ubuntu" VERSION="20.04.3 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 20.04.3 LTS" VERSION_ID="20.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=focal UBUNTU_CODENAME=focal root@nuc:/# apt update Hit:1 http://security.ubuntu.com/ubuntu focal-security InRelease (以下略)
今回、--unshare-net
」--ro-bind /etc/
」
これで管理者権限を取得することなく、
ちなみに今回のコンテナーは隔離度が低く