Let's EncryptでSSL証明書の新規作成と自動更新(dns-01編)

Let's Encrypt とは

  • Lets' Encrypt は認証局(Certificate Authority)のブランドの一つである。

  • これは「Symantec(旧Verisign)セキュア・サーバーID」「CyberTrust SureServer」「SecomTrust セコムパスポート for Web3.0」「GlobalSign クイック認証SSL」「GeoTrust RapidSSL」「Comodo PositiveSSL」などの一つと考えれば良い。

  • たぶんどれかは聞いたことあるはずと思う。アレが抜けてるというツッコミは却下で:-)。
  • 他の認証局と同じような点は、
    • 1証明書をどのように(複数IP、複数バックエンド、複数プロトコル)使用しても1取得で済む(安い認証局は大抵そうだよね)。
    • 親ドメインをまたぐ、マルチドメイン証明書(Subject Alternative Names)に対応している(全ての認証局で対応してるね)。
    • いよいよワイルドカード証明書に対応(対応してるブランドと対応してないブランドとあるね)。

    • DV(Domain Validation)証明書のみ提供(対応してるブランドと対応してないブランドとあるね)。
    • ただしOV(Organization Validation)証明書やEV(Extended Validation)証明書との純技術的な優劣は無い。
  • 他の認証局と明確に違う点は、
    • 無償。
    • RSA(2048bit、3072bit、4096bit), ECDSA(prime256v1、secp384r1) の5種類の鍵が選べる(ここまで選べる認証局は限られるね)。DSAが無い?時代だ。諦めろ。
    • ACME(Automated Certificate Management Environment)プロトコルによる証明書の認証から発行までの一連のバッチ化(自動化)が可能。
    • 今どき誤差かもしれないが、扱える端末が(他の認証局と比べて)少ない。
    • エンドユーザーが粘り強く使用している、全アクセスの0.5%以下の端末であってもフォローしないといけない用途であるならお勧めしない。
    • 逆に今どきのメジャーどころの端末・ブラウザは対応している。
    • よってPC相手にはほぼ問題無い(Windows XP? IE6? 知らんがな)。
    • 取得数制限(特に単位時間あたりの)があるので注意。詳しくは Rate Limits を参照のこと。

    • 検証(ステージング)用認証局も用意されているので、セットアップ時の検証や、ACMEクライアントの開発といった用途ではこちらを使う。

    • 「現在の」ルート証明書は「IdentTrust|DST(Digital Signature Trust) Root CA X3」である。

    • IdentTrust なの? DST なの? については旧会社のブランドも残ってるらしい、としか自分は認識してない。詳しくは会社概要でも読んでくれい。

    • 少なくとも中間証明書の発行者(Issuer)はそうである(Let's Encrypt 運用元の ISRG - Internet Security Research Group ではない)。

    • このルート証明書がインストールされた端末が対応端末となる。
    • 中間証明書(Subject)は「Let's Encrypt Authority X3」である(場合によってはこっち「も」入ってることがあるかもしれない)。
  • なおツールとしては certbot が代表的だが、他にもたくさんのツールが存在する。

  • ここでは全て dehydrated を使用を前提に解説する。certbot と dehydrated の違いについては特に解説しない。

  • dehydrated を選んだ理由は、
    • dehydrated は bash/zsh 依存スクリプトであるため、特別な言語環境(Python)を必要としない。
    • Python に依存する分には問題無いが、certbot の場合、依存する Python モジュールが極めて大量にあって維持が大変。
    • dehydrated はまだ依存が少ない(curl のせいでずいぶん増えてるが)。

    • dehydrated の場合、わけわかんなくなっても、シェルスクリプトなのでソースコード読んで理解できる。また長いコードではない。
    • dehydrated はWebサーバーを内蔵していないため、Webサーバー(Apache 等)との組み合わせに考慮しなくてよい。

dns-01 とは

目次

Contents

  1. Let's EncryptでSSL証明書の新規作成と自動更新(dns-01編)
    1. Let's Encrypt とは
    2. dns-01 とは
    3. 目次
    4. 検証環境
    5. 検証作業内容
      1. 想定サーバー・ドメイン
      2. DNSコンテンツサーバー側
      3. SSLサーバー側
    6. インストール
  2. DNSコンテンツサーバー側の設定
    1. /usr/local/etc/namedb/named.conf(一部)
    2. /usr/local/etc/namedb/dynamic/example.jp.db(example.jp ゾーンファイル)
    3. /usr/local/etc/namedb/ns-www.key(TSIGキーファイル)
      1. 本例における具体的設定例
  3. SSLサーバー側の設定
    1. /etc/periodic.conf
    2. /usr/local/etc/dehydrated/ns-www.key(TSIGキーファイル)
    3. /usr/local/etc/dehydrated/config
    4. /usr/local/etc/dehydrated/domains.txt
      1. ワイルドカード証明書
    5. /usr/local/etc/dehydrated/hook.sh
    6. /usr/local/etc/dehydrated/deploy.sh
  4. 【付録】ゾーン分割によるダイナミックアップデートの制限
    1. /usr/local/etc/namedb/named.conf(一部)
    2. /usr/local/etc/namedb/master/example.jp.db(example.jp ゾーンファイル)
    3. /usr/local/etc/namedb/dynamic/_acme-challenge.www.example.jp.db(_acme-challenge.www.example.jp ゾーンファイル)
    4. /usr/local/etc/dehydrated/hook.sh
    5. 相違点
      1. /usr/local/etc/namedb/named.conf
      2. /usr/local/etc/namedb/master/example.jp.db
      3. /usr/local/etc/namedb/dynamic/_acme-challenge.www.example.jp.db
  5. 参考文献
    1. 参考文献について一言
  6. 【付録】 dehydrated v0.40 での変更
  7. 【付録】 letsencrypt.sh から dehydrated への移行
    1. ディレクトリ名の変更
      1. /usr/local/etc/letsencrypt.sh/config.sh
      2. /usr/local/etc/letsencrypt.sh/deploy.sh
      3. /usr/local/etc/letsencrypt.sh/domains.txt
      4. /usr/local/etc/letsencrypt.sh/hook.sh
      5. /usr/local/etc/letsencrypt.sh/certs/
      6. /usr/local/etc/letsencrypt.sh/archive/
      7. 後始末
    2. 設定ファイルの変更
    3. SSL証明書利用アプリケーションの設定変更
      1. SSL証明書の旧指定例
      2. トークンディレクトリの旧指定例
    4. 起動設定(periodic)の変更

検証環境

検証作業内容

想定サーバー・ドメイン

DNSコンテンツサーバー側

SSLサーバー側

インストール

DNSコンテンツサーバー側の設定

/usr/local/etc/namedb/named.conf(一部)

include "/usr/local/etc/namedb/ns-www.key";

zone "example.jp" {
    type master;
    file "/usr/local/etc/namedb/dynamic/example.jp.db";
    update-policy {
        grant ns-www. name _acme-challenge.www.example.jp. TXT;
    };
};

/usr/local/etc/namedb/dynamic/example.jp.db(example.jp ゾーンファイル)

$TTL               300

@                       IN SOA ns.example.jp. domain.example.jp. (
                               2017032201 ; serial
                               7200       ; refresh (2 hours)
                               900        ; retry (15 minutes)
                               2419200    ; expire (4 weeks)
                               86400      ; minimum (1 day)
                               )
                        IN NS ns
ns                      IN A  192.0.2.1

/usr/local/etc/namedb/ns-www.key(TSIGキーファイル)

key "ns-www." {
    algorithm hmac-sha256;
    secret "PfzeGvXiOqtPOwQJY/iNFrvlD3/eKAHRZ0TbyK5GYII=";
};

上記ファイルは以下の手順にて生成することができる。

tsig-keygen -a hmac-sha256 ns-www. > /usr/local/etc/namedb/ns-www.key
chown bind:wheel /usr/local/etc/namedb/ns-www.key
chmod 0400       /usr/local/etc/namedb/ns-www.key

本例における具体的設定例

SSLサーバー側の設定

/etc/periodic.conf

weekly_dehydrated_enable="YES"

自動更新設定(YES=自動更新する)。 periodic(8)にある通り、毎週土曜日3時に実行される。

なお今回、weekly_dehydrated_deployscript は指定しない(後述の HOOK 設定参照のこと)。

/usr/local/etc/dehydrated/ns-www.key(TSIGキーファイル)

これは先に tsig-keygen コマンドで作成されたファイルである。 DNSコンテンツサーバーと同一になるようコピーするなどして設定すること。 その際のオーナー・グループ・パーミッションは以下の通りである。

chown root:wheel /usr/local/etc/dehydrated/ns-www.key
chmod 0400       /usr/local/etc/dehydrated/ns-www.key

/usr/local/etc/dehydrated/config

alias openssl="/usr/bin/openssl"

CHALLENGETYPE="dns-01"
HOOK="${BASEDIR}/hook.sh"
RENEW_DAYS="30"
KEY_ALGO="rsa" KEYSIZE="2048"
#KEY_ALGO="prime256v1"
CONTACT_EMAIL="メールアドレス"
#テスト発行したい場合、以下の2行を有効にすること。
#CA="https://acme-staging.api.letsencrypt.org/directory"
#CA_TERMS="https://acme-staging.api.letsencrypt.org/terms"

/usr/local/etc/dehydrated/domains.txt

www.example.jp

本ファイルの設定については コモンネームの設定に準拠するものとする(例)。

ワイルドカード証明書

v0.6.0 以降でワイルドカード証明書を取得したい場合の domains.txt の書き方は下記の通りです。

*.example.jp > star_example_jp

またベストプラクティクス的にははSANsによる設定がスマートかと思われます(example.jp をコモンネームとする)。

example.jp *.example.jp

/usr/local/etc/dehydrated/hook.sh

TTL="300"
DNSSERVER="ns.example.jp"
alias nsupdate="/usr/local/bin/nsupdate -k ${BASEDIR}/ns-www.key"

deploy_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
    printf 'server %s\nupdate add _acme-challenge.%s. %d TXT "%s"\nsend\n' "${DNSSERVER}" "${DOMAIN}" "${TTL}" "${TOKEN_VALUE}" | nsupdate
}

clean_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
    printf 'server %s\nupdate delete _acme-challenge.%s. TXT\nsend\n' "${DNSSERVER}" "${DOMAIN}" | nsupdate
}

deploy_cert {
    /usr/sbin/service apache24 restart && /usr/local/bin/dehydrated -gc
}

HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert)$ ]]; then
  "$HANDLER" "$@"
fi

/usr/local/etc/dehydrated/deploy.sh

【付録】ゾーン分割によるダイナミックアップデートの制限

/usr/local/etc/namedb/named.conf(一部)

include "/usr/local/etc/namedb/ns-www.key";

zone "example.jp" {
    type master;
    file "/usr/local/etc/namedb/master/example.jp.db";
};

zone "_acme-challenge.www.example.jp" {
    type master;
    file "/usr/local/etc/namedb/dynamic/_acme-challenge.www.example.jp.db";
    update-policy {
        grant ns-www. name _acme-challenge.www.example.jp. TXT;
    };
};

/usr/local/etc/namedb/master/example.jp.db(example.jp ゾーンファイル)

$TTL               300

@                       IN SOA ns.example.jp. domain.example.jp. (
                               2017032201 ; serial
                               7200       ; refresh (2 hours)
                               900        ; retry (15 minutes)
                               2419200    ; expire (4 weeks)
                               86400      ; minimum (1 day)
                               )
                        IN NS ns
_acme-challenge.www     IN NS ns
ns                      IN A  192.0.2.1

/usr/local/etc/namedb/dynamic/_acme-challenge.www.example.jp.db(_acme-challenge.www.example.jp ゾーンファイル)

$TTL               300

@                       IN SOA ns.example.jp. domain.example.jp. (
                               2017032201 ; serial
                               7200       ; refresh (2 hours)
                               900        ; retry (15 minutes)
                               2419200    ; expire (4 weeks)
                               86400      ; minimum (1 day)
                               )
                        IN NS ns.example.jp.

/usr/local/etc/dehydrated/hook.sh

  :
DNSSERVER="ns.example.jp"
  :

場合によっては DNSSERVER 設定を変更する(今回の前提では必要ない)。

相違点

/usr/local/etc/namedb/named.conf

/usr/local/etc/namedb/master/example.jp.db

/usr/local/etc/namedb/dynamic/_acme-challenge.www.example.jp.db

参考文献

参考文献について一言

【付録】 dehydrated v0.40 での変更

v0.40 で、ライセンス受諾の確認が必須になりました。 それまでは暗黙の受諾として、dehydrated が勝手に受諾していましたが、 別途手動で、dehydrated --register --accept-terms を実行する必要があります。

ステージング用のライセンス受諾ともまた別の話となりますので、 CA だけでなく、CA_TERMS も合わせて設定する必要があります。

この振る舞いは /usr/local/etc/dehydrated/accounts/ハッシュキー/account_key.pem の有り無しがトリガーとなりますので、 既にこのファイルがある環境では影響有りません。

初回実行に手動で実行していれば気がつくレベルとなります(エラーメッセージ等「読まない」人についてはノーコメント)。

【付録】 letsencrypt.sh から dehydrated への移行

商標上の理由で、letsencrypt.sh から dehydrated へ名前変更が実施された。 合わせてディレクトリから設定まで色々変更になったので対応についてメモっておく。

ディレクトリ名の変更

/usr/local/etc/letsencrypt.sh から /usr/local/etc/dehydrated へ変更になったわけであるが、更新後は新しいディレクトリが作成されていることもあり、こまめに手を入れる必要がある。

/usr/local/etc/letsencrypt.sh/config.sh

mv /usr/local/etc/letsencrypt.sh/config.sh /usr/local/etc/dehydrated/config

※config.sh から config に変更になったので合わせて変更を実施する。

/usr/local/etc/letsencrypt.sh/deploy.sh

mv /usr/local/etc/letsencrypt.sh/deploy.sh /usr/local/etc/dehydrated/

/usr/local/etc/letsencrypt.sh/domains.txt

mv /usr/local/etc/letsencrypt.sh/domains.txt /usr/local/etc/dehydrated/

/usr/local/etc/letsencrypt.sh/hook.sh

mv /usr/local/etc/letsencrypt.sh/hook.sh /usr/local/etc/dehydrated/

/usr/local/etc/letsencrypt.sh/certs/

mv /usr/local/etc/letsencrypt.sh/certs /usr/local/etc/dehydrated/
  • certs ディレクトリはインストール時には作成されない。
  • SSL証明書取得時にコモンネームと合わせて作成される。
  • 既に /usr/local/etc/dehydrated/certs ディレクトリが作成されている場合は、子ディレクトリ(コモンネーム)を個別にリネーム(移動)すること。

/usr/local/etc/letsencrypt.sh/archive/

mv /usr/local/etc/letsencrypt.sh/archive /usr/local/etc/dehydrated/
  • archive ディレクトリはインストール時には作成されない。
  • SSL証明書クリーンナップ(-gc オプション指定)時にコモンネームと合わせて作成される。
  • 既に /usr/local/etc/dehydrated/archive ディレクトリが作成されている場合は、子ディレクトリ(コモンネーム)を個別にリネーム(移動)すること。

後始末

rmdir /usr/local/etc/letsencrypt.sh

※全ての移動が完了すれば、ディレクトリもファイルも残ってないはずなので削除する。

設定ファイルの変更

リネームによる影響で変更になるファイルは以下の3ファイルである。 ディレクトリ名およびコマンド名に letsencrypt.sh というキーワードが無いか確認し、dehydrated に置き換えること。

  • /usr/local/etc/dehydrated/config
  • /usr/local/etc/dehydrated/deploy.sh
  • /usr/local/etc/dehydrated/hook.sh

SSL証明書利用アプリケーションの設定変更

本例では Apache(2.4)を例に説明した。 当該箇所のディレクトリ名変更を新しいディレクトリ名に変更する。

SSL証明書の旧指定例

  SSLCertificateFile    /usr/local/etc/letsencrypt.sh/certs/コモンネーム/fullchain.pem
  SSLCertificateKeyFile /usr/local/etc/letsencrypt.sh/certs/コモンネーム/privkey.pem

トークンディレクトリの旧指定例

Alias /.well-known/acme-challenge/ /usr/local/etc/letsencrypt.sh/.acme-challenges/

<Directory /usr/local/etc/letsencrypt.sh/.acme-challenges>
  Options       None
  AllowOverride None
  Require       all granted
  Header        add Content-Type text/plain
</Directory>

起動設定(periodic)の変更

/etc/periodic.conf あるいは /etc/periodic.conf.local のどちらかの設定で定期更新を実施しているわけだが、変数名が変更になっている。

本例では以下のような設定を実施しているが、

weekly_letsencrypt_enable="YES"
weekly_letsencrypt_deployscript="/usr/local/etc/letsencrypt.sh/deploy.sh"

下記のように変更する。

weekly_dehydrated_enable="YES"
weekly_dehydrated_deployscript="/usr/local/etc/dehydrated/deploy.sh"

単純に letsencrypt や letsencrypt.sh を dehydrated に置き換えるだけである。

certificate/レッツエンクリプトでSSL証明書の新規取得と自動更新(dns-01編) (last edited 2019-05-08 17:19:31 by NorikatsuShigemura)