Docker+Nginx+PHP-FPMでユーザーをroot以外に設定する

Docker Composeでnginxとphp-fpmの環境を作るときの設定で少しハマりました。

実行ユーザーをrootのままにすればdocker-composeですんなり設定できるかもしれませんが、今後の運用も考えてセキュリティ面でroot以外に設定したほうが良いと思い、そのように設定を進めました。

php-fpmとDockerfileの設定を見直すことで実行ユーザーの設定ができましたのでその経緯を書いていきます。

今回想定している環境は下記になります。

- docker 18.09.2
- docker-compose 1.23.2
- image php:7.2-fpm
- image nginx:1.15

## TL; DR
php-fpmのwww.confでuser, groupを削除しlisten.groupのみ設定。そしてDockerfileでphp-fpmのファイルにグループ権限付与することでroot以外のユーザーで実行できる。

---

## Dockerfileの実行ユーザーはroot以外にする
NginxとMySQLはオフィシャルのイメージをビルドするだけで済みましたが、
PHPは設定する部分や必要な拡張子などが多かったのでDockerfileを個別に作りました。

ベストなDockerfileを書きたかったので[Dockle](https://github.com/goodwithtech/dockle)を用いてチェックしたところ、

```bash
WARN    - CIS-DI-0001: Create a user for the container
        * Last user should not be root
```  

という警告が。
こちらにも載っていました。　　
https://github.com/goodwithtech/dockle/blob/master/CHECKPOINT.md#cis-di-0001-create-a-user-for-the-container

セキュリティ的にも、Dockerfileでコンテナを実行するユーザーはroot以外の方が良いです。

それに`docker-compose exec`をroot権限で実行できてしまうのは危険ですからね。

そこでphp-fpmのDockerfileにユーザーを追加してみます。  

## php-fpmのDockerfileに追記
php-fpm用のユーザーを追加します。今回は`www-user`としています。

```diff:./docker/php/Dockerfile
FROM php:7.2-fpm

# Install extensions
RUN apt-get update \
	&& apt-get install -y libicu-dev zip unzip curl zlib1g-dev libpng-dev vim procps git \
	&& docker-php-ext-install intl opcache \
	&& docker-php-ext-enable intl opcache \
	&& docker-php-ext-install pdo pdo_mysql mysqli\
	&& docker-php-ext-install mbstring \
	&& docker-php-ext-install gd \
	&& apt-get clean && rm -rf /var/lib/apt/lists/*

+ RUN useradd -ms /bin/bash www-user \
+	&& echo "www-user:piyohoge" | chpasswd
+ USER www-user
```  

これで再度ビルドしてみます。

```zsh:ターミナル
% docker-compose down
% export $(cat .env | xargs)
% docker-compose build --no-cache
Creating network "myservice_app-network" with the default driver
Building php
Step 1/17 : FROM php:7.2-fpm
...
...
Successfully built 8d4ff435a5d2
Successfully tagged myservice_php:latest

% docker-compose up -d
creating network "myservice_mynetwork" with the default driver
Creating myservice_php_1 ... done
Creating myservice_db_1  ... done
Creating myservice_web_1 ... done
```  

上手くいったような気がします。
しかし、

## php-fpmのパーミッション周りでエラーが
ログを見てみるとエラーになっていました。

```zsh:ターミナル
% docker-compose logs php
Attaching to myservice_php_1
php_1  | NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root
php_1  | NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root
php_1  | NOTICE: [pool www] 'group' directive is ignored when FPM is not running as root
php_1  | NOTICE: [pool www] 'group' directive is ignored when FPM is not running as root
php_1  | ERROR: unable to bind listening socket for address '/var/run/php-fpm.sock': Permission denied (13)
php_1  | ERROR: unable to bind listening socket for address '/var/run/php-fpm.sock': Permission denied (13)
php_1  | ERROR: FPM initialization failed
php_1  | ERROR: FPM initialization failed
```  

myservice_php_1がExitに。
エラー内容を見てみると、php-fpmの実行ユーザーが上手く設定されていないようです。
rootじゃないとphp-fpmの`user`、`group`ディレクティブは無効になりますし、かといってrootにするわけにもいきませんし、ハマりました。

## Dockerfile、php-fpmの設定を修正する
調べていく中で、少し古い内容ですが、下記が参考になりました。
https://github.com/phpbrew/phpbrew/issues/459

root以外のユーザーでphp-fpmを立ち上げたい場合、**php-fpm用のグループを設定することで実行ができるようになる**とのことです。

そこで、`www.conf`でグループを設定し、そのグループに権限を与えます。

下記が`www.conf`の設定です。

```diff:/usr/local/etc/php-fpm.d/www.conf
[global]
daemonize = no

; Start a new pool named 'www'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('www' here)
[www]
- user = nginx
- group = nginx

listen = /var/run/php-fpm.sock
- listen.owner = nginx
listen.group = nginx
+ listen.mode = 0660
```  

Dockerfileの方でもグループへの権限を付与します。

php-fpmの実行ユーザー`www-user`を先ほど設定したグループに追加します。
ただ、上記で使用している`usermod`はグループを全て上書きしてしまい危険なので別の書き方にします。

php-fpmに関係するファイルにも権限を付与します。

```diff:./docker/php/Dockerfile
FROM php:7.2-fpm

# Install extensions
RUN apt-get update \
	&& apt-get install -y libicu-dev zip unzip curl zlib1g-dev libpng-dev vim procps git \
	&& docker-php-ext-install intl opcache \
	&& docker-php-ext-enable intl opcache \
	&& docker-php-ext-install pdo pdo_mysql mysqli\
	&& docker-php-ext-install mbstring \
	&& docker-php-ext-install gd \
	&& apt-get clean && rm -rf /var/lib/apt/lists/*

+ RUN chown -R www-user:nginx /var/www/ \
+	&& chown -R www-user:nginx /var/run/ \
+	&& chown -R www-user:nginx /var/log/

- RUN useradd -ms /bin/bash www-user \
+ RUN useradd -ms /bin/bash -g nginx www-user \
	&& echo "www-user:piyohoge" | chpasswd
USER www-user
```  

`docker volume rm`でボリュームを削除して再ビルド。

```zsh:ターミナル
% docker-compose build --no-cache && docker-compose up -d
...
% docker-compose ps
    Name                  Command               State                     Ports                   
--------------------------------------------------------------------------------------------------
myservice_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp                    
myservice_php_1   docker-php-entrypoint php-fpm    Up      9000/tcp, 9001/tcp                        
myservice_web_1   /bin/sh -c nginx -g daemon ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:8080->80/tcp
```  

これで上手く立ち上がりました。

初歩的なところかもしれませんが、もしphp-fpm周りの設定で詰まっていたら試してみてください。