Fargate で構築した Laravel と WordPress の コンテナを 読み取り専用(ReadOnly)にする

# はじめに

Fargate で Laravel や WordPress を構築後、セキュリティを高める方法として、`読み取り専用(readonlyRootFilesystem)` を有効にする方法があります。
この設定を行なうと、コンテナ内のファイルやディレクトリが読み取り専用となり、マルウェア等に対してセキュリティーが高めることができます。


ただし、全てを読み取りにはできず、一時的に書き込みが必要なディレクトリがサーバー内にありますので、一部のディレクトリを読み書き可能なボリュームとしてマウントし、残りは読み込み専用にすることが必要です。

今回は、Fargate で構築した Laravel と WordPress を読み取り専用にする方法について方法を記載します。

ちなみに、`読み取り専用(readonlyRootFilesystem)` を有効にすると、Fargate Execが使用できないデメリットがあります。

# 事前構築

- 下記の記事でwordpressをfargateで構築
- ソースはDockerfileのみ

https://qiita.com/hirai-11/items/bde67b8334f3362c8009

```Dockerfile:wordpressのDockerfile
FROM wordpress:6.0.0-php8.1-apache

RUN set -ex; \
  apt-get update && apt-get install -y --no-install-recommends \
  unzip \
  wget \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# /usr/src/wordpress/wp-content/plugin にプラグインを置いておくと、ビルド時、ドキュメントルートに設置する
WORKDIR /usr/src/wordpress/wp-content/plugins

# プラグイン[WP Offload Media Lite for Amazon S3]をインストール
RUN set -ex; \
  wget -q -O amazon-s3-and-cloudfront.zip https://downloads.wordpress.org/plugin/amazon-s3-and-cloudfront.1.4.3.zip \
  && unzip -q -o '*.zip' -d /usr/src/wordpress/wp-content/plugins \
  && chown -R www-data:www-data /usr/src/wordpress/wp-content/plugins \
  && rm -f *.zip

# 所有者の変更
RUN chown -R www-data:www-data /var/www/html

WORKDIR /var/www/html
```

- 下記の記事でlaravelをfargateで構築
- ソースも添付(ブランチは、mainを使用してください)

https://qiita.com/hirai-11/items/241df305a90af3ccf973

https://github.com/yuji-hirai/laravel-ecs

# マウントタイプ
読み取り専用にするにあたり、/var/runなどの一時的に書き込みが必要なディレクトリがあるため、その部分のみ書き込みを許可する必要があります。
Dockerでよく用いられる`マウント`を行い、一部書き込みを許可します。

Fargateでマウントする方法は、3つあります
1. `Volume`をDockerfileに記載し、マウントする
1. タスク定義の`Bind mount`でマウントする
1. EFSを利用して、マウントする
    - EFSは、新たにリソースを作成する手間がある、EFSだと永続的に一時ファイルが保存される、という理由で今回は利用しません。

それぞれ説明します。

## VolumeとBind mountの違い
- `Bind mount` はホストマシン OS のディレクトリ構造に依存しますが、`Volume`は完全に Docker によって管理されます。
- `Volume`を作成しマウントすると、書き込まれるファイルシステムはDockerで管理され、ホストマシンからは見えません。
    - ホストマシンのディレクトリ構成を実行中のコンテナから分離することができます。

調べたところセキュリティー的には、`Volume`でマウントする方がよいようです。

# wordpressのコンテナを読み取り専用
Dockerfileに`Volume`を追加、タスク定義の`readonlyRootFilesystem`を`true`にします。

## Dockerfileの一部のディレクトリにVolumeでマウント
ソースは、Dockerfileのみです。
wordpress (fargate) では、下記の２つのディレクトリ内に一時ファイルが作成されるため、Volumeでマウントします
- /var/run/apache2
- /tmp

```diff_docker:wordpressのDockerfile
FROM wordpress:6.0.0-php8.1-apache

RUN set -ex; \
  apt-get update && apt-get install -y --no-install-recommends \
  unzip \
  wget \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# /usr/src/wordpress/wp-content/plugin にプラグインを置いておくと、ビルド時、ドキュメントルートに設置する
WORKDIR /usr/src/wordpress/wp-content/plugins

# プラグイン[WP Offload Media Lite for Amazon S3]をインストール
RUN set -ex; \
  wget -q -O amazon-s3-and-cloudfront.zip https://downloads.wordpress.org/plugin/amazon-s3-and-cloudfront.1.4.3.zip \
  && unzip -q -o '*.zip' -d /usr/src/wordpress/wp-content/plugins \
  && chown -R www-data:www-data /usr/src/wordpress/wp-content/plugins \
  && rm -f *.zip

# 所有者の変更
RUN chown -R www-data:www-data /var/www/html

WORKDIR /var/www/html

+ VOLUME ["/var/run/apache2", "/tmp"]
```

## タスク定義の`readonlyRootFilesystem`を`true`に修正
タスク定義の`読み取り専用ルートファイルシステム`にチェックマークをつけるだけです。
![スクリーンショット 2022-07-10 19.01.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/b113834d-e4c1-d113-cc6b-9cb1917cbec5.png)

これでデプロイすると、`/var/run/apache2`と`/tmp`を除いて、読み取り専用になりました。

# laravelのコンテナを読み取り専用
DockerfileにVolumeを追加、タスク定義の`readonlyRootFilesystem`を`true`にします。
また、タスク定義のコマンドを変更します。

ソースは、以下のmainブランチを使用します。

https://github.com/yuji-hirai/laravel-ecs/blob/main/docker/php/Dockerfile

## Dockerfileの一部のディレクトリにVolumeでマウント
### phpのDockerfile修正
phpのDockerfileでは、下記の4つのディレクトリを、Volumeでマウントします 
- /var/www/html/bootstrap/cache
- /var/www/html/storage/framework
- /var/www/html/storage/logs
- /tmp

```diff_docker:.docker/php/Dockerfile
FROM php:8.1-fpm-bullseye

# timezone environment
ENV TZ=Asia/Tokyo \
  # locale
  LANG=ja_JP.UTF-8 \
  LANGUAGE=ja_JP:ja \
  LC_ALL=ja_JP.UTF-8 \
  # composer environment 
  COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer

RUN apt-get update \
  && apt-get -y install --no-install-recommends \
    git \
    libzip-dev \
    libicu-dev \
    libonig-dev \
    locales \
    unzip \
    vim \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/* \
  && locale-gen ja_JP.UTF-8 \
  && localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 \
  && docker-php-ext-install \
    bcmath \
    intl \
    pdo_mysql \
    zip \
  && composer config -g process-timeout 3600 \
  && composer config -g repos.packagist composer https://packagist.org

COPY ./src /var/www/html
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

+ VOLUME ["/var/www/html/bootstrap/cache", "/var/www/html/storage/framework", "/var/www/html/storage/logs", "/tmp"]

RUN composer install -q -n --no-ansi --no-dev --no-scripts --no-progress --prefer-dist \
+ && chown -R www-data:www-data /var/www/html \
  && chmod -R 775 storage bootstrap/cache \
  && php artisan optimize:clear \
  && php artisan optimize \
  && php artisan cache:clear \
  && php artisan config:clear \
  && php artisan route:clear \
  && php artisan view:clear 
```

Volumeを使用すると、所有者がrootになったため、`&& chown -R www-data:www-data /var/www/html`も追加しています。

### phpのDockerfile修正
phpのDockerfileでは、下記の3つのディレクトリを、Volumeでマウントします 
- /var/cache/nginx
- /var/run
- /etc/nginx/conf.d

```diff_docker:.docker/php/Dockerfile
FROM nginx:1.20-alpine
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./src /var/www/html
ENV TZ=Asia/Tokyo
EXPOSE 80
+ VOLUME ["/var/cache/nginx", "/var/run", "/etc/nginx/conf.d"]
```

## タスク定義のコマンドを修正
コマンドは、以下の記事に記載しておりました。

https://qiita.com/hirai-11/items/241df305a90af3ccf973#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E8%BF%BD%E5%8A%A0php

- 変更前
`php artisan config:cache && php artisan view:cache && php artisan route:cache && php artisan migrate --force && chmod -R 777 storage/* && php-fpm && composer install --optimize-autoloader --no-dev`

↓

- 変更後
`php artisan config:cache && php artisan view:cache && php artisan route:cache && php artisan migrate --force && php-fpm && composer install --optimize-autoloader --no-dev
`

読み取り専用にすると、権限変更がエラーになりましたので、`chmod -R 777 storage/*`を削除しました。
必要であれば、Dockerfileに追記するとよいです。

## タスク定義の`readonlyRootFilesystem`を`true`に修正
先程と同様に、タスク定義の`読み取り専用ルートファイルシステム`にチェックマークをつけるだけです。

これでデプロイすると、4つのディレクトリを除いて、読み取り専用となります。

# laravelの/tmpのパスを変更してみる
phpコンテナでは、`/tmp`をVolumeでマウントしましたが、`/tmp`は予想がつきマルウェア等に狙われやすいため、`/fuga/tmp`パスを変更してみたいと思います。

やることは以下の2点です。
- phpのDockerfileを修正
- タスク定義の環境変数にTMPDIRを追加

## phpのDockerfileを修正
```diff_docker:.docker/php/Dockerfile
FROM php:8.1-fpm-bullseye

# timezone environment
ENV TZ=Asia/Tokyo \
  # locale
  LANG=ja_JP.UTF-8 \
  LANGUAGE=ja_JP:ja \
  LC_ALL=ja_JP.UTF-8 \
  # composer environment
  COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer

RUN apt-get update \
  && apt-get -y install --no-install-recommends \
    git \
    libzip-dev \
    libicu-dev \
    libonig-dev \
    locales \
    unzip \
    vim \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/* \
  && locale-gen ja_JP.UTF-8 \
  && localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 \
  && docker-php-ext-install \
    bcmath \
    intl \
    pdo_mysql \
    zip \
  && composer config -g process-timeout 3600 \
  && composer config -g repos.packagist composer https://packagist.org

COPY ./src /var/www/html
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini
+ WORKDIR /fuga/tmp
+ VOLUME ["/var/www/html/bootstrap/cache", "/var/www/html/storage/framework", "/var/www/html/storage/logs", "/fuga/tmp"]

RUN composer install -q -n --no-ansi --no-dev --no-scripts --no-progress --prefer-dist \
  && chown -R www-data:www-data /var/www/html \
  && chmod -R 775 storage bootstrap/cache \
  && php artisan optimize:clear \
  && php artisan optimize \
  && php artisan cache:clear \
  && php artisan config:clear \
  && php artisan route:clear \
  && php artisan view:clear 
```
`/fuga/tmp`ディレクトリを作成し、Volumeにもマウントさせます。

## タスク定義の環境変数にTMPDIRを追加
envファイル、もしくは、タスク定義に以下を追記します。
```envファイル
TMPDIR=/fuga/tmp
```

![スクリーンショット 2022-07-10 19.45.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/fc6a4d4c-4746-66d0-7adf-8839b1036d80.png)

`/tmp`ではなく、`/fuga/tmp`に一時ファイルが保存されるようになりました。

# Codebuildでビルドする場合
Codebuildでビルドする場合、Dockerfileに`Volume`や`権限変更`のコマンドが効かなかったため、タスク定義のコマンドに追加しました。


タスク定義のコマンドを追加

- 変更前
`php artisan config:cache && php artisan view:cache && php artisan route:cache && php artisan migrate --force && php-fpm && composer install --optimize-autoloader --no-dev`

↓

- 変更後
`php artisan config:cache && php artisan view:cache && php artisan route:cache && php artisan migrate --force 
&& chown -hR www-data:www-data /var/www/html/bootstrap/cache /var/www/html/storage/framework  /var/www/html/storage/logs /fuga/tmp && php-fpm && composer install --optimize-autoloader --no-dev`

# その他のfargateに関するtips

その他のfargateに関するtipsを記事としてまとめていますので、こちらを参考にしてください。

https://qiita.com/hirai-11/items/733c09b74aacce451aa7


# 参考記事

https://www.seeds-std.co.jp/blog/creators/2022-02-08-114520/

https://blog.tech-monex.com/entry/2021/11/05/182327

https://www.seeds-std.co.jp/blog/creators/2022-03-31-235602/


https://qiita.com/y518gaku/items/456f34c317a65a9dae86

https://www.ogis-ri.co.jp/otc/hiroba/technical/docker/part7.html

https://www.php.net/manual/ja/function.sys-get-temp-dir.php