Skip to content

3-3. Mount Namespace

別のNamespaceも追加で隔離してみましょう。次はMount Namespaceです。

Mount Namespaceを分けてみる

Mount Namespaceは、マウント情報を分けることができるNamespaceです。
Namespace内で行ったマウントは、(一部例外を除いて) 別Namespaceに影響を及ぼしません
なお、マウントについては4-1で詳しく紹介します。

実はお配りしたテンプレートには、rootfsの設定がMount Namespaceを必要とする都合で、既にMount Namespaceを分ける処理が入っています。

go
// runサブコマンド
func runCommand(c Config) error {
  // cgroupの設定
  if err := SetupCgroup(c.Name, os.Getpid(), c.Cgroup); err != nil {
    return errors.WithStack(err)
  }

  // Namespaceを分離
  if err := unix.Unshare(unix.CLONE_NEWUTS); err != nil {
    return errors.WithStack(err)
  }

  // rootfsの設定
  _ = unix.Unshare(unix.CLONE_NEWNS) // rootfsで使うので、Namespace系の処理だが仮置き
  if err := SetupRootfs(c.Rootfs); err != nil {
    return errors.WithStack(err)
  }

  // 作成した簡易コンテナ内でエントリーポイントを実行
  path, err := exec.LookPath(c.EntryPoint[0])
  if err != nil {
    return errors.WithStack(err)
  }
  if err := unix.Exec(path, c.EntryPoint, os.Environ()); err != nil {
    return errors.WithStack(err)
  }

  return nil
}

この節では、既に仮置きされていたMount Namespaceの処理を、先程記述したUTS Namespaceの設定に合体させてみましょう!

ヒント1

flagsは、ビット論理和演算子|を使うことで、複数を同時に指定することができます。

ヒント2

上でも出てきましたが、Mount Namespaceのflagsunix.CLONE_NEWNSです!
なおCLONE_NEWMOUNTでない理由は、マウント名前空間が一番最初にできたNamespaceで、New Namespaceから名付けられたからです。

想定解答

想定解答
go
// runサブコマンド
func runCommand(c Config) error {
  // cgroupの設定
  if err := SetupCgroup(c.Name, os.Getpid(), c.Cgroup); err != nil {
    return errors.WithStack(err)
  }

  // Namespaceを分離
  if err := unix.Unshare(unix.CLONE_NEWUTS); err != nil { 
  if err := unix.Unshare(unix.CLONE_NEWUTS | unix.CLONE_NEWNS); err != nil { 
    return errors.WithStack(err)
  }

  // rootfsの設定
  _ = unix.Unshare(unix.CLONE_NEWNS) // rootfsで使うので、Namespace系の処理だが仮置き
  if err := SetupRootfs(c.Rootfs); err != nil {
    return errors.WithStack(err)
  }

  // 作成した簡易コンテナ内でエントリーポイントを実行
  path, err := exec.LookPath(c.EntryPoint[0])
  if err != nil {
    return errors.WithStack(err)
  }
  if err := unix.Exec(path, c.EntryPoint, os.Environ()); err != nil {
    return errors.WithStack(err)
  }

  return nil
}

Namespaceが分かれたことを確かめる

Namespace内でマウントをしても元のシェルに影響を及ぼさないことを確かめましょう。
Namespaceの分離にはroot権限が必要なので、sudo suを実行してrootになってからプログラムを実行して下さい。

umountコマンドを使って/procフォルダをアンマウントするとpsの結果が見れなくなるので、それを使って確かめてみます。
なお、procフォルダについては4-1で詳しく紹介します。

console
$ sudo su
# make run
go build -o main *.go
./main run bash
# ps                    ← ここではちゃんと表示できている
    PID TTY          TIME CMD
 177532 pts/8    00:00:00 sudo
 177533 pts/8    00:00:00 su
 177534 pts/8    00:00:00 bash
 177589 pts/8    00:00:00 make
 177634 pts/8    00:00:00 bash
 177706 pts/8    00:00:00 ps
# umount -l /proc
# ps                    ← /procをアンマウントすると表示できない
Error, do this: mount -t proc proc /proc
# exit
exit
# ps                    ← Namespaceを抜けるとちゃんと表示できている
    PID TTY          TIME CMD
 177532 pts/8    00:00:00 sudo
 177533 pts/8    00:00:00 su
 177534 pts/8    00:00:00 bash
 178291 pts/8    00:00:00 ps
#

また、以下のようにバインドマウントを行って確かめることもできます。
バインドマウントについても、4-1で詳しく紹介します。

console
$ sudo su
# make run
go build -o main *.go
./main run bash
# mkdir /tmp/container                       ← マウント元の準備
# echo "Mount Test" > /tmp/container/test.txt
# ls -l /tmp/container/
total 4
-rw-r--r-- 1 root root 11 Dec 16 07:47 test.txt
# mkdir /tmp/bind-dst                        ← マウント先の準備
# ls -l /tmp/bind-dst/
total 0
# mount --bind /tmp/container /tmp/bind-dst  ← バインドマウント
# ls -l /tmp/bind-dst/                       ← マウント元の中身が見れるように
total 4
-rw-r--r-- 1 root root 11 Dec 16 07:47 test.txt
# cat /tmp/bind-dst/test.txt
Mount Test
# exit
exit
# ls -l /tmp/container/                       ← マウント元のファイルは存続
total 4
-rw-r--r-- 1 root root 11 Dec 16 07:47 test.txt
# ls -l /tmp/bind-dst                         ← Namespaceを抜けるとマウントが切れる
total 0
#