IPv6 only環境でcertbotをpipインストールする (Let's EncryptでSSL証明書を自動更新する)
Let's EncryptでSSL証明書の更新を自動で行いたいため、Rocky Linux 8のIPv6 only環境でcertbotをpipインストールしたときのメモを残します。
TL;DR
公式のsnapからインストールする手順だとIPv6サーバがなく途中で躓いてしまったため、snap経由ではなくpipコマンドでcertbotをインストールしました。 一日一回の自動実行はcrontabではなくsystemctlでcertbot.timerを設定しました。 SELinuxのポリシー設定のあるサーバでnginxの設定に躓いたので回避策を書きました。
- Rocky Linux 8 (CentOS8互換ディストリビューション)
- snapのサーバがIPv6に対応していない
- 仕方なくpipでcertbotをインストールする
- certbotを試しに実行してみる
- systemctlでcertbot.timerを設定する
- SELinuxを無効化せずにnginxを動かす
- nginxのVirtualHost設定ファイル例
1. Rocky Linux 8 (CentOS8互換ディストリビューション)
今回はひとまず Rocky Linux 8 (CentOS8互換ディストリビューション) でcertbotを動かしてみます。何も考えずにsnapを使いたい場合はUbuntuを使うのが一番良いです。ただ、簡単インストールできてしまうことの裏返しとして、プログラムの動作構成をあまりよく知らずに使えてしまうので、あえて茨の道を渡ってみるのも良いと思います。ちなみにApline Linuxはディストリビューションのシステム構成の問題でそもそもsnapはインストールできません(対応できておらずサポートされていません)。今回試すsnapを利用しないpipからのインストール手順であれば、Apline Linuxでもcertbotを動かすことができると思います。
2. snapのサーバがIPv6対応していない
Rocky Linux 8の環境でcertbotをインストールするために、まず公式で推奨されているsnapをインストールしようとするのですが、、、
sudo dnf install -y epel-release sudo dnf install -y snapd sudo systemctl enable --now snapd.socket sudo ln -s /var/lib/snapd/snap /snap sudo snap install core
上記コマンドをIPv6 only環境で実行すると、最後のsnapコマンド実行時に下記のようなエラーが発生してしまいます。
$ sudo snap install core error: cannot install "core": persistent network error: Post "https://api.snapcraft.io/v2/snaps/refresh": dial tcp 185.125.188.58:443: connect: network is unreachable
これはsnapのパッケージ管理サーバ api.snapcraft.io
がAAAAレコードを持たず、サーバとIPv6通信ができないことが原因です。
この件は、2017年からBug #1710022 “Snap store APIs (api.snapcraft.io) are not reachab...” : Bugs : Snap Store Serverというタイトルでバグ報告がされていますが、2022年になってもまだ何も進展がなく、Snap Store ServerにIPv6対応のAAAAレコードが追加されるような気配がありません…。
3. 仕方なくpipでcertbotをインストールする
snapはあきらめて、certbotの最新版をPython3.9のpip経由でインストールすることにします。前準備としてpip3コマンドをインストールします。
sudo dnf -y install python39-pip
pip自体のバージョンを pip3 install --upgrade pip
コマンドを実行して最新版にバージョンアップします。
$ pip3 --version pip 20.2.4 from /usr/lib/python3.9/site-packages/pip (python 3.9) $ sudo pip3 install --upgrade pip WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead. Collecting pip Downloading pip-22.0.4-py3-none-any.whl (2.1 MB) Installing collected packages: pip WARNING: The scripts pip, pip3 and pip3.9 are installed in '/usr/local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. Successfully installed pip-22.0.4 $ pip3 --version pip 22.0.4 from /usr/local/lib/python3.9/site-packages/pip (python 3.9)
certbotの動作に必要な依存ライブラリを予めOSパッケージからインストールしておきます。
sudo dnf -y install python39-devel python39-cffi python39-requests python39-urllib3 sudo dnf -y install augeas-libs libffi-devel openssl-libs
certbotをpip経由でインストールします。nginxとapacheのプラグインを同時にインストールしています。
sudo pip3 install certbot certbot-nginx certbot-apache
/usr/local/binにcertbotがインストールされるため、/usr/binにシンボリックリンクを貼っておきます。
sudo ln -s /usr/local/bin/certbot /usr/bin/certbot
4. certbotを試しに実行してみる
インストールされたcertbotのバージョンを確認します。
$ certbot --version certbot 1.27.0
--dry-runオプションを指定して、試しにcertbot renewを実行してみます。
$ sudo certbot renew --dry-run Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No simulated renewals were attempted. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
何も前回の設定がなければ、certbotコマンドが無事動いているようなので、ひとまずこれでokです。おめでとうございます。
5. systemctlでcertbot.timerを設定する
certbot renewalを実行するサービスの定義certbot.serviceを作成します。ExecStartの行で実行するcertbot renewのコマンドを指定するのですが、--post-hookの引数をつけることで、Webサーバの設定を再起動してSSL証明書を再読み込みするようにしています。
cat<<EOF>/etc/systemd/system/certbot.service [Unit] Description=Let's Encrypt certbot renewal [Service] Type=oneshot ExecStart=/usr/bin/certbot renew --quiet --agree-tos --post-hook "systemctl reload nginx.service" EOF
ここではcrontabではなく、certbot.timerを定義して一日一回dailyで定時実行されるようにします。
cat<<EOF>/etc/systemd/system/certbot.timer [Unit] Description=Let's Encrypt certbot renewal (daily) [Timer] OnCalendar=0/12:00:00 RandomizedDelaySec=1h Persistent=true [Install] WantedBy=timers.target EOF
このファイルの中でRandomizedDelaySec=1hを指定することで実行時間を1時間の誤差をわざと設けて、全世界で同じ時刻にLet's Encryptのサーバにアクセスして過負荷になることを防いでおきます。このテクニックはArch Linuxのcertbot設定ファイルを参考にしました。
systemctlコマンドでcertbot.timerを起動して、再起動時でも有効になるように設定します。
sudo systemctl start certbot.timer sudo systemctl enable certbot.timer
ファイルの内容を書き換えた場合は、systemctl daemon-reloadコマンドを実行して設定ファイルを再読み込みします。 そして、systemctl list-timersコマンドを実行して、次回起動される.serviceコマンドの一覧を出力します。
sudo systemctl daemon-reload sudo systemctl list-timers
実行結果の中にcertbot.timer certbot.serviceの行があればokです。
$ sudo systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Sat 2022-05-07 08:09:58 UTC 32min left Sat 2022-05-07 07:06:44 UTC 30min ago dnf-makecache.timer dnf-makecache.service Sat 2022-05-07 11:13:44 UTC 3h 36min left Fri 2022-05-06 11:13:44 UTC 20h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Sat 2022-05-07 12:19:07 UTC 4h 41min left Sat 2022-05-07 07:37:05 UTC 11s ago certbot.timer certbot.service Sun 2022-05-08 00:00:00 UTC 16h left Sat 2022-05-07 00:00:44 UTC 7h ago unbound-anchor.timer unbound-anchor.service 4 timers listed. Pass --all to see loaded but inactive timers, too.
もしも、certbotが勝手に自動起動されると困る場合は、以下のコマンドを実行して、certbot.timerを停止して無効にします。
sudo systemctl stop certbot.timer sudo systemctl disable certbot.timer
確認のためにsystemctl list-timers --allを実行して、certbot.timerの行が n/a (not available) になっていることを確認します。
$ sudo systemctl list-timers --all NEXT LEFT LAST PASSED UNIT ACTIVATES Sat 2022-05-07 08:09:58 UTC 29min left Sat 2022-05-07 07:06:44 UTC 33min ago dnf-makecache.timer dnf-makecache.service Sat 2022-05-07 11:13:44 UTC 3h 33min left Fri 2022-05-06 11:13:44 UTC 20h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Sun 2022-05-08 00:00:00 UTC 16h left Sat 2022-05-07 00:00:44 UTC 7h ago unbound-anchor.timer unbound-anchor.service n/a n/a Sat 2022-05-07 07:37:05 UTC 3min 8s ago certbot.timer certbot.service 4 timers listed.
6. SELinuxを無効化せずにnginxを動かす
まだnginxをインストールしていない場合はOSパッケージからインストールします。
sudo dnf -y install nginx
nginxのサービスを起動して、再起動時にも自動起動を有効にします。
$ sudo systemctl start nginx $ sudo systemctl enable nginx Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
certbot certonly --nginxで証明書を取得します。(certonlyオプションを省略するとnginxの設定ファイルを書き換えてくれます)
$ sudo certbot certonly --nginx Saving debug log to /var/log/letsencrypt/letsencrypt.log Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): 自分のメールアドレスを入力する - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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: 規約を読んで同意するならyを入力する - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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: 特にメールで案内が欲しくなければnを入力する Account registered. Please enter the domain name(s) you would like on your certificate (comma and/or space separated) (Enter 'c' to cancel): 自分のサーバのFQDNを入力する(例:ssl.example.com) Requesting a certificate for ssl.example.com Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/ssl.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/ssl.example.com/privkey.pem This certificate expires on 2022-08-07. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If you like Certbot, please consider supporting our work by: * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate * Donating to EFF: https://eff.org/donate-le - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
もしも以下のようなエラーが出る場合は途中のacme-challengeに失敗してしまっています。nginxがうまく動いていないようです。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/ssl.example.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Simulating renewal of an existing certificate for ssl.example.com Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: ssl.example.com Type: unauthorized Detail: XXXX:XXXX:100:XXX:XX:cafe:4e:1: Invalid response from https://ssl.example.com/.well-known/acme-challenge/hlbtXXXXeDKRQI9f0lNFBSLY6v4PyRfwoFY8GwOHAKo: 404 Hint: The Certificate Authority failed to download the challenge files from the temporary standalone webserver started by Certbot on port 80. Ensure that the listed domains point to this machine and that it can accept inbound connections from the internet. Failed to renew certificate ssl.example.com with error: Some challenges have failed.
ファイルの権限設定が正しいのにnginxでエラーが発生してしまう場合(webのrootディレクトリも読み取れない場合)は、getenforceコマンドを実行してSELinuxの設定の有無を確認してみましょう。
$ getenforce Disabled
もしもDisabledだったら、SELinuxは無効になっているので、ファイルの権限設定を見直して、Webのルートディレクトリがnginxの動作権限で読み込みができるかどうか、上位のディレクトリも含めて再確認してください。
$ getenforce Enforcing
もしもEnforcingだったら、SELinuxが有効になっていて監査にひっかかっているので、適当な作業ディレクトリでSELinuxのポリシーでどこにひっかかっているかを確認します。
$ sudo grep nginx /var/log/audit/audit.log | audit2allow -m nginx module nginx 1.0; require { type var_t; type httpd_t; class file { getattr read }; } #============= httpd_t ============== allow httpd_t var_t:file { getattr read };
httpd_tコンテキスト(Apacheやnginxの動作権限)に対して、var_tファイルの読み取り権限がないようなので、許可するポリシーファイルを作成します。
$ sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx ******************** IMPORTANT *********************** To make this policy package active, execute: sudo semodule -i nginx.pp
少し時間がかかりますが、作成したポリシーファイルnginx.ppを適用します。
$ sudo semodule -i nginx.pp
もう一つ別の方法として、ポリシーを変更せずに、/var/www/html以下のファイルに対してrestoreconコマンドを実行してhttpd_sys_content_tのラベルを追加してあげるという方法もあります。ファイルのラベルを確認するにはls -Zオプションで、ファイルのSELinuxコンテキストを変更するには、chcon、semanage fcontext、restoreconコマンドが使えます。
$ cd /var/www/html $ ls -Z unconfined_u:object_r:var_t:s0 index.html $ sudo restorecon *.html $ ls -Z unconfined_u:object_r:httpd_sys_content_t:s0 index.html
これでもうまくいかない場合は、セキュリティは弱くなりますが、httpd_tコンテキストに対してpermissiveにして全許可します。
$ sudo semanage permissive -a httpd_t
それでもうまくいかない場合は、SELinuxをオフにする、というのが長らくの定説ですが、ちょっと悲しいですね。
$ setenforce 0 $ getenforce Permissive
一時的にSELinuxを無効化して、SELinuxが原因かどうかを確認して、サーバ内外のファイアウォールでTCP/80,TCP/443が外から通信できないとか、どうやらSELinuxは冤罪で別のことが原因でありそうであれば元通りに設定を戻しておいてあげます。
$ setenforce 1 $ getenforce Enforcing
以下のURLにSELinux上でnginxを動かす方法が載っていますので、SELinuxのポリシー設定は一度慣れておくと良いでしょう。
7. nginxのVirtualHost設定ファイル例
ちなみに、私が使っているnginxのVirtualHost設定ファイル例を以下に掲載します。IPv6 onlyの設定になっています。 シェル変数DOMAINに設定したいVirtualHostのFQDNを指定してVirtualHost毎に.confファイルを作成しています。
DOMAIN="ssl.example.com" cat<<EOF| sudo tee /etc/nginx/conf.d/$DOMAIN.conf map \$http_upgrade \$connection_upgrade { default upgrade; '' close; } server { ## listen 80; # IPv4 listen [::]:80; # IPv6 server_name $DOMAIN; return 301 https://\$host\$request_uri; } server { ## listen 443 ssl http2; # IPv4 listen [::]:443 ssl http2; # IPv6 server_name $DOMAIN; access_log /var/log/nginx/$DOMAIN.access.log; location / { root /var/www/$DOMAIN; index index.html; } ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } EOF
複数の.confファイルを作成することによって、SSL証明書はFQDN個別に生成して1台のnginxサーバで複数のVirtualHostを運用できます。
nginxの設定ファイルの文法チェックを-tコマンドで実施した後、systemctlで設定ファイルを再読み込みします。
sudo nginx -t sudo systemctl reload nginx
エラーが出なければokです。それでは、良いLet's Encryptライフをお過ごしください!
参考文献
- User Guide — Certbot 1.27.0 documentation
- Certbot - ArchWiki
- Get Certbot — Certbot 1.27.0 documentation
- Installing snap on CentOS | Snapcraft documentation
- あらためてEPELリポジトリの使い方をまとめてみた - Qiita
- Certbot を python3 で CentOS7 へインストール - AR ホームベーカリー
- nginxでホームディレクトリを公開ディレクトリに設定するふ - Qiita
- .devドメインと.appドメインがHTTPSを強制する仕組み - Qiita
- Let’s EncryptによるSSLサーバー証明書の取得、自動更新設定(2021年3月版) | 稲葉サーバーデザイン
- certbot/configurator.py at 2017669544b1d296f10339321f5f66b6b5f158bf · certbot/certbot · GitHub
- Modifying SELinux Settings for Full NGINX and NGINX Plus Functionality
- 【Ubuntu】Let’s Encrypt+nginxでSSL/TLS(https接続)を設定する方法 | VPS Life
- certbot(Letsecnrypt)コマンド・オプションを真面目に理解する - Qiita