TenForward

技術ブログ。はてなダイアリーから移転しました

マウントプロパゲーション(4)〜 unbindable mount 〜

引き続きマウントプロパゲーションについて書いていきます。完全に私個人が理解するための資料です。間違いの指摘は大歓迎です。そもそも、このシリーズ、カーネル付属ドキュメントと mount_namespaces(7) に書いてあるんですよねw

tenforward.hatenablog.com

tenforward.hatenablog.com

tenforward.hatenablog.com

unbindableは、そのマウント自身が他でバインドマウントできないようにするための指定です。マウントプロパゲーションとは少し違うものに見えます。しかし、これはprivateと同じでさらにバインドマウントを禁止するフラグがついたものと考えることができます。

unbindableはセキュリティ対策的な意味合いがあります。配下に多数のマウントを持ったマウントポイントがあったとします(例えば / なんかは通常はそうですよね)。その多数のマウントを持ったマウントを再帰的にバインドマウントするオプション(--rbind)を指定して何度もバインドマウントするとどうでしょう? 配下には大量のバインドマウントが再帰的に出現することになり、「バインドマウントボム(bomb)攻撃」とも言える状態になってしまいます。これを防ぐためにこのような指定ができるようになっています。

簡単に試してみましょう。まずはトップレベルのマウントとしてtestというディレクトリを作成し、ここをtmpfsでマウントします。このtestというマウント配下にふたつディレクトリ(ab)を作成し、その片方aもtmpfsをマウントします。

# mkdir test
# mount -t tmpfs tmpfs test (ディレクトリtestをtmpfsマウントする)
# mkdir test/a
# mount -t tmpfs tmpfs test/a (test/aディレクトリを作成しtmpfsマウントする)
# mkdir test/b
# tree .
.
└── test  (←tmpfsマウント)
    ├── a (←tmpfsマウント)
    └── b (←ディレクトリ)

3 directories, 0 files

上のようにtmpfs配下にtmpfsとディレクトリがひとつずつある状態になります。

ここでディレクトbに親ディレクトリであるtestをバインドマウントします。この際、再帰的にマウントするように--rbindを指定します。

# mount --rbind test test/b
# tree .
.
└── test
    ├── a (←tmpfsマウント)
    └── b (←バインドマウント)
        ├── a (←tmpfsマウント)
        └── b

5 directories, 0 files

再帰的にマウントしていますので、b配下のatest/b/a)はtmpfsとしてマウントされたまま見えています。

ここでtest配下にcというディレクトリを作成し、ここも上で行ったbのように再帰的にマウントします。

# mkdir test/c
# mount --rbind test test/c

再帰的にマウントしていますので、c配下にはさきほど再帰的にマウントしたb配下のマウントもすべて再帰的にマウントされています。そして、先に行ったバインドマウントb以下にもcが出現し、その配下でもb配下のマウントが見えています。

# tree .
.
└── test
    ├── a (←tmpfsマウント)
    ├── b (←バインドマウント)
    │   ├── a (←tmpfsマウント)
    │   ├── b
    │   └── c (←バインドマウント)
    │       ├── a
    │       ├── b (←バインドマウント)
    │       │   ├── a (←tmpfsマウント)
    │       │   ├── b
    │       │   └── c
    │       └── c
    └── c (←バインドマウント)
        ├── a (←tmpfsマウント)
        ├── b (←バインドマウント)
        │   ├── a
        │   ├── b
        │   └── c
        └── c

19 directories, 0 files

これが繰り返されると、延々と再帰的なマウントが出現することになり、リソースを食いつぶすDoS攻撃が成り立ちます。このようなことを防ぐためにunbindableが存在します。

# mount -t tmpfs tmpfs test
# mkdir test/{a,b}
# mount -t tmpfs tmpfs test/a

ここまでは先程の例と同じです。さきほどの例では--rbindとだけ指定したところで同時に--make-unbindableを指定してバインドマウントします。

# mount --rbind --make-unbindable test test/b
# tree .
.
└── test
    ├── a (←tmpfsマウント)
    └── b (←バインドマウント)
        ├── a (←tmpfsマウント,unbindable)
        └── b

5 directories, 0 files

ここでさきほどと同様にtest/cを作成し、test--make-unbindableでバインドマウントします。

# mkdir test/c
# mount --rbind --make-unbindable test test/c
# tree .
.
└── test
    ├── a (←tmpfsマウント)
    ├── b (←バインドマウント)
    │   ├── a (←tmpfsマウント,unbindable)
    │   ├── b
    │   └── c
    └── c (←バインドマウント)
        ├── a (←tmpfsマウント)
        ├── b
        └── c

10 directories, 0 files

unbindableを指定した以外は同じ操作を行っていますが、test/b配下もtest/c配下でもバインドマウントは再帰的に行われていません(tmpfsはマウントされています)。

このようにunbindableを指定すると爆発的にバインドマウントが増殖することを防げます。