--- title: FreeBSD 12 で自宅サーバ作成 Let's Encrypt編 tags: FreeBSD author: Chun3 slide: false --- ## はじめに FreeBSD 12を使用した自宅サーバの構築を行った。FreeBSD 12はリリースされて間もない状態ですが、カーネルに標準でVIMAGEが取り込まれDNSの外向きと、内向きを別々のシステム(jail+VIMAGE)が同一ホストで構築できることから採用することにしました。その時の構築手順を備忘録の意味も含め、具体的にいくつかに渡り説明する。 * [基本システム編その1](https://qiita.com/Chun3/items/2f891e5993c91b58ae5c) - 背景 - 基本のインストール - 日本語環境の設定 * [基本システム編その2](https://qiita.com/Chun3/items/1e8fe5071c4a78d37e66) - セキュリティ - ftpサーバ * [基本システム編その3](https://qiita.com/Chun3/items/e5cc6a69542153649dc0) - jail - 基本システム編おわり * [jail内基本システム編](https://qiita.com/Chun3/items/4b11b9acdd080ecdab6c) - 基本の設定(jail1・jail2共通) * [DNS編](https://qiita.com/Chun3/items/95ad3113202d111ee53c) - DNSの構築におけるこれまでの問題 - bind9インストール - NTPサーバ * [Let's Encrypt編](https://qiita.com/Chun3/items/4814c25c09756690133f)(この記事) - Let's Encryptについて - certbotのインストール - 各種設定 - 証明書の取得 * [Webサーバ編](https://qiita.com/Chun3/items/286e567952cd9faf279f) - インストール(apache2.4・webalizer・php7.3・postgresql11.2・mysql8.0) - 設定 * [メールサーバ編](https://qiita.com/Chun3/items/dc0e176c9de4f6b4bfb8) - インストール(cyrus-sasl・postfix・dovecot・procmail・pflogsumm・policyd-spf) - 設定・起動 - spfレコード送信設定 * [ファイルサーバ編](https://qiita.com/Chun3/items/356b7fadd7aa5280ec65) - sambaの選択 - samba3のインストール * [メールサーバ編その2](https://qiita.com/Chun3/items/4c15ee889b052df67bbd) - 概要 - OpenDKIM - OpenDMARC - ClamAV (clamav-milter) - Spamassasin (spamass-milter) ## Let's Encryptについて これまで、証明書の取得は個人で実施するには金銭的・手続き的にハードルが高くできないものと決めつけていました。しかし、無償で手続きもネットで簡単に行えそうであることを最近知り、情報を集め実行しました。手続き的にはdnsやhttpなどいくつかの方法がありましたが、以下のサイトを参照しdnsを使って証明書を取得することができました。 - [CertbotとBINDの組み合わせでLet's Encryptのワイルドカード証明書を取得・更新する](https://qiita.com/yasuhirokimura/items/3a95e169f806b3772e06) - [Let's Encrypt 総合ポータル (非公式解説サイト)](https://free-ssl.jp/) dnsをviewにより外向きと内向きを同一システムで運用していたことで問題があり今回の自宅サーバ再構築のきっかけとなりました。 なお、上記サイトは分かりやすく丁寧に説明されていますので詳細はそちらを参照していただき、以下は具体的にやった手順をまとめる。 また、外部dnsと連携して認証するためjail1(外部用)にインストールし作業を行う。 加えて、この作業の性質上1からやり直し確認しづらく、これまでにやったことをまとめたものである。jailの中からはまだ実施していないため問題が出る可能性があり、最終的にシステム移行時に確認予定。 **結果報告(2019/9/23)** 最初に自動更新が確認されたときに追記する予定が、随分とサボってしまった。現在自動更新を無事に4回実施できています。証明書ファイルの添字も”5”になりファイルの日付を見ると約2ヶ月毎に更新されているのが確認できた。また、運用始めてから今までに問題も発生していないことを追加報告します。 **問題発生(2019/10/19)** pkgをアップデート後自動更新が行われていないことに気づき、土曜日にcronで更新プログラムが起動するので、その結果を確認した。 すると以下のメッセージが ``` /usr/local/sbin/renew-letsencript: certbot: not found ``` ソフトがなくなっている? 確認すると ``` /usr/local/bin# ls -l cert* -rwxr-xr-x 1 root wheel 393 Sep 28 10:23 certbot-2.7* -rwxr-xr-x 1 root wheel 326216 Oct 3 10:19 certtool* ``` プログラム名にpythonのバージョンが入ったようだ(python3用も出来たのか?) とりあえずリンクを張って更新できるようになった。 ``` /usr/local/bin# ln -s certbot-2.7 certbot ``` また、有効期限まで19日であったことから先週には、最初の自動更新失敗のメールが来ているはずであるが、気づかずに廃棄したようだ(よく確認しないと)。しかし、本日Let's Encrypt Expiry Bot からLet's Encrypt certificate expiration notice for domain "*.example.jp"のメールを受け取りありがたく思っている。 ``` Hello, Your certificate (or certificates) for the names listed below will expire in 19 days (on 07 Nov 19 15:11 +0000). Please make sure to renew your certificate before then, or visitors to your website will encounter errors. (以下省略) ``` ## certbotのインストール certbotのオプションを選択することにより認証方式を選ぶことができる。今回はdnsを使用するためcertbot-dns-rfc2136をインストールする。プログラム自身はpythonで書かれているため以下のようにたくさんのモジュールがインストールされる。 ``` jail1 /root # pkg install security/py-certbot-dns-rfc2136 Updating FreeBSD repository catalogue... FreeBSD repository is up to date. All repositories are up to date. The following 63 package(s) will be affected (of 0 checked): New packages to be INSTALLED: py27-certbot-dns-rfc2136: 0.31.0 py36-certbot-dns-rfc2136: 0.31.0 py27-certbot: 0.31.0,1 py27-openssl: 18.0.0 py27-cryptography: 2.3 py27-ipaddress: 1.0.22 py27-idna: 2.7 py27-six: 1.12.0 py27-enum34: 1.1.6 py27-cffi: 1.11.5 py27-pycparser: 2.18 py27-asn1crypto: 0.22.0 py27-josepy: 1.1.0 py27-acme: 0.31.0,1 py27-requests-toolbelt: 0.8.0 py27-requests: 2.21.0 py27-chardet: 3.0.4 py27-certifi: 2018.11.29 py27-urllib3: 1.22,1 py27-pysocks: 1.6.8 py27-pytz: 2018.9,1 py27-pyrfc3339: 1.1 py27-zope.interface: 4.6.0 py27-zope.component: 4.2.2 py27-zope.event: 4.1.0 py27-parsedatetime: 2.4_1 py27-configobj: 5.0.6_1 py27-configargparse: 0.14.0 py27-dnspython: 1.15.0 py27-mock: 2.0.0 py27-pbr: 3.1.1 py27-pip: 9.0.3 py27-funcsigs: 1.0.2 py36-certbot: 0.31.0,1 py36-openssl: 18.0.0 py36-cryptography: 2.3 python36: 3.6.8 py36-idna: 2.7 py36-setuptools: 40.8.0 py36-six: 1.12.0 py36-cffi: 1.11.5 py36-pycparser: 2.18 py36-asn1crypto: 0.22.0 py36-josepy: 1.1.0 py36-acme: 0.31.0,1 py36-requests-toolbelt: 0.8.0 py36-requests: 2.21.0 py36-chardet: 3.0.4 py36-certifi: 2018.11.29 py36-urllib3: 1.22,1 py36-pysocks: 1.6.8 py36-pytz: 2018.9,1 py36-pyrfc3339: 1.1 py36-zope.interface: 4.6.0 py36-zope.component: 4.2.2 py36-zope.event: 4.1.0 py36-parsedatetime: 2.4_1 py36-configobj: 5.0.6_1 py36-configargparse: 0.14.0 py36-dnspython: 1.15.0 py36-mock: 2.0.0 py36-pbr: 3.1.1 py36-pip: 9.0.3 Number of packages to be installed: 63 The process will require 207 MiB more space. 42 MiB to be downloaded. (以下省略) ``` ## 各種設定 ### dns更新用パスワード作成 証明書を取得する際、要求元が確かに要求されたドメインの管理者であることを証明するためにISRG(証明書発行元)から送られてきた認証コードをdnsのTXTレコードとしてISRGに応答することで証明書が発行される(詳細は上記サイトを参照)。 このことから、ダイナミックにTXTレコードを書き換える必要があり、certbotからdnsをアクセスするためのパスワードを作成する。 ``` jail1 /root # tsig-keygen certbot-key > /usr/local/etc/namedb/certbot-key.key ``` ```/usr/local/etc/namedb/certbot-key.key key "certbot-key" { algorithm hmac-sha256; secret "/9a5f/hobiMY9BuycJOehKDNB1QzDMPliFbnSBecZZM="; }; ``` ### bindの設定 前節の設定に追加し以下のようにする。 ```/usr/local/etc/namedb/named.conf include "/usr/local/etc/namedb/certbot-key.key"; // 認証鍵の読み込み include "/usr/local/etc/namedb/rndc_key"; controls { inet 127.0.0.1 port 953 allow {127.0.0.1;} keys {"rndc-key";}; }; options { directory "/usr/local/etc/namedb/working"; pid-file "/var/run/named/pid"; dump-file "/var/dump/named_dump.db"; statistics-file "/var/stats/named.stats"; listen-on-v6 { none; }; allow-transfer { 192.168.1.0/24; }; }; // wan側の設定 // 自ドメイン正引き zone "example.jp" { type master; file "/usr/local/etc/namedb/master/example.jp.zone"; check-names ignore; }; zone "_acme-challenge.example.jp" { type master; file "/usr/local/etc/namedb/dynamic/_acme-challenge.example.jp.zone"; check-names ignore; update-policy { grant certbot-key. name _acme-challenge.example.jp. TXT; }; }; ``` example.jpのゾーンファイルに最後の行を追加する ```/usr/local/etc/namedb/master/example.jp.zone $TTL 3600 @ IN SOA ns1.example.jp. root.example.jp. ( 20190020101 ; Serial 3600 ; Refresh 900 ; Retry 3600000 ; Expire 3600 ; Minimum ) IN NS ns1.example.jp. IN NS ns2.example.jp. IN MX 10 mail.example.jp. IN A 111.222.333.100 ns1 IN A 111.222.333.100 ns2 IN A 111.222.333.200 mail IN A 111.222.333.100 www IN CNAME ns1.example.jp. www2 IN CNAME ns2.example.jp. ftp IN CNAME ns1.example.jp. _acme-challenge IN NS ns1.example.jp. ``` _acme-challengeサブドメインのゾーンファイルをdynamicディレクトリーに作成 ```/usr/local/etc/namedb/dynamic/_acme-challenge.example.jp.zone $TTL 3600 @ IN SOA ns1.example.jp. root.example.jp. ( 20190020101 ; Serial 3600 ; Refresh 900 ; Retry 3600000 ; Expire 3600 ; Minimum ) IN NS ns1.example.jp. ``` パーミッションの設定 ``` jail1 /usr/local/etc/namedb/dynamic # chmod 644 _acme-challenge.example.jp.zone jail1 /usr/local/etc/namedb/dynamic # chown bind:bind _acme-challenge.example.jp.zone ``` ### certbotの設定 ```sh:/usr/local/etc/letsencrypt/dns-rfc2136.ini # _acme-challenge.example.jpゾーンのmasterサーバのアドレス dns_rfc2136_server = 111.222.333.100 # アクセスするポート番号 dns_rfc2136_port = 53 # 認証鍵の名前 dns_rfc2136_name = certbot-key. # 認証鍵の値 dns_rfc2136_secret = /9a5f/hobiMY9BuycJOehKDNB1QzDMPliFbnSBecZZM= # 鍵の生成に用いたアルゴリズム dns_rfc2136_algorithm = HMAC-SHA256 ``` ## 証明書の取得 ### アカウントの作成 以下は連絡用のメールをシェアする場合でありしない場合は--eff-emailを--no-eff-emailとすれば良い ``` jail1 /root # certbot register --email admin@example.jp --agree-tos --eff-email Saving debug log to /var/log/letsencrypt/letsencrypt.log IMPORTANT NOTES: - Your account credentials have been saved in your Certbot configuration directory at /usr/local/etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. ``` ### 証明書の取得 ``` jail1 /root # certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /usr/local/etc/letsencrypt/dns-rfc2136.ini -d '*.example.jp' Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator dns-rfc2136, Installer None Obtaining a new certificate Performing the following challenges: dns-01 challenge for example.jp Waiting 60 seconds for DNS changes to propagate Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /usr/local/etc/letsencrypt/live/example.jp/fullchain.pem Your key file has been saved at: /usr/local/etc/letsencrypt/live/example.jp/privkey.pem Your cert will expire on 2019-04-27. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - 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 ``` 取得した証明書は以下のように実態のソフトリンクになっている。更新していくとリンク先のファイル名の添字が変わっていく(例cert1.pem → cert2.pem)。 ``` jail1 /root # ls -l /usr/local/etc/letsencrypt/live/example.jp/ total 4 -rw-r--r-- 1 root wheel 692 Jan 27 15:16 README lrwxr-xr-x 1 root wheel 31 Jan 27 15:16 cert.pem@ -> ../../archive/example.jp/cert1.pem lrwxr-xr-x 1 root wheel 32 Jan 27 15:16 chain.pem@ -> ../../archive/example.jp/chain1.pem lrwxr-xr-x 1 root wheel 36 Jan 27 15:16 fullchain.pem@ -> ../../archive/example.jp/fullchain1.pem lrwxr-xr-x 1 root wheel 34 Jan 27 15:16 privkey.pem@ -> ../../archive/example.jp/privkey1.pem ``` ### 証明書の更新 初期認証条件などが記録されているため、更新は以下のコマンドで簡単に更新できる。 ``` jail1 /root # certbot renew ``` 証明書を使用しているアプリケーションに対し更新したことを通知する必要があり以下のスクリプトをクロンで呼び出すことで自動更新ができる(上記参考サイトに公開されているスクリプトをそのまま使用)。 ```sh:/usr/local/sbin/renew-letsencrypt #!/bin/sh LANG=C PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/sbin:/usr/sbin letsencrypt_dir=/usr/local/etc/letsencrypt/ services="apache24 postfix dovecot" check_update() { dir=$1 [ $(find ${dir} -mtime -1 -print | wc -l) -gt 1 ] } if [ $(id -u) != 0 ] then echo "This command requires root previlege." 1>&2 exit 1 fi certbot renew if check_update ${letsencrypt_dir} then for service in ${services} do service ${service} reload || exit $? done fi exit 0 ``` crontabに以下を追加する。 ```sh:/etc/crontab 45 3 * * 6 /usr/local/sbin/renew-letsencrypt ``` これで、毎週土曜日に更新要求される。有効期間が30日未満になると実際に更新され、使用しているアプリケーションにreload指示が発行される。