AWS EC2でサーバを構築 3 - Apache + 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">**ウェブサーバにNginxを使用する場合、本記事は読み飛ばしてください。**</font>

Apache (Event MPM) とSSL (Let's Ecnrypt)、PHP-FPMを設定します。

## 脆弱性診断結果

結論から書いてしまいますが、以下の設定で[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+**」になるはずです。

## Apahce

管理者メールアドレス等、お決まりの項目は除いて説明します。以下は気をつける部分です。

**/etc/httpd/conf/httpd.conf**

```apacheconf
<Directory "/var/www/html">
...

#Options Indexes FollowSymLinks
Options FollowSymLinks ExecCGI MultiViews

#AllowOverride None
AllowOverride All
```

```apacheconf
#AddHandler cgi-script .cgi
AddHandler cgi-script .cgi
```

### バーチャルホスト

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

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

Let's Ecnryptが80/tcpを使用するため、それを待ち受ける仮設定をしておきます。

**/etc/httpd/conf.d/vhosts.conf**

```apacheconf
<VirtualHost *:80>
  ServerName  www.example1.com
  ServerAlias example1.com
  DocumentRoot /var/www/www.example1.com/public_html
  CustomLog logs/www.example1.com.access_log combined
  ErrorLog  logs/www.example1.com.error_log
</VirtualHost>

<VirtualHost *:80>
  ServerName  www.example2.com
  DocumentRoot /var/www/www.example2.com/public_html
  CustomLog logs/www.example2.com.access_log combined
  ErrorLog  logs/www.example2.com.error_log
</VirtualHost>
```

Apacheを起動しておきます。

```bash
systemctl start httpd
```

### 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@example2.com
```

### 証明書の自動更新

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

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

```bash
crontab -e
```

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

### バーチャルホスト (SSL対応)

証明書取得後のバーチャルホスト設定を行います。

常時SSL化（ポート80へのアクセスを443に301リダイレクト）の設定も行っておきます。

ドメイン正規化 (wwwあり/なし) は`.htaccess`で行う想定です。このファイル内で行うことも可能です。

**/etc/httpd/conf.d/vhosts.conf**

```apacheconf
<VirtualHost *:80>
  ServerName  www.example1.com
  ServerAlias example1.com
  Redirect permanent / https://www.example1.com/
</VirtualHost>

<VirtualHost *:80>
  ServerName  www.example2.com
  DocumentRoot /var/www/html/www.example2.com/public_html
  # Redirect permanent / https://www.example2.com/
</VirtualHost>

<VirtualHost *:443>
  ServerName  www.example1.com
  ServerAlias example1.com
  DocumentRoot /var/www/www.example1.com/public_html
  CustomLog logs/www.example1.com.access_log combined
  ErrorLog  logs/www.example1.com.error_log
  SSLEngine on
  SSLCertificateFile    /etc/letsencrypt/live/www.example1.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.example1.com/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/www.example1.com/chain.pem
</VirtualHost>

<VirtualHost *:443>
  ServerName  www.example2.com
  DocumentRoot /var/www/html/www.example2.com/public_html
  CustomLog logs/www.example2.com.access_log combined
  ErrorLog  logs/www.example2.com.error_log
  SSLEngine on
  SSLCertificateFile    /etc/letsencrypt/live/www.example2.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.example2.com/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/www.example2.com/chain.pem
</VirtualHost>
```

### セキュリティ設定

脆弱性診断対策として古い暗号プロトコルや暗号スイートを排除します。

以下を**末尾に追記**します。上の方に同じ項目がありますが、そこを変えてもバーチャルホスト側で有効になりません。

**/etc/httpd/conf.d/ssl.conf**

```apacheconf
SSLProtocol -all +TLSv1.2
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA:!3DES:!RC4:!DH
SSLHonorCipherOrder on
Header set Strict-Transport-Security "max-age=315360000;
```

### CAAレコード追加

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

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

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

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

### MPM変更

ApacheのMPMを古いPreforkからEventに切り替えます。

**/etc/httpd/conf.modules.d/00-mpm.conf**

```apacheconf
# LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

LoadModule mpm_event_module modules/mod_mpm_event.so
```

###  チューニング

パフォーマンスチューニング設定です。環境に合わせて調整してください。

> テストの結果、結局コア数に揃えるのが良いという情報を見たため以下のようにしています。

**/etc/httpd/conf.d/mpm.conf**

```apacheconf
<IfModule mpm_event_module>
  StartServers           4
  MinSpareThreads        4
  MaxSpareThreads        4  
  ThreadsPerChild        4
  MaxRequestWorkers      4
  MaxConnectionsPerChild 0
</IfModule>
```

###  モジュール版PHP廃止

PHP-FPM (FastCGI) を動かすため、mod_php（モジュール版）を使用しないよう設定します。

この設定は不要な場合があります。筆者の環境では下記ファイルは無く、作業不要でした。

mod_phpが読み込まれているかどうかは`httpd -M | grep php`で確認できます。

**/etc/httpd/conf.modules.d/15-php.conf**

```apacheconf
<IfModule !mod_php5.c>
  <IfModule prefork.c>
    # LoadModule php7_module modules/libphp7.so
  </IfModule>
</IfModule>


<IfModule !mod_php5.c>
  <IfModule !prefork.c>
    # LoadModule php7_module modules/libphp7-zts.so
  </IfModule>
</IfModule>
```

PHP-FPMとのソケット通信を行う設定を追記します。SetHandlerに後述するソケットのパスを指定します。

**/etc/httpd/conf.d/php.conf**

```apacheconf
<IfModule !mod_php5.c>
  <IfModule !mod_php7.c>
    # Enable http authorization headers
    SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1

    <FilesMatch \.(php|phar)$>
        SetHandler "proxy:unix:var/run/php-fpm/www.sock|fcgi://localhost"
    </FilesMatch>
  </IfModule>
</IfModule>
```

### Apacheが起動しない場合

以下のコマンドで起動時のエラー内容を表示できます。

```shell-session
systemctl status httpd
```

Apacheの設定内容が正しいかどうか、以下のコマンドで確認できます。

```shell-session
httpd -t
```

## 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
```

###  ソケット通信

ユーザーとグループ、ソケット通信を行うための設定を行います。

```ini
user = apache
group = apache

; listen = 127.0.0.1:9000
listen = /var/run/php-fpm/www.sock

listen.owner = apache
listen.group = apache

;listen.acl_users = apache,nginx
```

### チューニング

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

**/etc/php-fpm.d/www\.conf**

```ini
; pm = dynamic
pm = static

; pm.max_children = 50
pm.max_children = 12

; pm.start_servers = 5
pm.start_servers = 4

; pm.min_spare_servers = 5
pm.min_spare_servers = 4

; pm.max_spare_servers = 35
pm.max_spare_servers = 4
```

ApacheとPHP-FPMを再起動します。

```bash
systemctl restart httpd php-fpm
```

これで正常に起動しない場合、各種設定の見直しをしてください。

詳細な動作状態は`<?php phpinfo(); ?>`と書いたPHPファイルで確認できます。

## 参考：Composer

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

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

## 参考：BASIC認証

<span>www</span>.example2.com全体をBASIC認証で保護する想定です。

生成サービスを利用してパスワードファイルを作成しておきます。

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

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

.htaccessでBASIC認証を有効にします。

**/var/www/html/<span>www</span>.example2.com/public_html/.htaccess**

```bash
AuthType Basic
AuthName "Enter your ID and password"
AuthUserFile /var/www/html/www.example2.com/.htpasswd
require valid-user
```

```bash
chown -R dev. /var/www/html/www.example2.com
```