AWS EC2でサーバを構築 4 - Nginx + Let's Ecnrypt + PHP-FPM編 (脆弱性診断「A+」を狙う)

## 記事一覧

- [AWS EC2でサーバを構築 1 \- Amazon Linux 2 準備編](https://qiita.com/ryu-blacknd/items/23b86d9ad768d58ff161)
- [AWS EC2でサーバを構築 2 \- 各種インストール編](https://qiita.com/ryu-blacknd/items/36ccfcf96da7b0c981cf)
- [AWS EC2でサーバを構築 3 \- Apache \+ Let's Ecnrypt \+ PHP\-FPM編 \(脆弱性診断「A\+」を狙う\)](https://qiita.com/ryu-blacknd/items/77eebe43a18a69e7c1e5)
- [AWS EC2でサーバを構築 4 \- **Nginx \+ Let's Ecnrypt \+ PHP\-FPM編 \(脆弱性診断「A\+」を狙う\)**](https://qiita.com/ryu-blacknd/items/4049f04e445eb1d60e4d)
- [AWS EC2でサーバを構築 5 \- MySQL \+ phpMyAdmin](https://qiita.com/ryu-blacknd/items/03030d11eeef65e3cf67)
- [AWS EC2でサーバを構築 6 \- PostfixとDovecot \(SSL対応とSPAM対策\)](https://qiita.com/ryu-blacknd/items/8e0c28065af3f9b8a06d)
- [AWS EC2でサーバを構築 7 \- セキュリティ対策](https://qiita.com/ryu-blacknd/items/20e5540195501e16e90e)

## 概要

<font color="#dd4444">**ウェブサーバにApacheを使用する場合、本記事は読み飛ばしてください。**</font>

Nginx、SSL (Let's Ecnrypt)、PHP-FPMの設定を行います。また脆弱性診断で最高の「**A+**」を狙います。

> Apacheを設定済みの場合は削除するか、`systemctl disable httpd`しておいてください。削除を推奨します。

## 脆弱性診断結果

結論から書いてしまいますが、以下の設定で[SSL Labs](https://www.ssllabs.com/)の脆弱性診断を行ったところ最高の「**A+**」となりました。

![20200413_131134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6359/c62ae9f8-5c70-c8d0-3e07-0b3ea686c949.png)

> 当初は「**A**」でしたが、設定を見直して「**A+**」を達成できました。以下の通りの設定で「執筆時点では」「**A+**」になるはずです。

## Nginx

エラー画面でバージョンを吐かないようにし、SSLに関するデフォルト設定を書いておきます。

ここが脆弱性診断「**A+**」のキモとなります。

**注意**：以下は`server`ではなく`http`ディレクティブに追加してください。

**/etc/nginx/nginx.conf**

```nginx
    server_tokens       off;

    ssl_protocols TLSv1.2; 
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout  1d;
    ssl_ciphers HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA:!3DES:!RC4:!DH;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
```

> Amazon Linux 2のOpenSSLが1.0.2kなので、OpenSSL 1.1.1以降が必要となるTLSv1.3は設定できません。  

### バーチャルホスト

バーチャルホスト用に公開ディレクトリを作成しておきます。

Apacheとの互換を維持するため、`var/www`で始まり`public_html`で終わるようにしています。

```bash
mkdir -p /var/www/html/www.example1.com/public_html
mkdir -p /var/www/html/www.example2.com/public_html
chown -R nginx. /var/www/html
```

Let's Ecnryptの証明書取得時には`ssl_certificate`と`ssl_certificate_key`の行はコメントアウトしておきます。

また設定ファイル内でドメイン正規化 (wwwありに統一) を行います。Apacheの`.htaccess`が使えないためです。

**/etc/nginx/conf.d/<span>www</span>.example1.com.conf**

```nginx
server {
    listen      80;
    listen      443 ssl http2;
    server_name example1.com;
    return 301  https://www.$host$request_uri;

   ssl_certificate     "/etc/letsencrypt/live/example1.com/fullchain.pem";
   ssl_certificate_key "/etc/letsencrypt/live/example1.com/privkey.pem";

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

server {
    listen      80;
    server_name www.example1.com;
    root        /var/www/html/www.example1.com/public_html;
    return 301  https://$host$request_uri;
}

server {
   listen       443 ssl http2;
   server_name  www.example1.com;
   root         /var/www/html/www.example1.com/public_html;

   ssl_certificate     "/etc/letsencrypt/live/www.example1.com/fullchain.pem";
   ssl_certificate_key "/etc/letsencrypt/live/www.example1.com/privkey.pem";

   include /etc/nginx/default.d/*.conf;

   location / {
   }

   error_page 404 /404.html;
       location = /40x.html {
   }

   error_page 500 502 503 504 /50x.html;
       location = /50x.html {
   }
}
```

Aapcheの例と同じく、www.example2.comはBASIC認証を設定します。

生成サービスを利用して暗号化パスワードを生成しておきます。

**/var/www/html/<span>www</span>.example2.com/.htpasswd**

```bash
{ユーザー名}:{暗号化されたパスワード}
```

Apacheと異なり.htaccessが使えないため、設定ファイル内にBASIC認証の設定を記述します。

**/etc/nginx/conf.d/<span>www</span>.example2.com.conf**

```nginx
server {
   listen      80;
   server_name www.example2.com;
   root        /var/www/html/www.example2.com/public_html;
   return 301  https://$host$request_uri;
}

server {
  listen       443 ssl http2;
  server_name  www.example2.com;
  root         /var/www/html/www.example2.com/public_html;

  ssl_certificate     "/etc/letsencrypt/live/www.example2.com/fullchain.pem";
  ssl_certificate_key "/etc/letsencrypt/live/www.example2.com/privkey.pem";

  include /etc/nginx/default.d/*.conf;

  location / {
      auth_basic "Enter your ID and Password";
      auth_basic_user_file /var/www/html/www.example2.com/.htpasswd;
  }

  error_page 404 /404.html;
      location = /40x.html {
  }
  
  error_page 500 502 503 504 /50x.html;
      location = /50x.html {
  }
}
```

### SSL証明書取得

Let's Ecnryptの証明書を取得します。

```bash
certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d www.example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example2.com/public_html/ -d www.example2.com -m root@example1.com
```

### 証明書の自動更新

月曜と木曜の4時0分に自動更新されるようにします。

> Let's Ecnrypt証明書の期限は3ヶ月です。更新は週に5回までの制限があります。

```bash
crontab -e
```

```bash
0 4 * * 1,4  certbot renew --post-hook "systemctl restart nginx php-fpm postfix dovecot"
```

### CAAレコード追加

脆弱性診断への対策のため、DNSサーバ (ネームサーバ) でCAAレコードを追加します。

> できない場合やわからない場合は読み飛ばしてください。ただし診断結果に若干影響する可能性があります。

```bash
example1.com. 3600 IN CAA 0 issue "letsencrypt.org"
```

```bash
example2com. 3600 IN CAA 0 issue "letsencrypt.org"
```

## PHP-FPM

まずはPHPそのものの設定を行います。環境によって調整してください。

**/etc/php.ini**

```ini
;expose_php = On
expose_php = Off

; max_execution_time = 30
max_execution_time = 120

; memory_limit = 128M
memory_limit = 256M

; post_max_size = 8M
post_max_size = 128M

; upload_max_filesize = 2M
upload_max_filesize = 128M

;date.timezone =
date.timezone = Asia/Tokyo

; session.gc_maxlifetime = 1440
session.gc_maxlifetime = 86400
```

###  チューニング

実行ユーザー/グループの設定とパフォーマンスチューニングの設定を行います。環境によって調整してください。

**/etc/php-fpm.d/<span>www</span>.conf**

```ini
; user = apache
user = nginx

; group = apache
group = nginx

;listen.owner = nobody
;listen.group = nobody
;listen.mode = 0660
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

; pm.max_children = 50
pm.max_children = 30

; pm.start_servers = 5
pm.start_servers = 10

; pm.min_spare_servers = 5
pm.min_spare_servers = 10

; pm.max_spare_servers = 35
pm.max_spare_servers = 25

;pm.max_requests = 500
pm.max_requests = 30
```

NginxとPHP-FPMを再起動します。これは設定を変更するたびに行います。

```bash
systemctl restart nginx php-fpm
```

### Composer

Laravel等で使用するcomposerをインストールしておきます。

```bash
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
```

## トラブルシューティング

`/var/lib/php`内のセッションやキャッシュ用ディレクトリの所有グループがapacheになっている場合の対処法です。

```bash
chgrp nginx /var/lib/php/opcache
chgrp nginx /var/lib/php/session
chgrp nginx /var/lib/php/wsdlcache
```

セッションについては中のファイルが`apache:apache`な状態になっている場合があるので修正。

```bash
chown nginx. /var/lib/php/sesion/*
```