名前

unshare - プログラムを新たな名前空間内で実行する

書式

unshare [options] [program [arguments]]

説明

unshare コマンドは、 新たな名前空間を生成する (これは、 以下に示すコマンドラインオプションから指定される)。 そして指定された program を実行する。 program が指定されなかった場合は、 "${SHELL}" が実行される (デフォルトは /bin/sh)。

名前空間というものは、 デフォルトでは、 メンバープロセスが存在する限り、 永続化される。 新たな名前空間を生成する際に、 たとえメンバープロセスが存在しない場合であっても、 /proc/pid/ns/type ファイルを、 ファイルシステムパスにバインドマウントしておけば、 永続化することができる。 このようにして生成された名前空間は、 program が終了した後であっても、 nsenter(1) により続けて利用することができる (ただし、 init プロセスを常時動作させておかなければならない PID 名前空間は別である)。 永続化された名前空間が必要なくなったときは、 umount(8) を用いてバインドマウントを削除することで、 永続化の解除を行うことができる。 詳しくは セクションを参照のこと。

util-linux バージョン 2.36 から、 unshare は、 永続的な PID 名前空間および時間名前空間に対して、 /proc/[pid]/ns/pid_for_children/proc/[pid]/ns/time_for_children を利用するようになった。 この変更は、 Linux カーネル 4.17 以降を必要とする。

unshare から生成される名前空間のタイプは、 以下のものである。

マウント名前空間

ファイルシステムのマウント、アンマウントが、 システム上のこれ以外の部分には影響しなくなる。 ただしファイルシステムが、 明示的に共有 (shared) としてマークされている場合は除く (mount --make-shared 利用時。 shared フラグについては /proc/self/mountinfo または findmnt -o+PROPAGATION を参照)。 さらに詳細は、 mount_namespaces(7) を参照のこと。

util-linux バージョン 2.27 以降の unshare は、 新規のマウント名前空間のプロパゲーションには、 自動的に private を設定するようになった。 こうすることで、 その新たな名前空間を確実に分離する (unshare する) ものである。 この機能は --propagation unchanged の指定によって、 無効化することができる。 なお private は、 カーネルにおけるデフォルトである。

UTS 名前空間

ホスト名やドメイン名の設定が、システム上のこれ以外の部分には影響しなくなる。 詳しくは uts_namespaces(7) を参照のこと。

IPC 名前空間

このプロセスには、 POSIX メッセージキュー、 System V メッセージキュー、 セマフォーセット、 共有メモリセグメントに対して、 独立した名前空間が与えられる。 詳しくは ipc_namespaces(7) を参照のこと。

ネットワーク名前空間

このプロセスには、 独立した IPv4 および IPv6 スタック、 IP ルーティングテーブル、 ファイアーウォールルール、 /proc/net/sys/class/net のディレクトリツリー、 ソケットなどが与えられる。 詳しくは network_namespaces(7) を参照のこと。

PID 名前空間

子プロセスが持つ PID プロセスマッピングのセットを、 親プロセスとは別のものとする。 詳細は、 pid_namespaces(7) を参照のこと。

cgroup 名前空間

プロセスは、 /proc/self/cgroup への仮想ビューを持つようになる。 そして新たな cgroup マウントは、 名前空間の cgroup ルートとしてルート化される。 詳しくは cgroup_namespaces(7) を参照のこと。

ユーザー名前空間

プロセスが、 個別の UID、 GID のセットやケーパビリティー (capability) を持つようになる。 詳細は user_namespaces(7) を参照のこと。

時間名前空間

プロセスは、 CLOCK_MONOTONICCLOCK_BOOTTIME に対する個別の仮想ビューを持つようになるため、 /proc/self/timens_offsets を用いて、 それらを変更できるようになる。 詳しくは time_namespaces(7) を参照のこと。

オプション

-i, --ipc[=file]

新たな IPC 名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。

-m, --mount[=file]

新たなマウント名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。 なお file が置かれているマウントは、 プロパゲーションタイプが shared 以外のものでなければならない (そうでない場合、 エラーとなる)。 現在の設定が不明である場合は、 findmnt -o+PROPAGATION を利用すること。 また以降の例も参照のこと。

-n, --net[=file]

新たなネットワーク名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。

-p, --pid[=file]

新たな PID 名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。 (ただし PID 名前空間の永続的生成は、 --fork オプションも同時に指定していない場合には失敗する。)

--fork オプションや --mount-proc オプションも参照のこと。

-u, --uts[=file]

新たな UTS 名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。

-U, --user[=file]

新たにユーザー名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。

-C, --cgroup[=file]

新たに cgroup 名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。

-T, --time[=file]

新たに時間名前空間を生成する。 file が指定された場合、 file にバインドマウントを生成することによって、 名前空間が永続的に生成される。 --monotonic オプションおよび --boottime オプションが利用可能であり、 これによって、 時間名前空間内での対応するオフセットを指定できる。

-f, --fork

指定された program を、 直接実行するのではなく、 分離された子プロセスとしてフォークすることを指定する。 これは、 新たな PID 名前空間を生成する際に、 利用することができる。 なお、 unshare が子プロセスを待っている間は、 SIGINTSIGTERM は無視し、 その子プロセスに向けてのシグナルは、 何も転送はしない。 これは、 子プロセスにシグナルを送るために、 必要になることである。

--keep-caps

--user オプションが指定された場合に、 ユーザー名前空間内において許可されていたケーパビリティーは、 子プロセス内においても維持されるようにする。

--kill-child[=signame]

unshare が終了する際には、 フォークされている子プロセスに対して、 signame を送信するようにする。 --pid と組み合わせて指定すれば、 unshare に属するプロセスツリー全体を、 簡単かつ確実に kill することができる。 signame が指定されなかった場合、 デフォルトは SIGKILL である。 このオプションは、 暗に --fork の指定を含む。

--mount-proc[=mountpoint]

プログラムの実行前に、 proc ファイルシステムを mountpoint にマウントする (デフォルトは /proc)。 これは、 新たな PID 名前空間を生成する際に、 利用することができる。 この指定にあたっては、 新たなマウント名前空間が暗に生成されるのであるが、 それをしていないと、 /proc マウントが原因で、 システム上の既存プログラムが動作しなくなるからである。 新たに生成された proc ファイルシステムは、 明示的に private としてマウントされる (MS_PRIVATE|MS_REC が用いられる)。

--map-user=uid|name

現在の実効ユーザー ID が uid にマッピングされた後にのみ、 プログラムを実行する。 このオプションは複数回の指定が可能であるが、 最後の 1 つが優先される。 このオプションは、 暗に --user の指定を含む。

--map-users=outeruid,inneruid,count|auto

outeruid で始まるユーザー ID の count 分のサイズのブロックが、 inneruid で始まるユーザー ID にマッピングされた後にのみ、 プログラムを実行する。 このマッピングは newuidmap(1) により生成される。 そのユーザー ID の範囲が --map-user によって指定されるマッピングと重なる場合、 「穴」となる部分はマッピングから削除される。 これによってユーザー ID の最大値がマッピングされないという結果も起こり得る。 特別な値 auto を指定すると、 有効なユーザーが /etc/subuid によって所有するユーザー ID の最初のブロックを、 ユーザー ID が 0 から始まるブロックにマッピングする。 このオプションが複数指定された場合は、 最後に指定されたものが有効になる。 このオプションは暗に --user の指定が含まれる。

--map-group=gid|name

現在の実効グループ ID が gid にマッピングされた後にのみ、 プログラムを実行する。 このオプションは複数回の指定が可能であるが、 最後の 1 つが優先される。 このオプションは、 暗に --setgroups=deny--user の指定を含む。

--map-groups=outergid,innergid,count|auto

Run the program only after the block of group IDs of size count beginning at outergid has been mapped to the block of group IDs beginning at innergid. This mapping is created with newgidmap(1). If the range of group IDs overlaps with the mapping specified by --map-group, then a "hole" will be removed from the mapping. This may result in the highest group ID of the mapping not being mapped. The special value auto will map the first block of user IDs owned by the effective user from /etc/subgid to a block starting at group ID 0. If this option is specified multiple times, the last occurrence takes precedence. This option implies --user.

--map-auto

Map the first block of user IDs owned by the effective user from /etc/subuid to a block starting at user ID 0. In the same manner, also map the first block of group IDs owned by the effective group from /etc/subgid to a block starting at group ID 0. This option is intended to handle the common case where the first block of subordinate user and group IDs can map the whole user and group ID space. This option is equivalent to specifying --map-users=auto and --map-groups=auto.

-r, --map-root-user

現在の実効ユーザー ID と実行グループ ID が、 新たに生成されたユーザー名前空間内のスーパーユーザーの UID と GID にマッピングされた後にのみ、 プログラムを実行する。 こうすることによって、 そのとき非特権のユーザーで実行した場合であっても、 新しく生成された名前空間内においては、 各種の管理操作 (たとえば、ネットワーク名前空間内でのインターフェース設定や、 マウント名前空間内でのファイルシステムマウントなど) に必要となるケーパビリティーを、 簡単に取得できるようになる。 これは単なる便利機能であって、 より複雑な利用方法、 たとえば UID や GID の複合的な範囲へのマッピングのような機能をサポートするわけではない。 このオプションは、 暗に --setgroups=deny--user の指定を含む。 これは、 --map-user=0 --map-group=0 の指定と同じである。

-c, --map-current-user

現在の実効ユーザー ID と実行グループ ID が、 新たに生成されたユーザー名前空間内での、 同じ UID と GID にマッピングされた後にのみ、 プログラムを実行する。 このオプションは、 暗に --setgroups=deny--user の指定を含む。 これは、 --map-user=$(id -ru) --map-group=$(id -rg) の指定と同じである。

--propagation private|shared|slave|unchanged

新たなマウント名前空間内において、 マウントプロパゲーションフラグを再帰的に設定する。 デフォルトは、 プロパゲーションを private に設定する。 この機能は unchanged 引数の指定によって、 無効化することができる。マウント名前空間を必要としていない (--mount がない) 場合、 このオプションは、何も出力されず単に無視される。

--setgroups allow|deny

名前空間内において、 システムコール setgroups(2) の許可または拒否を設定する。

setgroups(2) の呼び出しを可能にするために、 呼び出し元のプロセスは、 最低でも CAP_SETGID がなければならない。 なお Linux 3.19 以降は、 さらに制約が設けられている。 カーネルが setgroups(2) 呼び出しを許可するのは、 GID マッピング (/proc/pid*/gid_map*) のセットを終えた後である。 GID マップが root によって書き込み可能になるのは、 setgroups(2) が有効になったときである (つまり、デフォルトで allow になったとき)。 また、 GID マップが非特権プロセスによって書き込み可能になるのは、 setgroups(2) が (deny を使って) 永久的に無効化されたときである。

-R, --root=dir

ルートディレクトリを dir に設定して、 コマンドを実行する。

-w, --wd=dir

ワーキングディレクトリを dir に変更する。

-S, --setuid uid

名前空間に入ってから利用するユーザー ID を設定する。

-G, --setgid gid

名前空間に入ってから利用するグループ ID を設定する。 補助グループは削除される。

--monotonic offset

時間名前空間に入った際に利用される CLOCK_MONOTONIC のオフセットを設定する。 このオプションは、 --time を使って分離した時間名前空間が必要である。

--boottime offset

時間名前空間に入った際に利用される CLOCK_BOOTTIME のオフセットを設定する。 このオプションは、 --time を使って分離した時間名前空間が必要である。

-h, --help

ヘルプを表示して終了する。

-V, --version

バージョンを表示して終了する。

注意

ユーザー名前空間内において root によりマウントされている proc と sysfs の両ファイルシステムには、 利用制限がなければならない。 つまり、 権限のより高いユーザーが、 アクセス不能とした機密情報を、 それよりも権限の低いユーザーが、 その権限を越えてアクセスできるようであってはならない。 わかりやすく言えば、 proc や sysfs に対するルールは、 できるかぎりバインドマウントに近いもの、 ということである。

以下のコマンドは、 --fork を使って PID 名前空間を生成する。 指定されるコマンドは、 (その名前空間内の最初のプロセスである) PID に 1 を持った子プロセス内として実行される。 --mount-proc オプションがあるので、 上の処理と同時に新たなマウント名前空間が生成されて、 新たな proc(5) ファイルシステムは、 新たな PID 名前空間に応じた情報に従ってマウントされる。 readlink(1) コマンドが終了すると、 新たな名前空間は自動的に解放される。

# unshare --fork --pid --mount-proc readlink /proc/self
1

非特権ユーザーとして、 新たなユーザー名前空間を生成する。 ユーザーの資格情報は、 その名前空間内部では root ID にマッピングされる。

$ id -u; id -g
1000
1000
$ unshare --user --map-root-user \
        sh -c ''whoami; cat /proc/self/uid_map /proc/self/gid_map''
root
         0       1000          1
         0       1000          1

非特権ユーザーとして、 最初の 65536 個分の ID をすべてマッピングしたユーザー空間を生成する。 そしてユーザーの資格情報は、 名前空間内の root ID にマップする。 このマッピングは subuid(5) と subgid(5) によって割り当てられたサブ ID によって定められる。 以下ではユーザー ID を 1、 グループ ID を 1 とするファイルを生成して、 そのマッピング状況を示している。 例示にあたっては簡単にユーザー ID のマッピングのみを示す。

$ id -u
1000
$ cat /etc/subuid
1000:100000:65536
$ unshare --user --map-auto --map-root-user
# id -u
0
# cat /proc/self/uid_map
         0       1000          1
         1     100000      65535
# touch file; chown 1:1 file
# ls -ln --time-style=+ file
-rw-r--r-- 1 1 1 0  file
# exit
$ ls -ln --time-style=+ file
-rw-r--r-- 1 100000 100000 0  file

以下のコマンドの初めにおいて、 新たな永続的 UTS 名前空間を生成して、 その名前空間内から見えるホスト名を変更する。 次に、 nsenter(1) によって名前空間内に入って、 修正したホスト名を表示する。 この一連の手順が示しているのは、 unshare コマンドが終了した後に、 名前空間内にメンバープロセスが存在していなくても、 UTS 名前空間は存在し続けている、ということである。 この名前空間は、 この後にバインドマウントを削除すれば破棄される。

# touch /root/uts-ns
# unshare --uts=/root/uts-ns hostname FOO
# nsenter --uts=/root/uts-ns hostname
FOO
# umount /root/uts-ns

以下のコマンドは、 バインドマウント /root/namespaces/mnt によって参照される、 永続的なマウント名前空間を作成する。 バインドマウントの生成が成功したことを確認するために、 親ディレクトリ (/root/namespaces) は、 プロパゲーションタイプを shared ではないものとして、 バインドマウントを行っている。

# mount --bind /root/namespaces /root/namespaces
# mount --make-private /root/namespaces
# touch /root/namespaces/mnt
# unshare --mount=/root/namespaces/mnt

以下のコマンドは、 PID 名前空間の生成時における、 --kill-child オプションの利用方法を示すものである。 unshare が kill されたときに、 PID 名前空間内のプロセスが、 すべて kill されることを確認する。

# set +m                # ジョブのステータスメッセージを非表示


# unshare --pid --fork --mount-proc --kill-child -- \


       bash --norc -c ''(sleep 555 &) && (ps a &) && sleep 999'' &
[1] 53456
#     PID TTY      STAT   TIME COMMAND
      1 pts/3    S+     0:00 sleep 999
      3 pts/3    S+     0:00 sleep 555
      5 pts/3    R+     0:00 ps a

# ps h -o 'comm' $!     # Show that background job is unshare(1)
unshare
# kill $!               # Kill unshare(1)
# pidof sleep

上の pidof(1) コマンドでは、 何も出力されない。 なぜなら sleep プロセスが、 すでに kill されているからである。 もう少し細かく言うと、 この名前空間内において PID 1 を持っている sleep プロセス (つまりは、 この名前空間の init プロセス) が kill されると、 ここから、 この名前空間内のその他すべてのプロセスが kill される、 ということである。 これと同じような一連のコマンドを、 今度は --kill-child オプションを用いないで実行してみると、 unshare が終了したときに、 PID 名前空間内の各プロセスは、 kill されていないことがわかる。

# unshare --pid --fork --mount-proc -- \


       bash --norc -c ''(sleep 555 &) && (ps a &) && sleep 999'' &
[1] 53479
#     PID TTY      STAT   TIME COMMAND
      1 pts/3    S+     0:00 sleep 999
      3 pts/3    S+     0:00 sleep 555
      5 pts/3    R+     0:00 ps a

# kill $!
# pidof sleep
53482 53480

以下は、 時間名前空間の生成例を示すものであり、 boottime クロックを、 数年前の時点に設定している。

# uptime -p             # 初期の時間名前空間の uptime を表示
up 21 hours, 30 minutes
# unshare --time --fork --boottime 300000000 uptime -p
up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes

作者

関連項目

newuidmap(1) newgidmap(1) clone(2), unshare(2), namespaces(7), mount(8)

バグ報告

バグ報告は、 以下にある issue トラッカーを利用すること。 <https://github.com/util-linux/util-linux/issues>.

入手方法

unshare コマンドは util-linux パッケージの一部であり、 以下からダウンロードできる。 Linux Kernel Archive <https://www.kernel.org/pub/linux/utils/util-linux/>.