OpenWrt で Let's Encrypt (with acme.sh)

このブログ記事を最初にまとめた当時(2020-11-11)には気付かなかったが、現在(2023-12-06)ではいつの間にか、OpenWrt のパッケージモジュールとして acme が用意されており、さらには LuCI から使うための luci-app-acme までも存在している。以前の僕はこれらの OpenWrt パッケージについて知らなかったので、acme.sh の公式サイトの説明に従って一般的な Linux マシンとしてインストールし、コマンドを実行して使用してと、愚直に自力の作業をしていたものである。今では OpenWrt の acme.sh についての公式の説明も存在するので、それに従って作業すれば、ほとんど苦もなく完了できる。

だが、acme.sh の実行による Let's Encrypt からの証明書の発行が半自動化されたとはいえ、DNS や NGINX などと連携させる必要があり、総合的な話としては一定の知識を要するので、ここにまとめておきたいと思う。


Web ルートの準備

acme.sh では証明書の発行時の身元確認の方式として、Web ルートに .well-known/acme-challenge という一時的な隠しフォルダを作成し、そこにユニークな文字列からなるファイル名を持ったファイルを設置して、インターネット側から http://(ドメイン名)/.well-known/acme-challenge/(ファイル名)にアクセスできるかどうかで、確認するという形になっている。

このため、いきなり、acme を使うのではなくて、先に DNS 側でドメインが OpenWrt ルーターの IP を指し示すように設定しておき、さらに OpenWrt ルーター側では、NGINX の設定で、そのドメイン名での http アクセスが /www 以下に対応付けられるようにしておかなければならない。

DNS の設定

A レコードでデフォルトドメインがルーターの IP アドレスを指し示すように設定している他、CNAME で www がデフォルトドメインの別名であるようにしている。CAA レコードは今回の趣旨とは全く関係がないオマケで、無関係の他者が勝手にこのドメイン名を使った証明書を作成することを防ぐためのものである(参考)。

ローカルディレクトリーの用意

ローカルの /www/.well-known/acme-challenge を Web 側からアクセスする acme のチャレンジ用に使うことにするが、acme プログラムが直接操作に使うのは /var/run(メモリー上に設置された一時ファイル)なので、次のようにしてシンボリックリンクを作成しておく:


mkdir /www/.well-known/
ln -s /var/run/acme/challenge/ /www/.well-known/acme-challenge

NGINX の設定

/etc/nginx/init.d/(ドメイン名).conf:


server {
    listen 80;
    server_name (ドメイン名) www.(ドメイン名);

	location /.well-known/acme-challenge/ {
		root /www;
	}
}

そのドメイン名に関して、acme が一時的に作成する .well-known/acme-challenge が HTTP(ポート 80)で Web からアクセスできるようにしておくことが必要である。ドメインのルート設定(root)は必ずしも /www にしておく必要はないが、この設定では、ローカルの /www/.well-known/acme-challenge/ 以下が acme のチャレンジ用に使われることになる。

「そのドメイン名に関して」というのは server_name のことである。他のドメイン名用の設定でポート 80 を利用するかどうかは関係ない。

以上のようにして、たとえば、test.txt をチャレンジ用ディレクトリーに置いてみて、http://(ドメイン名)/.well-known/acme-challenge/test.txt に Web ブラウザーからアクセスできるかを確認して、問題ないのであれば、次に acme を使った作業に進んで問題ないだろう。


acme を使う


opkg update
opkg install luci-app-acme

もちろん、LuCI から luci-app-acme をインストールしても構わない。LuCI から一旦ログアウトして、ログインし直すと、Services > ACME certs という新しいメニューが増えているのがわかる。

やることはまず、この Acme certificates の Account email(更新通知などが来る管理用のアドレス。ドメイン名と関係なくても構わない)を適当に設定し、Save。

そのようにした上で、新しいドメイン設定項目を追加(Add)するか、既存のドメイン設定を選ぶ(Edit)かして、そのドメインの子ウィンドウを開く:

General Settings タブの Enabled を有効(✅)にし、Domain names をデフォルトドメインと、www 付きのサブドメインの両方をリストアップしておく。さらに Challenge Validation タブの Challenge method で「Webroot」を選んだ上にルートディレクトリーを指定して Save し、元の画面に戻ってから Save & Apply すればよい。設定が保存(Save)されるだけでなく、Apply により GUI の背後で acme.sh が実行され、ドメインの認証と証明書の発行が行われる。

処理の様子と結果はシステムログで確認できる:


Sat Dec  9 23:40:20 2023 daemon.info acme-acmesh: Running ACME for test.com
Sat Dec  9 23:40:20 2023 daemon.info acme-acmesh: /usr/lib/acme/client/acme.sh -d test.com -d www.test.com --keylength 2048 --accountemail tester@test.com --server letsencrypt --webroot /www --issue --home /etc/acme

(何らかのエラーが発生した場合には、上で acme-acmesh が実行している 2 行目のコマンド内容に --debug オプションをさらに追加して手動で実行してみるとよい。)

/etc/acme/(ドメイン名) 以下に fullchain.cer と、(ドメイン名).key が置かれており、これを NGINX で使えばいい。

NGINX の該当ドメイン用の設定に反映する

/etc/nginx/init.d/(ドメイン名).conf:


server {
    listen 80;
    server_name (ドメイン名) www.(ドメイン名);

	location /.well-known/acme-challenge/ {
		root /www;
	}

	location / { # デフォルトでは https へのリダイレクト
		return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name (ドメイン名) www.(ドメイン名);

    ssl_certificate     /etc/acme/(ドメイン名)/fullchain.cer;
    ssl_certificate_key /etc/acme/(ドメイン名)/(ドメイン名).key;

    root /www/(ドメイン名);
    index index.html;
}

上の NGINX の設定では、http:// を https:// にリダイレクトする設定にしている。下の server ブロックが https:// 用の設定であり、今回 acme によって半自動作成された fullchain.cer と(ドメイン名).key を使っている(fullchain ではない通常の(ドメイン名).cer も作成されるが、Apache 用に作成されるもので、NGINX には不要である)。

証明書の発行と NGINX の設定変更が無事に終ったならば、NGINX をリスタート(単なるリロードでは証明書まで再読み込みされないので注意)する:


service nginx restart

(NGINX の設定に問題がなければ、リスタートに成功する。問題がある場合、エラーになって、新しい設定の反映が拒否される)

CRON

acme は、自動更新のため、crontab(CRON ジョブ用の設定ファイル; /etc/crontabs/root)にも追記する。crontabは、OpenWrt では LuCI の System > Scheduled Tasks でも閲覧・編集が可能である:


0 0 * * * /etc/init.d/acme start

このような記述が追記されている。つまり、毎日 00:00 に 1 回、acme をサービスとして走らせているようだ。その時に、更新期限が迫っているドメインに関しては、更新するものと思われる。


設定ファイル

以上の作業の結果は、/etc/config/acme や、/etc/acme と、/etc/nginx/init.d/(ドメイン名).conf の設定内容、crontab に存在することになる。

/etc/config/acme
acme が使う設定ファイル。LuCI からも閲覧・操作ができる。
/etc/acme
acme の管理下にある各ドメインの設定や、証明書などのファイルが置かれているディレクトリー
/etc/nginx/init.d/(ドメイン名).conf
NGINX の各ドメインに関する設定ファイルで、証明書や暗号鍵を記載する
crontab (/etc/crontabs/root)
CRON が acme を毎日 1 回サービスとして走らせ、その結果 acme は更新が必要な場合には更新しようとする

備考:Used for nginx / uhttpd オプションについて

基本、これらのオプションは使わない。本来、セキュリティ上の観点からも LAN 向けで WAN(Web)向けには非公開で使われる前提の LuCI 用の証明書には OpenWrt の自己署名証明書が使われるが、ドメインの証明書を LuCI 用にも流用したい場合のオプションである。

備考:Standalone モードについて

OpenWrt の acme.sh モジュールでは Standalone モードでの acme チャレンジは上手く動かすことができなかった。公式のドキュメントでも、基本的には Webroot モードで解説されているので、無理に Standalone モードで行う必要はないと思う。

コメント

このブログの人気の投稿

シークエンスパパともの先見の明

清水俊史『上座部仏教における聖典論の研究』

シークエンスパパとも 本物の霊能力