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のflagsはunix.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になってからプログラムを実行してください。

今回はバインドマウントを行って確かめてみます。
バインドマウントについては、4-1で詳しく紹介します。

console
$ sudo su
# make run
go build -o main *.go
./main run bash
# mount --make-rprivate /                     ← おまじない (4-3に少し繋がります)
# 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
#