Let’s Encrypt証明書の差し替えを自動化したい

つい先日、実験的にVMware Horizon 7を導入し、外部からのアクセスを可能とするためにUnified Access Gateway(以後、UAG)を構築しました。この時、証明書は自己証明書とするのもなんだか微妙なので、Let’s Encrypt(以後、LE)を使うことにしました。
ですが、ご存じのようにLEの有効期間は3か月です。
一応我が家の環境には、期限が1か月を切ったときに証明書を自動更新する仕組みと、それを統合リバースプロキシサーバへ配信する仕組みを過去に作って現在も運用を続けています。これを何とか活用して、UAGに対する証明書適用の処理を自動化できないかなーと思ってスクリプトを作りました。
参考にした情報
で、UAGには何かしらREST APIでも備わってるやろう・・・と思って情報を探すのですが、意外と見つかりませんで。そして技術ブログ等でもLet’s EncryptでUAGと連携する記事って意外となく、たいていの情報は海外にありました。
しかし、頼みの綱の海外サイトもどちらかというとCertbotの話の比率が大きく、あまりREST APIに対する情報は記載がなく、うろうろし続けたら、まさかのVMware社のDocsに情報がありましたOrz

これを用いてスクリプトを作ることにしました。
スクリプトの内容
作ったスクリプトは以下のような内容です。ファイルは定義ファイルと実行ファイルに分かれていて、定義ファイルに対してはパーミッション600に設定しています。
/usr/local/etc/uag-certrenew.cnf
ADMUSERDAT='admin:<password>'
KEYFILE=<path>/****.key
CRTFILE=<path>/****.crt
ENDPOINT=https://<FQDN>:9443/rest/v1/config/certs/ssl
/usr/local/bin/uagcert-change.sh
#!/bin/bash
. /usr/local/etc/uag-certrenew.cnf
TEMPFILE1=/tmp/tmp-jsongen-1.txt
TEMPFILE2=/tmp/tmp-jsongen-2.txt
PRIVKEYPEM="`cat $KEYFILE`"
CERTCHAINPEM="`cat $CRTFILE`"
echo "{\"privateKeyPem\": \"$PRIVKEYPEM\", \"certChainPem\": \"$CERTCHAINPEM\"}" > $TEMPFILE1
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' $TEMPFILE1 > $TEMPFILE2
curl -k -d @- -u $ADMUSERDAT -H "Content-Type: application/json" -X PUT $ENDPOINT < $TEMPFILE2
処理の概要として簡単に説明すると、REST API用の受け口として提供されたURLに向けて、以下のようなJSONデータを投げ込んでいます。認証はBASIC認証を用いればよいようです。
{
"privateKeyPem": "string",
"certChainPem": "string"
}
stringの中には証明書データの中身を格納させる必要があります。この時—– BEGIN CERT…というような先頭・末尾にあるような行も含める必要があります(文字列マッチングに利用しているようだ)。
また、この時改行コードなどに対する一定の文字列変換が必要になるため、いったんJSONフォーマットに格納してから文字列変換を行い、それをcurlに渡すようにしています。該当箇所が以下です。
echo "{\"privateKeyPem\": \"$PRIVKEYPEM\", \"certChainPem\": \"$CERTCHAINPEM\"}" > $TEMPFILE1
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' $TEMPFILE1 > $TEMPFILE2
私の環境ですと、一定期間のサイクルで常に最新の証明書が定められた特定のディレクトリに配置されています。これは私の環境ではLegoで更新しています。これで更新されたファイルから証明書データを抜き出して、JSONに成型して変換し、UAGへ送るということをしているだけですね。
仕上げとしては、我が家で稼働しているRundeck環境にこのスクリプトのジョブとスケジュールを組み込んで回すだけです。うまくスクリプトが動作すると、UAGからは投入した証明書データの情報が出力されます。失敗するとエラーメッセージが返ってきます。
本当は変数処理に閉じて行えたら最高なんですが、ちょっとそのあたりの処理を書くのがへたくそで、どーしてもコントロール文字の無効化がうまくいかなかったんで、私は今回ファイルベースで処理するように組んでいますが、きっとデキる人はこの辺りをスマートに仕上げられるに違いない・・ぜひぜひチャレンジしてみてくだされ・・
Comments are closed