CentOS 8に無料SSL「Let’s Encrypt」を設定する

さくらのVPSにCentOS 8をインストールした覚書です。

今回は、無料SSLで有名なLet's Encryptを設定していきます。

なお本番環境で利用される場合はここにある内容だけを鵜呑みにせずセキュリティ専門家に相談されることをお勧めします。

環境

実施日2020-07-07
サーバさくらのVPS 2G
OSCentOS 8.2
cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)

公式の手順

https://certbot.eff.org/lets-encrypt/centosrhel8-nginx
公式の手順はこちらです。(2020-07-07時点)

こちらを参考に進めていきたいと思います。

ちなみにCentOS7、6ではちょっと手順が異なります。
こちらのページをご覧ください。

初めてのインストール

1つ目のドメインを設定しながら、Let's Encryptをセットアップしていきます。
2つ目からは一部手順を省略する形になります。

EPELリポジトリ

EPELリポジトリを使います。
CentOS 8 をインストールして最初にすること
こちらの設定を行っている前提で進めていきます。

確認

dnf --enablerepo=epel list certbot python3-certbot-nginx
メタデータの期限切れの最終確認: 0:07:42 時間前の 2020年07月07日 16時06分00秒 に実施しました。
利用可能なパッケージ
certbot.noarch                 1.5.0-1.el8   epel
python3-certbot-nginx.noarch   1.5.0-1.el8   epel

インストール

dnf --enablerepo=epel install certbot python3-certbot-nginx

1つ目のドメインを設定

証明書を取得しようとするドメイン(http://ドメイン/.well-known/・・・)に対してLet's Encryptからの接続があります。

この時Basic認証とかアクセス制限を設定していると認証が完了できずに証明書の発行ができません。
あらかじめアクセスできるようにしておく必要があります。

certbot certonly --webroot -w /var/www/vhosts/example.com/public_html --email example@example.com --debug -d example.com

▲こちらの内容は自分の環境に合わせて書き換えてください。
分解すると次のような設定になります。「」の個所を書き換えます。

certbot certonly --webroot -w 「ドキュメントルート」 --email 「自分のメールアドレス」 --debug -d 「ドメイン」

初回は必要なパッケージが読み込まれます。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel:

「利用規約を読んで同意してください。」といった内容です。
画面にある https:// ~ のURLをコピペしてブラウザで開いて内容を確認します。
※上記は2017.pdfになっているので最新版はファイルが変わると思います。

同意する場合は、「a」を入力してEnterします。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o:

「Certbotを開発する組織ですか?」と聞かれてる個所があるので、「いいえ」かな。

「n」を入力してEnterします。

このままトラブルなく進んでいくと証明書が作成されます。

2つ目以降のドメインを追加

1つ目のドメインを追加するときは、Let's Encryptのインストールや設定も兼ねていたのですが、2つ目のドメインからは証明書の発行部分だけになります。

1つ目のときと内容は同じになりますが、下記を実行して証明書を作成します。

certbot certonly --webroot -w /var/www/vhosts/example.com/public_html --email example@example.com --debug -d example.com

▲こちらの内容は自分の環境に合わせて書き換えてください。
分解すると次のような設定になります。「」の個所を書き換えます。

certbot certonly --webroot -w 「ドキュメントルート」 --email 「自分のメールアドレス」 --debug -d 「ドメイン」

証明書の確認

作成された証明書は次の場所にあります。

ls -la /etc/letsencrypt/live

証明書を適用

nginx

nginxとPHP-FPMの設定 – PHP共存版
バーチャルホストの設定

こちらのページを参考に、/etc/nginx/conf.d/vhost-ドメイン.conf の http と https のコメントを入れ替える。

ついでに http (ポート80)からはhttpsに転送させる。

apache

記述するファイルはバーチャルドメインを設定している conf の中です。

apache 2.4

SSLCertificateFile    /etc/letsencrypt/live/ドメイン/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ドメイン/privkey.pem

apache 2.2

SSLCertificateKeyFile   /etc/letsencrypt/live/ドメイン/privkey.pem
SSLCertificateFile      /etc/letsencrypt/live/ドメイン/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/ドメイン/chain.pem

apacheの再起動

設定を反映させるため再起動します。

/etc/init.d/httpd restart

証明書の自動更新設定

自動化はCRONに設定するので、まずは手動で動かしてみて確認します。

# nginxの場合
certbot renew --post-hook "systemctl restart nginx"

# apacheの場合
certbot renew --post-hook "systemctl restart httpd"

この時点では期限切れの証明書もないので、「更新対象なし」として終了します。

問題無さそうなのでCRONに設定します。

vi /etc/cron.d/letsencrypt
MAILTO="通知先にする自分のメールアドレス"
0 2 * * 2 root certbot renew --post-hook "systemctl restart nginx"
# 0 2 * * 2 root certbot renew --post-hook "systemctl restart httpd"

実行時間はそんなに頻繁でなくて構いません。
上記だと「毎週火曜の午前2時0分」の設定なので週一実行です。
証明書の期限は90日です。
期限切れが近づいてきたときに更新処理が実行されれば更新されますが、まだまだ期限があるときに実行しても何も行われません。

不要になった証明書を削除

公開が終了したり、サーバを引越ししたりして使わなくなったドメインは証明書を削除しましょう。

次の手順で行います。
「ドメイン」の2カ所を書き換えてください。

# 確認
ls -la /etc/letsencrypt/live

# 失効
certbot revoke --cert-path=/etc/letsencrypt/archive/ドメイン/cert1.pem

# 削除
certbot delete -d ドメイン

トラブル対応

証明書の発行でエラーになる

次のようなエラーが発生したときの対応方法を記します。

Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for www.example.net
Using the webroot path /home/example_net/public_html for all unmatched domains.
Waiting for verification...
Challenge failed for domain www.example.net
http-01 challenge for www.example.net
Cleaning up challenges
Attempting to renew cert (www.example.net) from /etc/letsencrypt/renewal/www.example.net.conf produced an unexpected error: Some challenges have failed.. Skipping.
IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: www.example.net
   Type:   unauthorized
   Detail: Invalid response from
   https://www.example.net/.well-known/acme-challenge/xxxxxx
   [xxx.xxx.xxx.xxx]: "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML
   2.0//EN\">\n<html><head>\n<title>403
   Forbidden</title>\n</head><body>\n<h1>Forbidden</h1>\n<p"

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.

2つ目のメッセージに「https://www.example.net/.well-known/acme-challenge/xxxxxx」の個所があります。
XXXXXXは毎回ランダムな文字に変わります。

証明書の発行では、Let's Encryptからこのファイルをチェックしにくる仕組みになっているのですが、アクセスできなかったためにエラーになったことを意味します。

認証ファイルは生成できていますか?

ドキュメントルート直下に「 .well-known 」が自動生成されているはずですが、存在するでしょうか。
存在しない場合は、ドキュメントルートの指定でパスを間違っていないか確認してみます。

リダイレクト設定は正しいですか?

htaccessでリダイレクトしている場合、 .well-known 以下もリダイレクト範囲に入っていないでしょうか。

http でアクセスできますか?

80番ポート(http)でアクセスできるでしょうか?
常時SSLにしたいので「http は閉じちゃえっ!」と言いたいところですが、Let's Encryptの認証は http にやってきます。

ただし、http から https にリダイレクトすることはサポートされています。
WEBサーバの設定として、http のアクセスを問答無用で https にリダイレクトすることで対応できます。
こちらの詳細は公式のこちらをご覧ください。

海外アクセスを拒否していませんか?

証明書の発行には Let's Encrypt からのアクセスを許可する必要があります。

海外からのアクセスをIPアドレス判定で拒否していることもあると思いますが、 Let's Encrypt からのアクセスは許可しておかないと証明書が発行できません。

いくつかある解決策のうちの1つですが次のようにすることもできます。

vi ドキュメントルート/.well-known/.htaccess
order deny,allow
deny from all
allow from 66.133.109.36

.well-known 以下に限定して、IPアドレスを指定してアクセスを許可しています。

ただしこの方法には重大な決定があって、 Let's Encrypt から接続してくるIPアドレスはこの1つだけとは限らないことです。2020年1月現在で5つくらいのIPアドレスがあるようです。
そして、 Let's Encrypt の公式発表としてはIPアドレスを公開していないことになっています。

そもそも、海外アクセスを拒否する目的は .well-known とは無関係なので、 .well-known 以下のアクセスは特に制限することなく全公開で良いじゃないかと思います。

vi ドキュメントルート/.well-known/.htaccess
order allow,deny
allow from all

こうですね。
.well-known の下に .htaccess を置くのがポイントです。

nginxの場合は次のようになります。

server {
    # Basic認証をかけないIPアドレス
    satisfy any;
    # allow 0.0.0.1;
    # allow 0.0.0.2;
    deny all;

    # Basic認証用の設定を追加
    auth_basic "Restricted";
    auth_basic_user_file /home/example/www/.htpasswd;

    location /.well-known {
        allow all;
    }

    location / {
        set $redirect_url $uri;
        try_files $uri $uri/ /main.php?$args;
    }
}

nginxとPHP-FPMの設定 – PHP共存版
バーチャルホストの設定