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

アフィリエイト広告を利用しています

このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
挨拶や報告は無しで大丈夫です

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

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

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

環境

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

Snapdを使う

2022年3月末時点でのやり方は Snapd を使う方法です。
以前までのやり方は使えなくなっています。

初めてのインストール

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

EPELリポジトリ

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

確認

dnf --enablerepo=epel list snapd
メタデータの期限切れの最終確認: 0:00:02 時間前の 2022年03月30日 00時40分34秒 に。
利用可能なパッケージ
snapd.x86_64   2.54.4-1.el9   epel

インストール

# インストール
dnf --enablerepo=epel install snapd

# 有効化
systemctl enable --now snapd.service snapd.socket

# リンク
ln -s /var/lib/snapd/snap /snap

# コアをインストール
snap install core

# Warning が出たら、「セッションを再開」みたいに書かれてるのでターミナルを開き直す

# コアをリフレッシュ
snap refresh core

# certbot をインストール
snap install --classic certbot

1つ目のドメインを設定

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

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

# 新しいやり方はこちらなんだけど、エラーになるはず
certbot --apache
# なので古いやり方を使う
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 「ドメイン」

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o:

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

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, 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 ドメイン

トラブル対応

アクセス制限をかけている場合

Bacic認証やIPアドレス制限を書けている場合、Let'sEncryptの登録や更新が正しく行えません。

登録のときは一時的に解除するなりで対応したら良いのですが、90日ごとの更新ではそういうわけにもいきません。自動化したいところです。

ということで、confを次のようにします。

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot "/home/example_user/public_html"
    SuexecUserGroup example_user example_user
    RewriteEngine on
    RewriteRule ^.well-known/acme-challenge/ - [L]
    RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [L,R=302]
    ErrorLog    /home/example_user/logs/error_log
    TransferLog /home/example_user/logs/access_log
    LogLevel warn
</VirtualHost>

<VirtualHost *:443>
    ServerName example.com

    # Basic認証をかけつつIPアドレスで許可
    <Location / >
        AuthType Basic
        AuthName "Private"
        AuthUserFile /var/wwww/example.com/.htpassword
        <RequireAny>
            Require ip 127.0.0.1
            Require ip 127.0.0.2
            Require ip 127.0.0.3
            Require valid-user
        </RequireAny>
    </Location>

    # Let's Encrypt 物理的にファイルがなければBasic認証になる
    <Location "/.well-known/acme-challenge/">
        Require all granted
    </Location>

</VirtualHost>

/.well-known/acme-challenge/ へのアクセスはそのまま通します。
それ以外のアクセスは https:// にリダイレクトしています。

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

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

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の場合は下記をご覧ください。

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