初めてでも大丈夫🙆‍♂️Nginxコンテナを本番運用するときのセキュリティー対策

## はじめに

nginxは多くのプロダクトやサービスで使用されているのですが、以外にセキュリティーの設定が分かり難かったりする部分があったので調べてみました。

nginxで行うセキュリティー対策は他にも沢山あるのですが、
今回は**コンテナとして本番運用**する際に絶対にやった方がいい基本的な設定を取り上げました。

公式のドキュメントにもさらっと書いてあるのですが、より詳細に解説していこうと思います。
また、今回はデフォルトのnginxの設定ファイルをベースに解説していきます。
https://hub.docker.com/_/nginx

## 1. non-rootユーザー（root以外のユーザー）で起動する

nginxを初め、多くのdockerイメージはデフォルトではrootで起動する設定になっています。
rootユーザーはありとあらゆる操作が可能で、万が一このユーザーでコンテナへの侵入を許してしまうと
ホスト側の操作もroot権限で実行できてしまうので非常に危険です。
コンテナだけでなく、基本的にroot権限でサーバーを運用するのは控えるべきです。

### nginxユーザーで起動する

実際に以下の様にDockerfileでユーザをnginxに指定してコンテナを作成してました。

```Dockerfile
FROM nginx:latest

USER 101 #nginxのUID
# USER nignx でも大丈夫です！
```
イメージのビルドが済んでしまえばroot権限は不要なので、nginxユーザーを指定して起動を試みたのですが以下の様なエラーがでてしまいました。。。
nginx以外のイメージはroot以外のユーザーを作成、指定してあげるだけで起動できる場合が多いのですが、
nginxはそれだけでは起動できないようです。。😑（幸いなことにnginxユーザーはデフォルトで作成されているようです）

- nginxユーザーを指定したときのエラー

```bash

docker run -u nginx -p 80:80 nginx:1.17.8-alpine                                     ✘ 1
[warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
[emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
```

nginxユーザーで起動できるようにもう少しDockerfileの作成やnginxの設定をしていく必要がありそうですね🤔
順次解説していきたいと思います。

###待ち受けportを変更する
nginxをデフォルトで起動すると、待ち受けのポートとして80番が使用されます。
しかし、0から1023番までのポートはウェルノウンポートと言われ、**このポートを使用するためにはroot権限が必要です。**（参考：[ウェルノウンポート](https://ja.wikipedia.org/w/index.php?title=%E3%83%9D%E3%83%BC%E3%83%88_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)&action=edit&section=2#%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7%E3%81%AE%E7%A8%AE%E9%A1%9E)とは）
そのため、nginxをroot権限以外で起動するためには、待ち受けのポートを1024番以降で指定する必要があります。
今回は8080番ポートでnginxが待ち受けられるように以下の例のように/etc/nginx/conf.d/default.confの設定を変更します。

#### nginxの設定を変更

```/etc/nginx/conf.d/default.conf
server {
    listen       8080; # <- この部分を変更
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

#### dockerのポートを変更

先ほどの設定でコンテナの中で起動するnignxのポートは8080番で起動するようになりました。
デフォルトのnginxイメージはコンテナポートが80番で開かられる設定になっているので、こちらもnginxの待ち受けポートである8080番に揃える必要があります。
また、先ほど作成したファイルがimageをbuildする際に取り込めるように設定を追加します。
COPY元のファイル名、パスは任意で構いませんが、COPY先は`/etc/nginx/conf.d/default.conf`を指定します。

```
FROM nginx:latest

COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080

USER 101
```
上記の設定でroot権限を必要としないport番号でコンテナを起動できるようになりました！
が、しかし

### nginxユーザーでも起動できるようにファイルの権限を設定する

デフォルトのnginxの設定は、pidの情報や各種一時ファイルの出力先がrootユーザーが所有するフォルダになっています。
nginxユーザーだとそれらのファイルに書き込むための権限がないので`/tmp`配下など、どのユーザーでも読み書きできるファイルに出力するように設定する必要があります。
具体的には以下のように設定します。


```/etc/nginx/nginx.conf
# user  nginx; <- マスタープロセスがroot起動時の設定なのでなくても大丈夫です
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;

#変更前 pid        /var/run/nginx.pid;
pid        /tmp/nginx.pid;


events {
    worker_connections  1024;
}


http {

    ##### 追加する設定 #####
    proxy_temp_path /tmp/proxy_temp;
    client_body_temp_path /tmp/client_temp;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;
    ######################

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
```

Dockerfile側で上記のファイルが取り込めるように設定を追加します。COPY先は`/etc/nginx/nginx.conf`です
また、キャッシュの書き込みが出来るようにnginxユーザーに`/var/cache/nginx`への書き込み権限を付与します。

```
FROM nginx:latest

COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf

RUN chown -R 101:0 /var/cache/nginx \
    && chmod -R g+w /var/cache/nginx

USER 101
```

以上の設定でやっとnginxユーザーでコンテナを起動するようにできました🎊

## 2. Read-Only モードで起動する
dockerにはコンテナを起動する際(runする際)に、`read-only`モードを指定することが出来ます。
これを指定することでコンテナへの書き込みが不可能となり、改竄を防止することが出来ます。

以下に先ほど作成したDockerfileをbuildして、read-onlyモードでコンテナを起動するコマンドの例を載せておきます。

```
docker build -t nginx-non-root:latest .
docker run -p 8080:8080 --read-only -v $(pwd)/tmp:/tmp/ nginx-non-root:latest
```


## 所感
振り返ってみると結構設定項目数がありました🤔
設定方法は公式のdockerHubに記載されてはいたのですが、
解説までは無かったので備忘録としてまとめてみました。

不適切な部分や考慮漏れ、もっとこうしたら分かりやすくなる、等ございましたら教えて頂けると嬉しいです。


## おまけ
ここまで一生懸命記事を書いたのですが、ここで作成したdocker imageはすでに有志の方が作ってくれています。。。
色々めんどくさい。手っ取り早く設定ファイルが欲しい。という方は以下コマンドで使用することが出来ます🙄

```
docker pull nginxinc/nginx-unprivileged
```
DockerHub https://hub.docker.com/r/nginxinc/nginx-unprivileged
GitHub https://github.com/nginxinc/docker-nginx-unprivileged

## おまけ２

日々とてもとても有益な情報を発信しています。
Nginxコンテナを本番運用するときのセキュリティー対策よりも重要になる日が来るかもしれません😼

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">256人彼女が欲しい</p>&mdash; ぐしゃ@ フリーランスクラウドエンジニア (@gusya_the_fool) <a href="https://twitter.com/gusya_the_fool/status/1246020146440720385?ref_src=twsrc%5Etfw">April 3, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>