目次
acme-client
Let's Encrypt(以下LE)は、無料でDomain Validation(以下DV)証明書を発行してくれる機関である。 このLEは、申請から証明書発行までを人手を介さずシステムだけで処理してくれる、自動化に非常に向いている仕組みを提供している。
Let's Encrypt
以前、Let's Encryptを少し調べてみたで調査した内容をまとめる。
Let's Encryptとは?
LEは、Internet Security Research Group (ISRG) が運営する、無料で利用でき、自動化されていて、オープンな認証局。
Let's Encryptの基本方針は
- 無料
- ドメインを保持する者が無料で信頼された証明書を取得可能
- 運用の自動化
- 証明書の取得から更新まで自動化することが可能
- 安全
- 認証局サイド、管理者の両面から、最先端のプラットフォームを提供
- 透明性
- 証明書の発行と失効が公式に記録され、一般公開され、誰でも監査できる
- オープン
- 各手続き(プロトコル)を、オープン標準として公表
- 互助・協力
- コミュニティの利益のための共同の取り組みであり、1つの組織によって統制されているものではない
(以上、About - Let's Encrypt - Free SSL/TLS Certificatesより抜粋)
実際に利用するレベルから見ると、
- 無料でSSLサーバ証明書を発行してくれるサービス。
- あくまでもサーバー証明書のみであり、個人証明書などは発行しない
- ドメイン認証型の証明書
- 一般に、サーバー証明書は「ドメイン認証(DV)」、「組織(企業)認証(OV)」、「実在性監査済(EV)」があるが、LEはDVしか発行しない
というサービスを提供してくれるサイト及びサービスである。
日本語訳のサイト: Let's Encrypt 総合ポータル
注意点
- 証明書の有効期間は90日
- 秘密鍵ファイルはパスフレーズで暗号化していない
- 国コードや組織名の入力がない
つまり、あくまで、「あるドメイン」が、「申請されたタイミング」で、「管理されているであろう」かつ「存在している」ことのみを証明するものである。 逆に、「仮に乗っ取られていたとしても申請を受けたLE側では判断しない」(管理組織が正当であるかどうかはドメインが存在しているかどうかとは独立の問題なので)ものである。
証明書の取得方法
Let's EncryptはDV証明書を発行するにあたって、ACME(Automatic Certificate Management Environment)を使用する。 このACMEは2017/03/13に、draft-ietf-acme-acme-06として更新されており、将来RFCになることが期待されている。
このACMEを利用した証明書取得のためのツールは様々ある。LEで把握しているACMEの実装に関しては、https://letsencrypt.org/docs/client-options/を参照すること。
acmeで証明書を取得・失効する流れ
acme-clientのManual Pageから抄訳
証明書取得の流れは以下の通り。
- CA(Let's EncryptのACME Server)に接続し、リソースリストを要求する
- (Option) RSAアカウント鍵を作成・登録する
- RSAアカウント鍵の確認。これは、CAへの認証とその後すべてのやりとりに利用される
- 各domainごとに以下の処理を行う
- CAに対し、認証のChallengeを提出
- Challengeへの返答ファイルを作成する
- Cアのchallengeによる検証を待つ
- 展開されたRSAもしくはECDSAのdomain鍵を読み込む
- そのdomain及びaltnamesに記載されたドメイン用の、domain鍵を用いたX509リクエストを作成する
- X509リクエストへの署名をCAに要求する
- X509証明書(Certificate)をダウンロードする
- CA issureから来たX509証明書を展開する
- 証明書チェーンをissureからダウンロードする
証明書失効の流れも同様。
- CA(Let's EncryptのACME Server)に接続し、リソースリストを要求する
- (もし見つかれば)、X509証明書を読み込み展開する
- X509 失効要求書を作成する
- CAに失効要求書を提出
- 証明書、チェン、そして、全チェーンを削除する。
Challenge
ACMEにおけるChallengeとは、要するに「存在確認」を意味する。この存在確認方法として以下の3つがIDに定義されている。
- http-01
- tls-sni-01
- dns-01
FreeBSDでのLEの証明書取得から更新
FreeBSD 11.0 RELEASE-p10 を利用している場合、LEを利用するにはportsに収録されているacme関連のツールを利用するのが一番簡単である。 おそらく、通常最も単純なのは、security/py-certbot を利用することだと考えられるが、今回は以下の理由により別ツールを利用する。
- SSL通信を行いたいWeb Serverは、冗長化されたLoadBalancer配下に設置されていること
- Certbotは、冗長環境を考慮していない
- 証明書取得・配布のために、巨大なpythonを導入するのは管理上面倒になる
- python自体は良いのだが、なにがしかのpy moduleを必要とするため、Update等を考慮しなければならないのが面倒
- certbotは有用なツールだが、中で何をしているのかわかりにくい
というわけで、代替ツールを利用するが、今回はacme-clientを利用する。 acme-clientはOpenBSDプロジェクトで利用されているacme clientアプリである。
今回の想定の構成
| | +-- nginx LB1 --+-- Web1a internet -- Router --+ | | +-- nginx LB2 --+-- Web1b | | | | | | +----------+-- Work ---+ | |
- 実際には、LB と Web の間に IPS や WAF を導入しているが、本件には影響がないので割愛する
- Work からのインターネットへの通信は Router 内で NAT している。
Challengeの流れ
- WorkからNATを経由してLEに要求を送る
- LEはChallenge TokenをWorkに返答する
- WorkはChallenge Tokenを必要な場所に設置し、Challenge要求をLEに送付する
- LEは、要求を受理し、要求のあったドメイン(ここでは、
http://example.com
とする)のWeb Server(実態はLB1/LB2)にChallenge Requestを送付 - LB1/LB2 はChallenge要求を受け付けると、Proxyとして動作し、WorkにChallenge要求を転送する
- WorkはTokenをLEに返す
- LEは正しくTokenを受領できた場合にのみ、Workに対して証明書を送る
行うべき設定
- LB1/LB2 に
http://example.com/.well-known/acme-challenge
宛のRequestをWorkに転送するように設定する - Workは、
http://example.com/.well-known/acme-challenge
宛の要求を処理できるようにする- httpdを立ち上げるなど
- Workが受け取ったCertファイルをLB1/LB2に自動で転送できるようにする(今回は割愛)
acme-client
acme-clientは、
- OpenBSD上で開発されており、LibreSSLを利用している。
- FreeBSDでは、ports/securityに収録されている。もちろん、Binary Packageあり。
- Linuxの場合、LibraSSLを利用することから、自力で作成するのは面倒かもしれない。Binary Packageは(あんまり探してないが)見つからなかった。
- これを期にFreeBSD/OpenBSDなマシンを作るのもあり
- Cで記述されており、余計なパッケージ類が不要
である。
ここでは、
- 証明書を保存するDirectory:
${CERTDir}
- 証明書の秘密鍵のフルパス名:
${CERTkey}
- 申請者の秘密鍵のフルパス名:
${PRIVKEY}
- Challengeの際のChallenge tokenを保存するDirectory:
${CHALLENGE}
とする。
acme-client -bnNv -k ${CERTkey} -f ${PRIVKEY} -C ${CHALLENGE} -c ${CERTDir} 申請するURL
を実行すると、必要な証明書等を作成し、秘密鍵、公開鍵を取得してくれる。
オプションの意味
- -b: バックアップを作成する
- -n: 4096bit RSA key(申請者用秘密鍵)を作成する
- -N: 4096bit RSA key(ドメイン用秘密鍵)を作成する
- -v: Verbose
- -k: ドメインの秘密鍵ファイルを指定
- -f: 申請者の秘密鍵を指定
- -C: チャレンジ用のDirectory
- -c: 証明書を置くディレクトリ
Load Balancer設定
- LB-nginx.conf
....(snip) upstream HTTP_WWW { server 198.51.100.11; server 198.51.100.12; } upstream HTTP_ACME { server 192.0.2.101; } server { listen 203.0.113.11; server_name www.example.com; location ^~ /.well-known/acme-challenge/ { proxy_pass http://HTTP_ACME; } location / { proxy_pass http://HTTP_WWW; } } ....(snip)
Work設定
- Work-nginx.conf
server { listen 192.0.2.101:80; server_name www.example.com; rewrite /.well-known/acme-challenge/(.*) /$1 last; location / { root ${CHALLENGE}; # 実際のChallengeディレクトリを記載してください。 index index.html; } }
これで、手元のブラウザから、http://www.example.com/.well-known/acme-challenge/index.html
にアクセスして、index.htmlが見えればOK
おまけ
マルチドメインに対応するために、acmeを一気に処理してくれるscriptを書いたので、参考にどうぞ。 詳しいことは、自力でこのshell scriptを読んでください。
- crt-update.sh
# cat crt-update.sh #! /bin/sh # Let's Encrypt Certificate renewal script for FreeBSD and acme-client # Copyright (C) by seirios@seirios.org # # Usage: crt-upd.sh [target domains...] ############################################################################### : ${DEBUG:=0} : ${FORCE:=0} ACME_BASE=/home/seirios/htdocs/acme OPTS="-bnN" DOPTS=""; [ ${DEBUG} -ne 0 ] && DOPTS="-v" FOPTS=""; [ ${FORCE} -ne 0 ] && FOPTS="-F" ACCKEY=${ACME_BASE}/SSL/privkey.pem SSL=${ACME_BASE}/SSL CHALLENGE=${ACME_BASE}/WWW DOMAINSFILE=${ACME_BASE}/domains.txt UID=`id -u` [ ${UID} -ne 0 ] && echo "Must run on root/UID=0" && exit if [ ${DEBUG} -ne 0 ]; then ECHO="/bin/echo" else ECHO="" fi if [ ${#} -eq 0 ]; then DOMAINS=`cat "${DOMAINSFILE}" | sed 's/[#|].*$//' | while read DOMAIN line ; do echo -n "${DOMAIN} " done` else DOMAINS=${@} fi [ ${DEBUG} -ne 0 ] && /bin/echo "Target domain: ${DOMAINS}" for i in ${DOMAINS}; do echo "Getting ${i} Certificates" DOMKEY=/home/seirios/htdocs/acme/SSL/$i/privkey.pem [ ! -d ${SSL}/${i} ] && ${ECHO} mkdir ${SSL}/${i} [ ! -d ${CHALLENGE}/${i} ] && ${ECHO} mkdir ${CHALLENGE}/${i} ${ECHO} acme-client ${OPTS} ${DOPTS} ${FOPTS} -k ${DOMKEY} -f ${ACCKEY} -C ${CHALLENGE}/${i} -c ${SSL}/${i} ${i} case $? in 0) echo "${i} is updated" ;; 1) echo "${i} is troubled" ;; 2) echo "${i} is not need to update" ;; esac done ############################################################################### # # Settings. # Requirement: FreeBSD and acme-client # # ToDo # - Change DEBUG/FORCE controll from environment variable to command # line options. # # Version History # # ver: 0.1 Initial revision. # 0.2 Add DEBUG initializer and FORCE initializer. # Display acme-client status. # ex. DEBUG=1 FORCE=1 crt-upd.sh #
注意
- このscriptは、エラーチェックとかを、あまり正しく(細かく)やっていないので、予期せぬ挙動をする可能性があります。
- 当然、無保証です。自明だと思うんだけど、At your own riskで。
- 時々書き換える可能性があります。
- domains.txt
# ACME domain configuration file. # format: domain|server:dir server:dir ... www.example.com|198.51.100.11:/some/where/Certs 198.51.100.12:/some/where/Certs www.example.net|198.51.100.11:/some/where/Certs 198.51.100.12:/some/where/Certs
これで、/some/where/SSL/www.example.(com|net)
にcert.pem
,chain.pem
,fullchain.pem
,privkey.pem
が作成されるので、必要に応じてファイルをrenameして転送すれば良い。