CMS 同期と証明書の運用
1. 概要
DAT CMS (Certificate Management Service) は、クラスタ全体で共有する証明書を生成し配布するサーバーです。
各アプリケーションは CMS クライアント (DatCmsManager) を通じて定期的に証明書リストを取得しており、この同期がキーローリングを自動化します。運用者がキーを手動で交換しなくても、証明書は定められた周期で新しく生成され、古いものは自ら失効します。
発行可能な証明書を受け取るのはログインサーバーだけで、コンテンツサーバーは検証専用の証明書だけを受け取ります。コンテンツサーバーは CMS だけを知っていればよく、ログインサーバーを知る必要はありません。
2. 同期プロトコル
2.1. リクエストとレスポンス
| エンドポイント | 用途 |
|---|---|
GET /v1/certs?version=N | 完全な証明書 (署名用秘密鍵を含む) |
GET /v1/certs/verify-only?version=N | 検証専用の証明書 |
GET /v1/certs.json, /v1/certs/verify-only.json | 同じ内容の JSON 形式 |
POST /v1/cert/{sig-alg}/{crypto-alg}/{delay}/{duration}/{ttl} | 証明書の手動生成 (Master トークンが必要) |
GET /health | ヘルスチェック |
レスポンス本文は、1 行目がサーバーの現在の version、2 行目以降に証明書が 1 行ずつ入った平文です。
1712345678
1a.1712345000.3600.1800.ECDSA-P256.IV-AES256-GCM.<sig-key>.<crypto-key>
2b.1712348600.3600.1800.ECDSA-P256.IV-AES256-GCM.<sig-key>.<crypto-key>2.2. バージョンカーソル
クライアントは最後に成功した version を記憶し、次のリクエストに載せて送ります。サーバーはその値より新しい証明書だけを選んで返します。
- クライアントの version がサーバーより過去である場合 → それ以降に作られた証明書のみを返します。
- クライアントの version がサーバーより未来である場合 (サーバー交換・DB 初期化など) → カーソルを
0に戻してセット全体を返します。 - クライアントはインポートに成功した場合にのみ version を前進させます。失敗したレスポンスでカーソルが進んでしまい、証明書を永久に取り逃す状況を防ぐためです。
増分リクエストですがレスポンスは全体の置き換えです
?version=N は「N 以降の変更分をください」というリクエストですが、クライアントは受け取ったリストを既存のリストとマージせずに置き換えます (clear = true)。サーバーが有効な証明書の全体を常に判断して返すためであり、この方式のおかげで CMS で失効 (revoke) された証明書がクライアントに残り続けることがありません。
2.3. 認証トークン
CMS は 3 種類のトークンでアクセスを分けます。
| トークン | 権限 |
|---|---|
Masterトークン | DAT証明書の生成、サーバーバージョンの確認 |
Full Certトークン | Full (Pair Key, Hash Key) 証明書の取得 |
Verify Certトークン | Verify (Verify Key Only) 証明書の取得 |
検証のみを行うサーバーには Verify Cert トークンだけを渡すのが原則です。ただし暗号化キーは verify-only のレスポンスにも含まれるため、その意味については 証明書 のドキュメントの注意事項もあわせて確認してください。
3. 証明書の発行遅延 (delay)
新しい証明書を作成した直後に発行へ使うと、まだ同期していない他のノードはその証明書で署名されたトークンを検証できません。発行遅延はこの区間をなくすための値です。
たとえば CMS が証明書 A を作成し、サーバー 1・2 が 60 秒周期で同期しているとします。サーバー 1 が先に取得して A で DAT を発行したものの、サーバー 2 がまだ取得していない場合、サーバー 2 はその DAT を検証できません。
遅延を 180 秒に設定すると、証明書の生成後 180 秒間は発行不可の状態のまま留まり、その間にすべてのサーバーが安全に同期を完了します。一時的なネットワーク障害を考慮し、各サーバーの同期周期より最低でも 3〜4 倍以上大きく設定することを推奨します。
4. 意図された挙動
以下の挙動はすべて設計上意図されたものであり、欠陥ではありません。運用時に予想と異なって見えることがあるため明記します。
4.1. 発行ウィンドウが閉じた後もキャッシュされた証明書で署名し続けます
アプリケーションは同期時点で選んだ発行用証明書を使い続け、発行のたびに issuable() を再確認することはしません。
理由: CMS との接続が切れた状態で発行ウィンドウが閉じると、再確認方式ではその瞬間にサービス全体のログインが止まります。 DAT はこの場合、「新しい証明書を受け取れなかったとしても、とりあえず発行は続ける」という選択をしました。
代償: ネットワーク障害が長引くと、すでに発行ウィンドウを過ぎた証明書でトークンが出続ける可能性があります。ただしそのトークンも証明書の最終失効までは他のノードで正常に検証されるため、障害時にサービスが停止するよりはましだと判断したトレードオフです。
4.2. 同じ CID で更新された証明書は破棄されます
すでに保有している CID と同じ CID の証明書が入ってきた場合、新しく入ってきた方を無視します。
理由: CID は証明書の不変の識別子です。同じ CID が互いに異なるキーを指すようになると、すでに発行されて出回っているトークンがどのキーで検証されるべきか分からなくなります。
キーの交換は必ず新しい CID で
同じ CID を維持したままキーだけを変えて配布すると、クライアントには永久に反映されず、エラーも出ません。 キーを交換する際は新しい CID の証明書を発行してください。
4.3. 新しい証明書がなければ既存のリストを維持します
レスポンスに証明書が 1 つも含まれていない場合、クライアントは保有リストをそのまま残します。 リストを空にはしません。
理由: 証明書サーバーがダウンしていたりレスポンスが異常だったりする最悪の瞬間に保有証明書を空にしてしまうと、その瞬間にすべてのトークン検証が失敗します。新しく受け取ったものがなければ、既にあるもので持ちこたえる方が安全です。
4.4. SINGLE_NODE モードは起動のたびに証明書を生成します
CMS をシングルノードモードで実行すると、発行可能な証明書の有無にかかわらず起動のたびに証明書を 1 つ作成します。
理由: シングルノードモードは、CMS を別途のインフラなしに単独で立ち上げて使うための構成です。起動直後にすぐ発行できる証明書が存在する必要があります。
注意: 再起動が繰り返されると証明書が積み上がり続けます。ただし各証明書は自らの失効時刻を過ぎるとリストから除外されるため、無限に増え続けることはありません。
4.5. 発行可能な証明書がなければ遅延なしで即座に発行されます
証明書を生成する時点で発行可能な証明書が 1 つもない場合、CMS は遅延区間をスキップし、遅延時間を発行期間に合算します。
理由: 遅延を守ると、その時間の間クラスタ全体がトークンを 1 つも発行できません。初回起動時や全体障害からの復旧時には、即座に発行できる必要があります。このときサーバーログに警告が残ります。
5. 証明書の回収と失効
- 証明書は最終失効 (
start + duration + ttl) の時点まで配布リストに残ります。発行ウィンドウが閉じたからといってすぐに消えるわけではありません。 - 発行ウィンドウ終了の直前に出た DAT は自らの TTL の分だけさらに生き続けるため、その時点より後に初めて起動した検証サーバーも証明書を受け取って当該トークンを検証できます。
- 最終失効を過ぎた証明書はリストから外れ、その後のクリーンアップ処理でストレージからも削除されます。
6. デプロイ
CMS サーバーの実行オプション、Docker · Kubernetes · バイナリでのデプロイ方法と環境変数は、別のドキュメントで扱います。