RHEL9で作る自宅サーバ:dnsサーバの構築 その2 Let’s Encryptで証明書を取得!

dnsサーバRHEL9パソコン自宅サーバ

15年以上前から、格安サーバ証明書を使っていましたが、今は無料で手に入るんですね!しかも憧れのワイルドカード証明書さえも無料です!

Let’s Encryptでサーバ証明書を取得するには難易度があります。

  1. Webサーバ経由→簡単!
  2. クラウドやレンタルサーバのdns経由→ちょっと難しい
  3. 自宅サーバのdns経由→超絶難しい!(bindの新機能で簡単になりました!)

ワイルドカードのサーバ証明書はdns経由でないと取得できません。頑張って設定してみましょう!

サーバ証明書を取得するには、そのドメインの所有者であることを証明しなくてはなりません。

例えば、以前の格安サーバ証明書を取得するためには「admin@ドメイン名」のメールアドレスで、メールを受け取ることが条件でした。

Let’s Encryptでdnsサーバ経由でサーバ証明書を取得するためには「_acme-challenge.ドメイン名」のホストに、指示された内容のTXTレコードを書き込めることが条件になります。

また、証明書の有効期限は90日と短くなっています。そのつど手動で更新するのは大変なので、ツールを使って自動化します。

certbotのインストール

[root@ace ~]# dnf install certbot python3-certbot-dns-rfc2136
サブスクリプション管理リポジトリーを更新しています。
メタデータの期限切れの最終確認: 1:17:20 前の 2024年02月05日 18時32分53秒 に実施 しました。
依存関係が解決しました。
================================================================================
 パッケージ        Arch   バージョン     リポジトリー                     サイズ
================================================================================
インストール:
 certbot           noarch 2.6.0-1.el9    epel                              18 k
 python3-certbot-dns-rfc2136
                   noarch 2.6.0-1.el9    epel                              32 k
依存関係のインストール:
 fontawesome-fonts noarch 1:4.7.0-13.el9 rhel-9-for-x86_64-appstream-rpms 207 k
 fonts-filesystem  noarch 1:2.0.5-7.el9.1
                                         rhel-9-for-x86_64-baseos-rpms     11 k
 python3-acme      noarch 2.6.0-1.el9    epel                             160 k
 python3-certbot   noarch 2.6.0-1.el9    epel                             644 k
 python3-cffi      x86_64 1.14.5-5.el9   rhel-9-for-x86_64-appstream-rpms 257 k
 python3-configargparse
                   noarch 1.7-1.el9      epel                              45 k
 python3-configobj noarch 5.0.6-25.el9   rhel-9-for-x86_64-appstream-rpms  66 k
 python3-cryptography
                   x86_64 36.0.1-4.el9   rhel-9-for-x86_64-baseos-rpms    1.2 M
 python3-dns       noarch 2.3.0-2.el9    rhel-9-for-x86_64-baseos-rpms    469 k
 python3-josepy    noarch 1.13.0-1.el9   epel                              60 k
 python3-parsedatetime
                   noarch 2.6-5.el9      epel                              79 k
 python3-pyOpenSSL noarch 21.0.0-1.el9   epel                              90 k
 python3-pycparser noarch 2.20-6.el9     rhel-9-for-x86_64-appstream-rpms 139 k
 python3-pyrfc3339 noarch 1.1-11.el9     epel                              18 k
 python3-pytz      noarch 2021.1-5.el9   rhel-9-for-x86_64-appstream-rpms  55 k
弱い依存関係のインストール:
 python-josepy-doc noarch 1.13.0-1.el9   epel                              19 k

トランザクションの概要
================================================================================
インストール  18 パッケージ

ダウンロードサイズの合計: 3.5 M
インストール後のサイズ: 14 M
これでよろしいですか? [y/N]: y
:
略
:
インストール済み:
  certbot-2.6.0-1.el9.noarch
  fontawesome-fonts-1:4.7.0-13.el9.noarch
  fonts-filesystem-1:2.0.5-7.el9.1.noarch
  python-josepy-doc-1.13.0-1.el9.noarch
  python3-acme-2.6.0-1.el9.noarch
  python3-certbot-2.6.0-1.el9.noarch
  python3-certbot-dns-rfc2136-2.6.0-1.el9.noarch
  python3-cffi-1.14.5-5.el9.x86_64
  python3-configargparse-1.7-1.el9.noarch
  python3-configobj-5.0.6-25.el9.noarch
  python3-cryptography-36.0.1-4.el9.x86_64
  python3-dns-2.3.0-2.el9.noarch
  python3-josepy-1.13.0-1.el9.noarch
  python3-parsedatetime-2.6-5.el9.noarch
  python3-pyOpenSSL-21.0.0-1.el9.noarch
  python3-pycparser-2.20-6.el9.noarch
  python3-pyrfc3339-1.1-11.el9.noarch
  python3-pytz-2021.1-5.el9.noarch

完了しました!
[root@ace ~]#

certbotはLet’s Encryptのサーバ証明書を発行するときに使うツールです。

certbot_dns_rfc2136は、「_acme-challenge.ドメイン名」に対して、TSIGキーを使ってTXTレコードを動的更新(DynamicDNS)してくれるプラグインになります。説明書はこちら

DDNSゾーンの追加

DDNSの設定をする前にcertbot_dns_rfc2136のドキュメントにも書いてありますが、TSIGキーを作っておきましょう。

[root@ace ~]# ddns-confgen -k ddns-key
# To activate this key, place the following in named.conf, and
# in a separate keyfile on the system or systems from which nsupdate
# will be run:
key "ddns-key" {
        algorithm hmac-sha256;
        secret "***非公開情報**";
};

# Then, in the "zone" statement for each zone you wish to dynamically
# update, place an "update-policy" statement granting update permission
# to this key.  For example, the following statement grants this key
# permission to update any name within the zone:
update-policy {
        grant ddns-key zonesub ANY;
};

# After the keyfile has been placed, the following command will
# execute nsupdate using this key:
nsupdate -k <keyfile>
[root@ace keys]#

TSIGキーの生成は3通りあって、公式サイトの例では「dnssec-keygen」を使っていますが、「ddns-confgen」コマンドの方がわかりやすいと思います。

で、以下のようにkeyファイルを作っておきます。

[root@ace ~]# mkdir /var/named/keys
[root@ace ~]# vi /var/named/keys/ddns-key
[root@ace ~]# cat /var/named/keys/ddns-key
key "ddns-key" {
        algorithm hmac-sha256;
        secret "***非公開情報***=";
};

[root@ace ~]#
[root@ace ~]# chown -R named:named /var/named/keys
[root@ace ~]# chmod 500 /var/named/keys
[root@ace ~]# chmod 400 /var/named/keys/ddns-key
[root@ace ~]# ls -al /var/named/keys
合計 4
dr-x------ 2 named named  22  2月  5 22:15 .
drwxrwx--T 9 root  named 183  2月  5 21:37 ..
-r-------- 1 named named 141  2月  5 22:13 ddns-key
[root@ace ~]#

「/var/named/keys/ddns-key」には秘密情報が入っているので、ファイルのパーティションをnamed以外読めないようにしておきます。

この、TSIGキーを組み込んで、/etc/named.confをDDNS対応に変更します。

[root@ace ~]# vi /etc/named.conf
[root@ace ~]# cat /etc/named.conf
//
// named.conf
//
:
省略
:
options {
:
省略
:
};

logging {
:
省略
:
};

include "/etc/named.root.key";

include "/var/named/keys/ddns-key";

view "internal" {
:
省略
:
        zone "_acme-challenge.zeke.ne.jp" {
                type master;
                file "ddns/_acme-challenge.zeke.ne.jp.zone";
                check-names ignore;
                allow-query { any; };
                update-policy {
                        grant ddns-key name _acme-challenge.zeke.ne.jp. txt;
                };
        };
:
省略
:
};

view "external" {
:
省略
:
        zone "_acme-challenge.zeke.ne.jp" {
                in-view internal;
        };
:
省略
:
};

[root@ace ~]#

また、以下のゾーンファイルも作っておきます。

[root@ace ~]# mkdir /var/named/ddns
[root@ace ~]# vi /var/named/ddns/_acme-challenge.zeke.ne.jp.zone
[root@ace ~]# cat /var/named/ddns/_acme-challenge.zeke.ne.jp.zone
$ORIGIN .
$TTL 60 ; 1 minute
_acme-challenge.zeke.ne.jp IN SOA ns.zeke.ne.jp. root.ns.zeke.ne.jp. (
                                1          ; serial
                                3600       ; refresh (1 hour)
                                600        ; retry (10 minutes)
                                1209600    ; expire (2 weeks)
                                60         ; minimum (1 minute)
                                )
                IN      NS      ns.zeke.ne.jp.

[root@ace ~]# chown -R named:named /var/named/ddns
[root@ace ~]# chmod 770 /var/named/ddns/
[root@ace ~]# chmod 660 /var/named/ddns/_acme-challenge.zeke.ne.jp.zone
[root@ace ~]# ls -alR /var/named/ddns/
[root@ace ~]# ls -alR /var/named/ddns/
/var/named/ddns/:
合計 4
drwxrwx--- 2 named named  45  2月  5 20:25 .
drwxrwx--T 9 root  named 183  2月  5 20:24 ..
-rw-rw---- 1 named named 225  2月  5 20:25 _acme-challenge.zeke.ne.jp.zone
[root@ace ~]#

「include “/var/named/keys/ddns-key”;」でTSIGキーを追加、「zone “_acme-challenge.zeke.ne.jp” 」ステートメントでDDNSできるゾーンを追加します。

zoneステートメントの中の「update-policy」がDDNSの指定で、 ddns-key の名前のTSIGキーで、_acme-challenge.zeke.ne.jp.名前のTXTレコードだけ変更できるようにしています。

「/var/named/ddns/_acme-challenge.zeke.ne.jp.zone」のゾーンファイルは、自動的に書き込むので、namedユーザで書き込みできるディレクトリ、ファイルにしておきます。

[root@ace ~]# rndc reload
server reload successful
[root@ace ~]# systemctl status named
● named.service - Berkeley Internet Name Domain (DNS)
     Loaded: loaded (/usr/lib/systemd/system/named.service; enabled; preset: di>
     Active: active (running) since Mon 2024-02-05 20:41:25 JST; 50s ago
    Process: 4696 ExecStartPre=/bin/bash -c if [ ! "$DISABLE_ZONE_CHECKING" == >
    Process: 4698 ExecStart=/usr/sbin/named -u named -c ${NAMEDCONF} $OPTIONS (>
   Main PID: 4699 (named)
      Tasks: 8 (limit: 23114)
     Memory: 45.8M
        CPU: 133ms
     CGroup: /system.slice/named.service
             mq4699 /usr/sbin/named -u named -c /etc/named.conf

 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: automatic empty zone: view interna>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: automatic empty zone: view interna>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: automatic empty zone: view interna>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: automatic empty zone: view interna>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: set up managed keys zone for view >
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: configuring command channel from '>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: command channel listening on 127.0>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: configuring command channel from '>
 2月 05 20:41:25 ace.zeke.ne.jp named[4699]: command channel listening on ::1#9>
 2月 05 20:41:25 ace.zeke.ne.jp systemd[1]: Started Berkeley Internet Name Doma>
[root@ace ~]#

設定ファイルを読み直して、動作確認してみます。

[root@ace keys]# nsupdate -d -k /var/named/keys/ddns-key
Creating key...
Creating key...
namefromtext
keycreate
> server 127.0.0.1
> update add _acme-challenge.zeke.ne.jp. 3600 in txt test
> send
Reply from SOA query:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:   2513
;; flags: qr aa ra; QUESTION: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; QUESTION SECTION:
;_acme-challenge.zeke.ne.jp.    IN      SOA

;; ANSWER SECTION:
_acme-challenge.zeke.ne.jp. 60  IN      SOA     ns.zeke.ne.jp. root.ns.zeke.ne.jp. 9 3600 600 1209600 60

;; AUTHORITY SECTION:
_acme-challenge.zeke.ne.jp. 60  IN      NS      ns.zeke.ne.jp.

;; TSIG PSEUDOSECTION:
ddns-key.               0       ANY     TSIG    hmac-sha256. 1707187162 300 32 tqdp0KwiY2bUNXauLNNvIDSA0iW7ZARSZfiiAtvnvp4= 2513 NOERROR 0

Found zone name: _acme-challenge.zeke.ne.jp
The master is: ns.zeke.ne.jp
Sending update to 127.0.0.1#53
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  50419
;; flags:; ZONE: 1, PREREQ: 0, UPDATE: 1, ADDITIONAL: 1
;; UPDATE SECTION:
_acme-challenge.zeke.ne.jp. 3600 IN     TXT     "test"

;; TSIG PSEUDOSECTION:
ddns-key.               0       ANY     TSIG    hmac-sha256. 1707187162 300 32 bWCHZZ2fXw8L8y62GYeWQI+/YGfdkJxEIYhZt1zEOeY= 50419 NOERROR 0


Reply from update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  50419
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;_acme-challenge.zeke.ne.jp.    IN      SOA

;; TSIG PSEUDOSECTION:
ddns-key.               0       ANY     TSIG    hmac-sha256. 1707187162 300 32 fQJs29efTaagXcNhGCiYG5YlbZGzRhKLtLgZ6+um8RA= 50419 NOERROR 0

> quit
[root@ace keys]#
[root@ace keys]#
[root@ace keys]# dig @127.0.0.1 TXT _acme-challenge.zeke.ne.jp

; <<>> DiG 9.16.23-RH <<>> @127.0.0.1 TXT _acme-challenge.zeke.ne.jp
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62304
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 69a8128ff2c5b64e0100000065c19bf218b788607dd24e62 (good)
;; QUESTION SECTION:
;_acme-challenge.zeke.ne.jp.    IN      TXT

;; ANSWER SECTION:
_acme-challenge.zeke.ne.jp. 3600 IN     TXT     "test"

;; Query time: 2 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 06 11:39:46 JST 2024
;; MSG SIZE  rcvd: 100

[root@ace keys]#

nsupdateコマンドをデバッグモード&TSIGキーを引数にして立ち上げ、「_acme-challenge.zeke.ne.jp」ホストのTXTレコードに「test」を書き込みます。

「status: NOERROR」になっていれば問題なしです。

digコマンドで、「_acme-challenge.zeke.ne.jp」ホストのTXTレコードを確認すると「test」が入っていますね。

うまく行ったように見えますが、「_acme-challenge.zeke.ne.jp」は内部向けのviewの中なので、インターネット側からは参照できません。これではLet’s Encryptから確認できないので、証明書を発行してもらえません。

先にあげた/etc/named.confに以下のように記述しました。

view "external" {
:
省略
:
        zone "_acme-challenge.zeke.ne.jp" {
                in-view internal;
        };
:
省略
:
};

これは、「外部から_acme-challenge.zeke.ne.jpを参照されたときはinternal viewの同じゾーンを返答して!」という意味です。また、外部から参照できるように、internal view側にはallow-query { any; };を入れる必要があります。

IPv6のアドレス(2001:2c0:cd03:ca00::ace)を外部から参照できるようにして、スマホのキャリア経由で動作を確認しました!

ちゃんと「_acme-challenge.zeke.ne.jp」の内容が表示されていますね。

certbotの設定

長い仕込が終わりました。ようやくcertbotの設定です。

[root@ace ~]# vi /etc/letsencrypt/rfc2136.ini
[root@ace ~]# chmod 640 /etc/letsencrypt/rfc2136.ini
[root@ace ~]# cat /etc/letsencrypt/rfc2136.ini
# Target DNS server (IPv4 or IPv6 address, not a hostname)
dns_rfc2136_server = 127.0.0.1
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name
dns_rfc2136_name = ddns-key
# TSIG key secret
dns_rfc2136_secret = ***非公開情報***
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA256
# TSIG sign SOA query (optional, default: false)
dns_rfc2136_sign_query = false

[root@ace ~]# ls -al /etc/letsencrypt/
合計 20
drwxr-xr-x  2 root root   40  2月  5 23:02 .
drwxr-xr-x 82 root root 8192  2月  5 22:15 ..
-rw-r--r--  1 root root  152  5月 23  2023 cli.ini
-rw-r-----  1 root root  396  2月  6 11:46 rfc2136.ini
[root@ace ~]#

設定ファイルは、/var/named/keys/ddns-keyの内容を参考に記述します。ただし、dns_rfc2136_algorithmは大文字でないと認識してくれないようで、小文字で書いちゃうとエラーが出ます。設定ファイルは秘密情報が入っているので、root以外読めないようにしておきます。

[root@ace ~]# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini -d *.zeke.ne.jp -d zeke.ne.jp -m zeke@mail.zeke.ne.jp  --agree-tos --test-cert
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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: y
Account registered.
Requesting a certificate for *.zeke.ne.jp and zeke.ne.jp
Waiting 60 seconds for DNS changes to propagate

Certbot failed to authenticate some domains (authenticator: dns-rfc2136). The Certificate Authority reported these problems:
  Domain: zeke.ne.jp
  Type:   unauthorized
  Detail: No TXT record found at _acme-challenge.zeke.ne.jp

  Domain: zeke.ne.jp
  Type:   unauthorized
  Detail: No TXT record found at _acme-challenge.zeke.ne.jp

Hint: The Certificate Authority failed to verify the DNS TXT records created by --dns-rfc2136. Ensure the above domains are hosted by this DNS provider, or try increasing --dns-rfc2136-propagation-seconds (currently 60 seconds).

Some challenges have failed.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
[root@ace ~]#

試しに、certbotコマンドを実行してみます。ここでは*.zeke.ne.jpのワイルドカードと、zeke.ne.jpのサーバ証明書を要求しています。また、テストのため–test-certオプションを付けています。

一番最初に実行すると、メールアドレスをメーリングリストに登録するか?みたいなメッセージが出てきます。

まだ新しいdnsサーバは公開していないので、 _acme-challenge.zeke.ne.jpのTXTレコードを書き換えてから60秒待ってから諦めて、承認がおりていませんね。

スマホのキャリア経由でTXTレコードが書き換わっていることを確認できたので、ここまでは問題ないと思います。あとはdnsサーバを公開したあとに確認ですね。

また、公開後は以下のシェルをcronに実行させてサーバ証明書を更新します。

[root@myhome ~]# cat /etc/cron.daily/letsencrypt
#!/bin/sh

/usr/bin/certbot renew -q --deploy-hook "systemctl restart httpd ; systemctl restart postfix ; systemctl restart dovecot ; systemctl restart vsftpd"

[root@myhome ~]#

certbot renewコマンドでサーバ証明書を更新したら、–deploy-hookオプションで、各サーバを再起動しています。(–deploy-hookオプションも一度設定したら、省略してもいいのかな?)

また、以下のCAAレコード・ジェネレーターを使って、ゾーンファイルにCAAレコードも入れておきます。

zeke.ne.jp.	IN	CAA	0 issue "letsencrypt.org"
zeke.ne.jp.	IN	CAA	0 iodef "mailto:zeke@mail.zeke.ne.jp"

今回はちょっと大変でした。問題なく動いている現行をそのままコピーすればいいのですが、おかしなところはないか調べ直したり、もっと簡単にできる方法を考えていたりとかしていたので。

証明書を操作するコマンドのおぼえ書き

サブドメインをまとめて証明書に入れたいときは-d オプションを複数つける。一番最初のオプションにつけたドメイン名が共通名(CN)になる。

certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini -d zeke.ne.jp -d *.zeke.ne.jp -d *.info.zeke.ne.jp -d *.mail.zeke.ne.jp -m zeke@mail.zeke.ne.jp  --agree-tos

作成した証明書の中身を確認するときのコマンド

openssl x509 -in /etc/letsencrypt/live/zeke.ne.jp/cert.pem -text

証明書に含めたサブドメインを変更したくて作り直したいときは、以下のコマンドで一度削除してから作成する。そうしないと /etc/letsencrypt/live/zeke.ne.jp-001 のように連番を振られてしまう。(Let’s Encrypt側で覚えているようで、ローカルのファイルを削除してもダメみたい)

certbot delete --cert-name zeke.ne.jp

お勧めのKindle本です!

コメント