FJCT Tech blog

富士通クラウドテクノロジーズ公式エンジニアブログです

富士通クラウドテクノロジーズ

FJCT/Tech blog

Let's EncryptのDNS-01方式をニフクラDNSで認証して無料のSSL証明書を取得し自動更新する

これはFJCT Advent Calendar 1日目の記事です。

昨年に引き続き記念すべき1日目の記事を担当することになりました。
よろしくお願いします。

はじめに

自分はニフクラコントロールパネルの開発を担当しているのですが
開発時に使用する検証環境で有料の証明書を購入する訳にもいかず、以前は検証環境で不正な証明書や自己証明書を使っていました。

しかし、自己証明書ではブラウザの警告やHTTPクライアントの設定によっては本番相当の動作確認ができない等のデメリットもあり、最近検証環境の証明書をLet's Encryptに置き換えました。

Let's Encryptは無料でSSL証明書を取得でき、ドメイン認証をするのでブラウザから不正な証明書として見なされる事はありません。
また、クライアントツールから簡単に自動更新できるので、期限切れになるというリスクもありません。

今回はLet's Encryptを利用し、無料で証明書を作成して自動更新を実施する手順を紹介したいと思います。

環境

手順

Certbotのインストール

$ sudo yum install -y epel-release
$ sudo yum install -y certbot

証明書の作成

Certbotを利用した証明書の取得はHTTP-01 Challengeが有名ですが、これは80番ポートでグローバルからアクセスできる必要があり、ファイアウォールでアクセス制限をしているような検証環境では使えません。 そこで、今回はDNSレコードで認証するDNS-01 Challengeを利用します。

DNSサービスはニフクラDNSを利用するため、こちらを参考に認証したいドメインをゾーン登録します。

フックスクリプト

DNS-01 ChallengeではTXTレコードを利用して認証するので、フックスクリプトからニフクラDNSAPIをリクエストしてTXTレコードを登録することで認証を実施します。
以下のようなshellスクリプト/usr/local/bin/ に配置しておきます。

  • nifcloud-dns-auth.sh
#!/usr/bin/env bash

NIFCLOUD_ACCESS_KEY_ID="<YOUR_ACCESS_KEY>"
NIFCLOUD_SECRET_KEY="<YOUR_SECRET_LEY>"

body="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
      <ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2012-12-12/\">
          <ChangeBatch>
              <Changes>
                  <Change>
                      <Action>CREATE</Action>
                      <ResourceRecordSet>
                          <Name>_acme-challenge.${CERTBOT_DOMAIN}</Name>
                          <Type>TXT</Type>
                          <TTL>60</TTL>
                          <ResourceRecords>
                              <ResourceRecord>
                                   <Value>${CERTBOT_VALIDATION}</Value>
                              </ResourceRecord>
                          </ResourceRecords>
                      </ResourceRecordSet>
                  </Change>
              </Changes>
              <Comment>Created by certbot.</Comment>
          </ChangeBatch>
      </ChangeResourceRecordSetsRequest>"

datetime=$(LC_ALL=en TZ='GMT' date "+%a, %d %b %Y %H:%M:%S %Z")
signature=`echo -en "${datetime}" | openssl sha1 -hmac ${NIFCLOUD_SECRET_KEY} -binary | base64`

result=$(curl -s -X POST \
  -H "Date: ${datetime}" \
  -H "Host: dns.api.nifcloud.com" \
  -H "Content-Type: text/xml" \
  -H "X-Nifty-Authorization: NIFTY3-HTTPS NiftyAccessKeyId=${NIFCLOUD_ACCESS_KEY_ID},Algorithm=HmacSHA1,Signature=${signature}" \
  -d "${body}" \
  "https://dns.api.nifcloud.com/2012-12-12N2013-12-16/hostedzone/${CERTBOT_DOMAIN}/rrset")

if [ `echo "$result" | grep '<Error>'` ] ; then
    echo $result >&2
    exit 1
fi

echo $result
  • nifcloud-dns-clean.sh
#!/usr/bin/env bash

NIFCLOUD_ACCESS_KEY_ID="<YOUR_ACCESS_KEY>"
NIFCLOUD_SECRET_KEY="<YOUR_SECRET_LEY>"

body="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
      <ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2012-12-12/\">
          <ChangeBatch>
              <Changes>
                  <Change>
                      <Action>DELETE</Action>
                      <ResourceRecordSet>
                          <Name>_acme-challenge.${CERTBOT_DOMAIN}</Name>
                          <Type>TXT</Type>
                          <TTL>60</TTL>
                          <ResourceRecords>
                              <ResourceRecord>
                                   <Value>${CERTBOT_VALIDATION}</Value>
                              </ResourceRecord>
                          </ResourceRecords>
                      </ResourceRecordSet>
                  </Change>
              </Changes>
              <Comment>Created by certbot.</Comment>
          </ChangeBatch>
      </ChangeResourceRecordSetsRequest>"

datetime=$(LC_ALL=en TZ='GMT' date "+%a, %d %b %Y %H:%M:%S %Z")
signature=`echo -en "${datetime}" | openssl sha1 -hmac ${NIFCLOUD_SECRET_KEY} -binary | base64`

result=$(curl -s -X POST \
  -H "Date: ${datetime}" \
  -H "Host: dns.api.nifcloud.com" \
  -H "Content-Type: text/xml" \
  -H "X-Nifty-Authorization: NIFTY3-HTTPS NiftyAccessKeyId=${NIFCLOUD_ACCESS_KEY_ID},Algorithm=HmacSHA1,Signature=${signature}" \
  -d "${body}" \
  "https://dns.api.nifcloud.com/2012-12-12N2013-12-16/hostedzone/${CERTBOT_DOMAIN}/rrset")

if [ `echo "$result" | grep '<Error>'` ] ; then
    echo $result >&2
    exit 1
fi

echo $result

ニフクラAPIの認証キーはご自身のアカウントのキーをコンパネから取得しご利用ください。
shellスクリプトへ実行権限を付与しておきます。

$ sudo chmod +x /usr/local/bin/nifcloud-dns-auth.sh
$ sudo chmod +x /usr/local/bin/nifcloud-dns-clean.sh

実行

以下のコマンドで証明書を作成します。

$ certbot certonly -n \
   -d your_domain \
   -m your_mail_address \
   --agree-tos \
   --manual-public-ip-logging-ok \
   --manual \
   --preferred-challenges dns \
   --manual-auth-hook nifcloud-dns-auth.sh \
   --manual-cleanup-hook nifcloud-dns-clean.sh

証明書の確認

認証に成功すると、以下のディレクトリに証明書が作成されるので存在することを確認します

$ ls -la /etc/letsencrypt/live/your_domain

cert.pem -> ../../archive/your_domain/cert1.pem
chain.pem -> ../../archive/your_domain/chain1.pem
fullchain.pem -> ../../archive/your_domain/fullchain1.pem
privkey.pem -> ../../archive/your_domain/privkey1.pem

証明書の指定

作成した証明書を早速Nginxに組みこんでみます。
ここは、通常の証明書をNginxに設定するのと変わりません。
例えば、以下のような設定になります。

server {
    listen       443 ssl http2;
    server_name your_domain;

    ssl                     on;
    ssl_certificate         /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/your_domain/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Nginxリロード後、ブラウザからアクセスしてみて証明書が設定されているか確認してみましょう。

自動化

Let's Encryptの証明書は3ヶ月で期限が切れてしまうのでcronなどで定期的に実行し、期限が近づいたら更新するようにします。

PATH="/bin:/usr/bin:/usr/local/bin:/usr/sbin:/usr/local/sbin:/sbin"
@weekly certbot renew -q --no-self-upgrade --post-hook 'systemctl reload nginx'

まとめ

FJCT Advent Calendarの記事として、Let's Encryptを利用した無料証明書の取得方法を紹介してみました。

Let's Encryptは無料で使えて大変便利なので、検証環境用や個人サービスを運営している方は是非使ってみてください。 ただしDV証明書しか取得できないため、商用環境でOV以上の証明書を取得したい場合はニフクラのSSL証明書サービスをご利用いただければと思います。

明日はhunter9x がlogstashのPersistent queuesとBackpressureに関する内容を投稿予定です。
お楽しみに!