gVisorを使ってdockerコンテナをより安全に利用する

# はじめに
gVisorとは、KubeCon2018でローンチが発表された[Open Container Initiative (OCI)](https://github.com/opencontainers/runtime-spec/blob/master/spec.md)に準拠したコンテナのランタイム `runsc` を含むプロジェクトのこと。
OCIについては、[この記事](https://blog.docker.com/2016/04/docker-engine-1-11-runc/) にわかりやすくまとめてある。

<img width="565" alt="kZ7lPEDwkIFnoq4nqnhtRbnTzqrqzN7Y9aBZTaXoQ8Q= (1).png" src="https://qiita-image-store.s3.amazonaws.com/0/14275/18d8dbef-19bc-e17a-bef1-d2b0879bdc1b.png">
https://blog.docker.com/2016/04/docker-engine-1-11-runc/ から引用

上記の記事にある図の `runc` というものが、今デフォルトで利用されているランタイムで、 `runc` の部分をよりセキュアに(ホストカーネルと隔離して)実行させようとしているのが `runsc`。

どうやってホストカーネルと隔離するかは、[gVisorのREADME](https://github.com/google/gvisor)にまとまっている。要するにDockerアプリケーションからのシステムコールをgVisorが一度受け取り、それをホストカーネルに渡す仕組みになっている。

![Layers (1).png](https://qiita-image-store.s3.amazonaws.com/0/14275/0b779219-4976-ee1b-43e1-4ac6a83ccbbb.png)
https://github.com/google/gvisor から引用

注意点としては、まだ動かないかもしれないけど、動かなかったら教えてね、くらいのふわっとした状況だということ。

# ためしてみる

今のところ、Linuxでの動作しか保証していないようなので、Linuxの環境を作る。今回はGCEで Ubuntu 16.04 LTS を選んだ。

Ubuntu 18.04 LTSだと、Docker, Bazelが正式にサポートされてない。また Container-Optimized OSはDockerバージョンが17.03と、gVisorの推奨するDockerバージョンを満たしていない。 
※いずれも2018/05/04時点

## Dockerのインストール
[Get Docker CE for Ubuntu | Docker Documentation](https://docs.docker.com/install/linux/docker-ce/ubuntu/) 
ドキュメント通りにコマンドを実行すればいい。

```
$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https ca-certificates \
    curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce
```

## Bazelのインストール
[Installing Bazel on Ubuntu - Bazel](https://docs.bazel.build/versions/master/install-ubuntu.html#install-with-installer-ubuntu)

こちらもドキュメント通り行う。

```
$ sudo apt-get install pkg-config zip g++ zlib1g-dev unzip python
$ curl -OL https://github.com/bazelbuild/bazel/releases/download/0.13.0/bazel
-0.13.0-installer-linux-x86_64.sh
$ chmod +x bazel
-0.13.0-installer-linux-x86_64.sh
$ ./bazel
-0.13.0-installer-linux-x86_64.sh
```

## gVisorのビルド

[Installation](https://github.com/google/gvisor#installation) < 略

```
$ sudo apt-get install git
$ git clone https://gvisor.googlesource.com/gvisor gvisor
$ cd gvisor
$ bazel build runsc
<ビルドにめっちゃ時間かかる>
$ sudo cp ./bazel-bin/runsc/linux_amd64_pure_stripped/runsc /usr/local/bin
```

## Dockerのruntimeに `runsc`を追加

`/etc/docker/daemon.json` を作成し、以下の内容を記入。

```
{
    "runtimes": {
        "runsc": {
            "path": "/usr/local/bin/runsc"
        }
    }
}
```

`sudo systemctl restart docker` でdockerデーモンを再起動し、設定を反映。

## runtimeに `runsc` を設定して実行

```
$ sudo docker run --runtime=runsc -it ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
a48c500ed24e: Pull complete
1e1de00ff7e1: Pull complete
0330ca45a200: Pull complete
471db38bcfbf: Pull complete
0b4aba487617: Pull complete
Digest: sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e
Status: Downloaded newer image for ubuntu:latest

<コンテナ内>
root@e1d3ad2716a8:/# echo hello
> hello
```

プロセスを見てみると、containerdはrunscを起動させているっぽい。一応 立ち上げたubuntuコンテナで、for文など試してみたけど問題なく動いた。

```
$ ps -auwx | grep docker
...
root     18501  0.0  0.0   9060  3760 ?        Sl   16:51   0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/e1d3ad2716a8e38c72fc5c8091e7bf39b8047301a512d66a989537e2b11a5d4b -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runsc

```

# gVisorのコードについて

https://github.com/google/gvisor/tree/master/runsc/cmd
ここでOCIに準拠したruntimeのインタフェースを定義している。

https://github.com/google/gvisor/tree/master/pkg/sentry
このあたりでシステムコールまわりの処理をしているっぽい。

# セキュリティについて #1

他にセキュリティを強固にするためのランタイムに [Kata Containers](https://katacontainers.io/)というものがある。([1pagerはこちら](https://katacontainers.io/collateral/kata-containers-1pager.pdf)

これはそもそもVM上にあるコンテナが対象で、コンテナごとに軽量カーネルをつくる。超軽量なので、軽量カーネルなしのコンテナと同じパフォーマンスが出るらしい。 `<100ms boot time` と書いてあるので確かに爆速だ。

<img width="673" alt="katacontainers.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14275/dc73b9ac-4ff6-03b3-6186-f2825ca405fb.png">
https://medium.com/kata-containers/why-kata-containers-doesnt-replace-kubernetes-75e484679727

コードは見れていないが、VMと同程度のセキュリティが担保されそう。

# セキュリティについて #2
[2018年のDocker・Moby](https://www.slideshare.net/AkihiroSuda/japan-container-days-2018dockermoby)という須田さんのプレゼンにもセキュリティについて触れている。 P.55から。 資料自体が、とても面白い。

[runcをrootless](https://github.com/rootless-containers/runrootless)にするという考えもあるという話。
今回調べた結果、すでに色々なアプローチをとられていることがわかったので、どこかのタイミングでまとめたい。

Podmanというrootlessのアプローチのプロダクトも出ている。

# 感想

最近はもはや何かを実行するときはコンテナの中で実行することが多い。ローカルには基本的に言語の実行環境をインストールせず、コンテナで実行環境のバージョンなどを指定して実行するようになった。

もちろんanyenvなどを使ってバージョン管理する方法もあるけど、ローカルの依存関係が複雑になっていくのが嫌だ。
あとコンテナにしておけば、k8sで簡単に並列処理できるし。

その反面、使うコンテナに含まれるセキュリティホールについては意識していなかったので、gVisorの需要は高い。
gVisorがMacに対応されたら、デフォルトのランタイムをrunscに変えるだろう。