1. Chun3

    Posted

    Chun3
Changes in title
+FreeBSD 12 で自宅サーバ作成 Let's Encrypt編
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,338 @@
+## はじめに
+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サーバ編
+* メールサーバ編
+* ファイルサーバ編
+
+## 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の中からはまだ実施していないため問題が出る可能性があり、最終的にシステム移行時に確認予定。
+
+## 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指示が発行される。