RaspberryPI を最初から設定してWEBサーバとして使う その３ ~ApacheのインストールとSSL設定~

# その２からの続き
　[前回](https://qiita.com/traumerei/items/2bbb6e027b9d6fa021ba)、OSの設定やWEBサーバのための設定をしたので、今回はいよいよWEBサーバとして必要なサービスの設定をしていこうと思う。
　その１（OSインストール等）は[こちら](https://qiita.com/traumerei/items/14555901f54170a7af71)。

# Httpdサービスの選定
WEBサーバを稼働させるためのHttpdサービスについては、いくつかの種類がある。有名なところではApache（アパッチ）やnginx（エンジンエックス）が使われている。その他、ラズパイにはあまり関係ないがWindowsでIIS（アイアイエス, Internet Information Servicesの略）というアプリケーションが使われている。おそらく、この３つが世界で最も利用されているのではないだろうか。
その他、ラズパイでも軽快に動作するlighttpdなどのパッケージもたくさんあるが、設定に関する情報などがあまり揃わないので、ある程度慣れるまでは最初の３つのうちのどれかにするのが無難だと思う。
ここでは、Apacheを利用する。Apacheはnginxと比べても情報量が格段に多く、PHPなどの動的ページを多く含むサイトに適しているという特徴がある。逆に、静的なページが多い場合はnginxの方が適している。全体的にはnginxの方が仕組み上ダウンしにくい気がするので、慣れてきたら特段に動的ページが多い等の場合でなければnginxにするのも良いだろう。

# Apacheのインストール・動作確認
まずはインストールせな始まらんよね。現行の最新のバージョンは2.4.xxになる。**2.2以前は設定ファイルの書き方が大きく異なる**ので、必ず2.4以降のバージョンを入れよう。WEBの記事等で設定のやり方を調べる場合も、**記事内のバージョンが2.2以前か2.4以降かは必ず確認すること**。

```console:コマンド入力
# apt info apache2
一覧表示... 完了
apache2/stable 2.4.38-3+deb10u3 armhf
```
パッケージの内容を確認したところ、2.4.38がインストールされるようだ。問題ないのでインストール。

```console:コマンド入力
# apt-get install apache2
~ 略 ~
以下の追加パッケージがインストールされます:
  apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libbrotli1 libjansson4
  ssl-cert
提案パッケージ:
  apache2-doc apache2-suexec-pristine | apache2-suexec-custom www-browser openssl-blacklist
以下のパッケージが新たにインストールされます:
  apache2 apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libbrotli1
  libjansson4 ssl-cert
アップグレード: 0 個、新規インストール: 11 個、削除: 0 個、保留: 0 個
2,296 kB のアーカイブを取得する必要があります。
この操作後に追加で 7,067 kB のディスク容量が消費されます。
続行しますか? [Y/n]
```
追加のパッケージが自動的にリストアップされるので、`Y`を入力してインストールしよう。
インストールが完了したら、正常に動いているか確認する。

```console:コマンド入力
# systemctl status apache2
● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2020-08-27 11:18:00 JST; 5min ago
～ 略 ～
```
いつも通り、Active:の行に **<font color="Lime">active (running)</font>** とあればOK。
また、Apacheが正常に動作していれば、すでに最低限のWEBサーバとしての機能も稼働している。試しにリクエストしてみよう。

```console:コマンド入力
# curl 127.0.0.1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
～ 略 ～
```
`curl`で自分自身（127.0.0.1）にアクセスしてみたところ、見事にhtmlのソースが返ってきた！
これでこのラズパイが正常にWEBサーバとしても動作していることが確認できたね。なお、DNSの設定などを別途していれば、この段階で外部からもアクセスできるようになっているはず。もしできなければ、ルータのセキュリティやポートフォワードなどの設定を見直してみよう。

# Apacheの設定、モジュール導入
さて、ここから肝心の設定を入れ込んで行くぞ。設定の内容は非常に膨大なので、ここではあくまでその一部の紹介となる。慣れてきたら公式サイトやその他解説サイトなどで勉強しよう。

設定ファイルの場所はデフォルトだとここになる。
`/etc/apache2/`
この中に`apache2.conf`のようなデフォルト設定のconfファイルと、conf, mods, sitesのそれぞれavailableとenabledのディレクトリがある。availableは予め準備されている機能のファイルが入っているが、これが全て有効化されている訳ではない。有効化されている機能についてはそれぞれのenabledディレクトリの方へ、リンクという形で保存されているのだ。modsのsslなど、enabledにリンクのない機能はavailableにあっても有効化されていないので、別途コマンドで有効化してあげる必要がある。

後々の作業を楽にするために、カレントディレクトリを設定ファイルの置き場へ移動しておく。

```console:コマンド入力
# cd /etc/apache2/
```

また、僕の場合、自分で入れたコンフィグファイルが他と混ざってよく分からんということにならないように、ここのディレクトリに「myconf」というフォルダを作成している。
自分で新しく作ったconfファイルはここに置く。

```console:コマンド入力
# mkdir /etc/apache2/myconf
```
なお、この場合、`vi`コマンドを使って`/etc/apache2/apache2.conf`に`IncludeOptional myconf/*.conf`という一行を追加しておかないとapacheがconfファイルを読み込んでくれないので、忘れずに追記しておこう。

## HTTPSで接続できるようにする（SSLモジュールの有効化）
今日では、サイトの接続はおおむねHTTPSを用いてセキュアな通信で行う形になっている。
デフォルトではモジュールが有効化されていないので、この設定を加えよう。

```console:コマンド入力
# a2enmod ssl
～ 略 ～
Enabling module ssl.
～ 略 ～
# apachectl restart
```
`a2enmod`がモジュールを有効化するコマンド。apache2 enable moduleの略とかだと思う、たぶん。コマンドを叩いたあと、`Enabling module ssl.`の表示があれば問題なく有効化されている。最後にapache2のrestartが必要だと言われるので、restartする。ここでは`apachectl`を使っているが、もちろん`systemctl restart apache2`のコマンドでもOK。

ただし、このままではまだサイトは見られない。SSLでアクセスしてきた人に対してどんなページを見せるかの設定がapacheにまだないからだ。次はサイトの設定を入れる。自分で作ってもいいが、ここではデフォルトの設定を入れる方法を紹介する。

```console:コマンド入力
# a2ensite default-ssl
# apachectl restart
```

これでdefaultのssl設定が有効になった。httpsでブラウザから接続してみよう。ただし、証明書のエラーが出るため、一部のブラウザでは許可設定などしないと閲覧できない。

## 設定でアクセスしてきたURLを書き換えられるようにする（Rewriteモジュールの有効化）
より複雑な設定を行うために、Rewriteモジュールを導入する。これをいれておくと、アクセスしてきたURLをこちらで一方的に書き換えられるようになるので、例えばどこのページにアクセスしてもトップページへ進むようにするとか、httpでのアクセスを強制的にhttpsにする、といったことができる。

```console:コマンド入力
# a2enmod rewrite
# apachectl restart
```
sslのときと同様にEnabling moduleの表示があれば成功。apacheを再起動しよう。

## ページのheaderを設定できるようにする（Headersモジュールの有効化）
キャッシュコントロールをしたり、XSSやクリックジャッキングなどの攻撃対策にHeaderを指定しておくことは非常に有効。これも有効化しよう。

```console:コマンド入力
# a2enmod headers
# apachectl restart
```
Enabling moduleの表示が(ry。apacheを再(ry。

## その他（include, remoteipモジュールの有効化）
それ以外に、僕がインストールしていたmod。

- include …… SSI（Server-Side Includes）のために導入。今はもう要らないかも。
- remoteip …… AWSなどの環境で、ロードバランサー下でもクライアントのIPを正しく取得するために使用。

```console:コマンド入力
# a2enmod include
# a2enmod remoteip
# apachectl restart
```

Enabling(ry。apache(ry。

# SSL証明書（Let's Encrypt）の導入
HTTPSで接続するにあたって必要なSSL証明書だが、現状ではデフォルト生成のものを利用しているため、ブラウザによってはエラーが出てくる。これを避けるため、証明書を無料で作成してくれるLet's Encryptを入れてみる。
Let's Encryptの詳細は[公式サイト](https://letsencrypt.org/)へどうぞ。 

まずはインストールしよう。Let's Encryptなんだけど、パッケージ名は`certbot`。

```console:コマンド入力
# apt list certbot
一覧表示... 完了
certbot/stable 0.31.0-1 all
# apt-get install certbot
```
pythonで動くプログラムなので、インストールしようとすると追加でpython3系がガッツリ入る。`Y`でインストール。

まずはcertbotがサーバを確認するためのディレクトリ`acme-challenge`を作成する必要がある。

```console:コマンド入力
# mkdir -p DocumentRoot/.well-known/acme-challenge
```
コマンド中の`DocumentRoot`は各自のApacheのconfファイルにあるDocumentRootの設定を入れる。例として、デフォルトのままならDocumentRootが`/var/www/html`なので、コマンドは`mkdir -p /var/www/html/.well-known/acme-challenge`となる。
また、もしもアクセス制限等を掛けていて外部からアクセスできないような状態になっている場合は、`.htaccess`を用いるなどして、このディレクトリだけ制限を外すこと。

準備ができたら、まずはテストしてみる。certbotの証明書確認には試行回数の制限があるので、最初のうちは必ず`--test-cert`のオプションをつけてテスト実行にする。

```console:コマンド入力
# certbot certonly --test-cert --webroot -w DocumentRoot -d ore-no-hosts.ore-no-domain -m ore-no-mailaddress@mymaildomain.com
～ 略 ～
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
～ 略 ～
```
`DocumentRoot`には先ほどと同じくDocumentRootの設定を、`ore-no-hosts.ore-no-domain`にはこのサーバへアクセスできるFQDNを、`ore-no-mailaddress@mymaildomain.com`にはサーバ管理者（つまり自分）のメールアドレスを入れる。
規約に同意しろとかメール登録に同意しろとか言われたりするが、よく読んで同意する。
成功すれば、IMPORTANT NOTES:の項目に`Congraturations!`という返事が返ってくる。

証明書確認の動作としては、
　1. `acme-challenge`ディレクトリに確認用のランダムファイルをcertbotが作成
　2. インターネット上から上記のランダムファイルが見られるか確認 
という形になるので、certbotが`acme-challenge`ディレクトリにファイルを作成できなかったり、インターネット上からサーバにアクセスできなかったりというエラーが起こりやすい。
失敗したら、エラー出力をよく読んで、**ディレクトリのファイル作成権限の設定が正しいか確認**したり（問題があれば`chmod`で修正）、**自分で試しに`acme-challenge`ディレクトリにファイルを作成して、インターネットでそれを閲覧できるか確認**したり（サーバ全般に接続できなければルータやラズパイのport等セキュリティ設定・ドメインのDNSを確認、このディレクトリだけ接続できなければapacheのconfや.htaccessを修正）してみよう。

成功したら次は本番。`--test-cert`のオプションを抜いて同じコマンドを実行。

```console:コマンド入力
# certbot certonly --test-cert --webroot -w DocumentRoot -d ore-no-hosts.ore-no-domain -m ore-no-mailaddress@mymaildomain.com
～ 略 ～
What would you like to do?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Keep the existing certificate for now
2: Renew & replace the cert (limit ~5 per 7 days)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
～ 略 ～
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
～ 略 ～
```
もう一度、規約に同意しろとかメール登録に同意しろとか言われたりするが、再度よく読んで同意する。
また、先ほどのテスト実行で証明書が一回作られているので、証明書あるけど置き換える？　と聞かれる。「`2`」を入力して置き換えておく。文中にある通り、置き換える回数は決まっているので無駄に何度も実行したりしないように。

再度、同じように`Congraturations!`が出たら成功。

apacheのconfファイルを編集して、証明書として今作成したLet's Encryptのものを利用するように設定する。

```console:コマンド入力
# vi /etc/apache2/sites-available/default-ssl.conf
```
もしHTTPSのページの設定でデフォルトのサイトを使用していない場合は、そのconfファイルに置き換えてコマンドを入力して欲しい。

```vim:default-ssl.confの修正
～ 略 ～
SSLCertificateFile	/etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
～ 略 ～
#SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
～ 略 ～
```
上記のように、`SSLCertificateFile`、`SSLCertificateKeyFile`、`SSLCertificateChainFile`とある行を探す。右側の設定値は違うかもしれないし、`#`でコメントアウトされているかもしれない。今現在の僕の`default-ssl.conf`では、それぞれ32行目、33行目、42行目にあった。vim上で`:32`と打ってみると見つけやすそうだ。検索してもいいけどね。

さて、これらを見つけたら、それぞれコメントアウトして設定値をLet's Encryptのファイルに変更した行を追記する。目的のファイルのパスは、先ほど`certbot`を実行した時の出力文中で教えてくれている。

```vim:default-ssl.confの修正
～ 略 ～
# SSLCertificateFile	/etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateFile	/etc/letsencrypt/live/ore-no-hosts.ore-no-domain/cert.pem
# SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
SSLCertificateKeyFile /etc/letsencrypt/live/ore-no-hosts.ore-no-domain/privkey.pem
～ 略 ～
# SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
SSLCertificateChainFile /etc/letsencrypt/live/ore-no-hosts.ore-no-domain/fullchain.pem
～ 略 ～
```

コマンド中の`ore-no-hosts.ore-no-domain`部分は、先ほど`certbot`のコマンドで入力していた「このサーバへアクセスできるFQDN」になる。

さて、confの修正が出来たら、ちゃんと修正できているかを確認して、OKなら再起動する。

```console:コマンド入力
# apachectl -t
Syntax OK
# apachectl restart
```

`apachectl -t`コマンドは、confファイル中にsyntaxエラーがないかを確認してくれる。
設定したDocumentRootが存在しなかったり、SSL証明書に指定したファイルが存在しなかったりすると教えてくれるので、apacheのconfを編集した際には、再起動前に必ず確認するようにしよう。

ここまでで現在の稼働には何の問題もないのだけど、Let's EncryptのSSL証明書は有効期限が90日間しかないため、定期的に更新する必要がある。
UbuntuやDebian系（もちろんRaspbianも含む）では、`certbot`パッケージのインストール時に`/etc/cron.d/certbot`というファイルが自動的に作成され、`crond`を通して定期的に証明書の更新処理が走るようになっている。
ただし、更新した証明書を適用するためにはapacheの再読込が必要となるので、その分のコマンドを`/etc/cron.d/certbot`に追記しておこう。

```console:コマンド入力
# vi /etc/cron.d/certbot

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
　↓↓
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew --post-hook "systemctl reload apache2"
```

上記の通り、`/etc/cron.d/certbot`の一番下にある`0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew`という行の末尾に、` --post-hook "systemctl reload apache2"`を追加する。
`--post-hook`は、証明書の更新が成功された際に、追加で行うコマンドを指定するオプションだ。ここでサーバの再読込を行うコマンドを指定しておく。
なお、`certbot renew`コマンド（`-q`は画面出力を抑制するオプション）は、証明書の残り期限が30日よりも長い場合には実行されないため、何度も更新が走ったりサーバが再読込されたりすることはない。

これで、サイトのHTTPS化については一通り完了だ。

次回はPHPを入れてみよう。