Let's Encrypt でワイルドカード証明書を発行してみる

Let's Encrypt のワイルドカード証明書発行がリリースされました。

https://community.letsencrypt.org/t/acme-v2-production-environment-wildcards/55578

プロダクション環境でのワイルドカード証明書が可能になったので、example.jp と、*.example.jp のマルチドメインで使用できるワイルドカード証明書を発行してみます。


## 必要なもの

* TXT レコードを設定できるドメイン (Google Cloud DNS 等 API 対応のサービス利用が望ましい)

* Go 言語コンパイラー (v1.10)

* openssl コマンド (Windows では、Git for Windows に付属)


本稿でのワークステーション環境は、Windows 10 です。

ドメイン名として example.jp と例示していますが、実際には、ご自分のドメイン名に読み替えてください。


## ツールのビルド

```
> go get -u github.com/noumia/acme/cmd/wild-le
```

%GOPATH%\bin へ、実行ファイルが作成されるので、パスを通しておきます。


## アカウントの作成

今回は、ECDSA の鍵を使用してみます。(RSA でも勿論可です。)


```
> openssl ecparam -out account.key -name prime256v1 -genkey

> wild-le account -c acme@example.jp --agree-tos
```

メールアドレスの登録は任意ですが、利用規約には同意する必要があります。


## 証明書の発行リクエスト

サーバー秘密鍵を作成します。アカウント用の鍵と共用することはできません。

サーバー用の鍵も ECDSA にしてみます。

```
> openssl ecparam -out server.key -name prime256v1 -genkey
```


## CSR の作成

example.jp と、*.example.jp の両方で使用できるようにマルチドメインの設定を行った CSR を作成します。

まず、テンプレートファイル (example.jp.conf) を作成します。

```
[req]
distinguished_name=dn
req_extensions=ex
[dn]
[ex]
subjectAltName=@alt_names
[alt_names]
DNS.1=example.jp
DNS.2=*.example.jp
```

キーポイントは、最終 2 行での subjectAltName 設定です。

```
> openssl req -new -key server.key -sha256 -out server.csr -subj "/CN=example.jp" -config example.jp.conf
```

以下コマンドで、CSR の内容を確認します。

```
> openssl req -in server.csr -text -noout
```

X509v3 Subject Alternative Name: が設定されているか確認してください。


## 証明書の発行

```
> wild-le renew server.crt
DNSSetup TXT: _acme-challenge.example.jp 8ZkX2so-Beyzq2RWmntJ_dsR_-W1B_j5X-7OXNNoPiI
Continue?
```

表示されたドメイン (_acme-challenge.example.jp) の TXT レコードへ、表示された認証用のテキスト (8ZkX2...) を設定してください。

設定できたら、y [Enter] とタイプすることで、処理が続行されます。(レコードの設定反映を確認するため、数分程度の時間を要することがあります。)

example.jp と、*.example.jp の両方の認証を行う場合は、２回確認手続きが必要になります。

エラーが起きなければ、server.crt へ証明書が出力されます。


## 動作確認

caddy サーバーを使用して、動作確認します。

www.example.jp としてアクセスできるサーバーに、Caddyfile, server.crt, server.key を配置します。

### Caddyfile
```
https://www.example.jp {
  tls server.crt server.key
}
```

### サーバーの起動
```
> caddy
Activating privacy features... done.
https://www.example.jp
```


curl コマンド等でアクセスできれば、成功です。

example.jp, api.example.jp 等のドメイン名も、同じ証明書で使用できるはずです。


## 自動更新

Let's Encrypt から発行される証明書は、有効期間が 90 日と短めですので、証明書の自動更新が必要です。

本ツールでも、DNS 認証設定用のフックスクリプトに対応していますので、対応する DNS プロバイダーを使用することで、自動更新が可能です。

```
wild-le renew -l フックスクリプト
```


Let's Encrypt クライアントである lego をベースとした DNS 設定ツールを用意したので、Google Cloud DNS 等の自動設定可能な DNS サービスを利用されている場合には、自動更新を実現できます。


### DNS 設定ツールのビルド

```
> go get -u github.com/noumia/acme/cmd/dns-lego
```

## 自動更新の運用例

Google Cloud DNS を使用している場合の例を説明します。

Google Cloud DNS API を使用するために適切な権限を割り当てた Service Account を作成して、その Key JSON ファイルを、機密の確保された場所に保存します。

スクリプトの所定の環境変数に、KeyJSON ファイルパスと、Google Cloud のプロジェクト名を設定してください。

LEGO_DNS_PROVIDER には、対応するプロバイダーを設定します。(gcloud: Google Cloud DNS)

使用できるプロバイダーの一覧は、以下の通りになります。

https://github.com/xenolf/lego/blob/master/providers/dns/dns_providers.go


### 定期実行するスクリプトの例
```
@rem renew.cmd
@echo off

set GOOGLE_APPLICATION_CREDENTIALS=<pathToServiceAccountKeyJSON>
set GCE_PROJECT=<projectName>
set LEGO_DNS_PROVIDER=gcloud

rem 証明書の有効期間をチェックしている
rem 30 日以上有効期間がある場合は、何もせず終了
wild-le cert server.crt
if not ERRORLEVEL 1 exit /b 0

wild-le renew -l dns-lego server.crt
if ERRORLEVEL 1 (
  echo error!
  exit /b 1
)

echo Congratulations! Your certificate and chain have been saved.

rem 証明書の更新が成功した
rem サーバーの更新処理などを記述する

exit /b 0
```

上記のようなスクリプトファイルを、スケジューラー等で 1日 1回 実行することで、自動更新を実現できます。