安全設計
このページは,ACME Conductor が何を資産とみなし,どんな攻撃者を想定し, それぞれの脅威に対して何を対策しているかを図解します.元になっているのは リポジトリの 脅威モデルと アーキテクチャ文書で, このページで断定的に書いていることは,すべてそちらの文書で詳しく裏付けられています. 「安全か」という単一の答えではなく,今日保証されていること・されていないことを 正直に分けて示します.
何を守るのか
脅威モデルが挙げる資産です.いちばん価値が高いのは秘密鍵で,Runner の一時領域と Certificate Store の外に決して存在してはならないとされています.
秘密鍵
最も価値の高い資産.Runner の一時領域と Certificate Store の外に決して存在してはならない.
DNS 書き込みの資格情報/ワークロード ID
ACME の DNS-01 チャレンジを完了するために使う.
Certificate Store の資格情報/ワークロード ID
Store への書き込み(および読み取り)に使う.
ACME アカウントの資材
External Account Binding(EAB)の HMAC 鍵を含む.
Target レジストリと CertificatePolicy
どの FQDN がどの制約の下で証明書を受け取れるかの支配権.ここの改ざんは不正発行への経路.
監査ログ
悪用の検出と調査のよりどころ.追記専用.
JobSpec / Result コントラクト
コントロールプレーンとデータプレーンの間の唯一のチャネル.その完全性が両者の分離を意味あるものにする.
Conductor と Runner の ID
各プロセスが実行に用いる資格情報またはワークロード ID.
発行された証明書そのものも資産ですが,公開のものであり,発行と有効性の正しさがセキュリティに関わります.
秘密はどこにあるか
Conductor は常駐するコントロールプレーンで,秘密鍵・DNS 資格情報・Store 資格情報のいずれも持ちません. 秘密鍵は 1 回ごとに起動される Runner の一時領域でだけ生まれ,Certificate Store に直接書き込まれた後に破棄されます. ADR 0005 は, Conductor のデータベーススキーマに秘密鍵・証明書本体・PFX・クラウド資格情報の列がそもそも存在しないこと, 秘密鍵を返す API エンドポイントが存在しないことを設計上の不変条件としています.
2 つの ID と最小権限
Conductor と Runner は別々のワークロード ID で動きます.Azure Container Apps へのデプロイでは, Bicep テンプレートが 2 つのユーザ割り当て ID をそれぞれ 1 つのカスタムロールにだけ紐付けます.Conductor の ID には DNS,Key Vault,ストレージデータのいずれの権限も与えません.
なぜ Conductor は Job を開始できないか.
Job の「開始」API は実行テンプレート(イメージ・コマンド・マウント)を丸ごと差し替えられます.
侵害された Conductor にそれを許すと,任意コードを Runner の ID で実行できてしまいます.
そのため Conductor の ID には jobs/start/action を持たせず,実行は Job 自身のスケジュールで始まり,
Conductor が差し出したジョブを取っていく形にしています(ADR 0014).
検証と認可は別物
Conductor が生成する JobSpec は,Runner にとって信頼できない入力です.Validate と
Authorize は意図的に異なる問いに答えるよう分離されており,
ドキュメントとコードコメントは前者に対して「認可」という語を決して使いません
(architecture.md「検証と認可」).
RunnerAuthorizationPolicy(許可 DNS サフィックス,ワイルドカードの可否,許可バインディング名)は Runner 自身の信頼された設定から読み込まれ,JobSpec からは決して読みません.署名は生成者を認証し改ざんとリプレイを防ぎますが,署名鍵を持つ侵害された Conductor は依然として好きな内容に署名できます.発行できる範囲を最終的に決めるのは Authorize です.Validate が保証するのは「文書が自己整合している」ことだけです.
侵害された Conductor は target.fqdn と埋め込まれた policy スナップショットを一緒に書き換えれば,
Validate をそのまま通る自己整合した JobSpec を任意の FQDN について作れます.
それを実際に制限するのは,JobSpec の外にある Runner 側の Authorize(RunnerAuthorizationPolicy)だけです.
API で指定できるのは名前だけ
API の入力でコマンド,コンテナイメージ,クラウドリソース ID,資格情報,プロバイダ設定を直接指定することは決してできません. 指定できるのは,管理者があらかじめ登録した論理バインディングの名前(DNS ラベルに似た短い文字列)だけです.
| バインディング | 何を指す名前か |
|---|---|
ExecutionBinding | Runner ジョブが実際にどこでどう動くか(ローカルプロセス/Azure Container Apps Job) |
DnsBinding | ACME DNS-01 チャレンジをどの DNS プロバイダのどのゾーンに書くか |
StoreBinding | 結果をどの Certificate Store に書くか |
AcmeBinding | Runner がどの ACME ディレクトリ・アカウント・(任意の)EAB として認証するか |
名前が何に解決されるか(実際のディレクトリ URL,資格情報,ワークロード ID)は Runner 側の起動時設定であり, Conductor がそれを見ることは決してありません.これにより,任意のインフラ値が API 入力から完全に排除されます. バインディングはすべて起動時設定から読み込まれ,実行時に作成・変更する管理 API はありません.
FQDN のラベル境界照合
FQDN はポリシーの許可サフィックスに対して ラベル単位で検査され,生の文字列サフィックスでは検査されません.
比較の前に,前後の空白除去,末尾ドット 1 つの除去,ASCII 英字の小文字化を行い,非 ASCII と xn--(IDNA)ラベルは拒否します
(architecture.md「FQDN の正規化規則」).
evil-example.ac.jp は文字としては example.ac.jp で終わりますが,境界がちょうどラベル区切りに落ちる「完全一致,または直前に .」を満たさないため,許可サフィックスの下には ない と判定されます.ワイルドカードは常に左端ラベル全体としてのみ受理され,*.ac.jp のような TLD 丸ごとのワイルドカードは拒否されます.この正規化とラベル境界照合は Conductor と Runner の両方で同一コード(internal/policy/fqdn.go)により適用されます.脅威と対策の一覧
脅威モデルが列挙する T1〜T16 の要約です.詳細な説明と実装状況はthreat-model.md「脅威」を参照してください.
| ID | 脅威 | 主な対策 |
|---|---|---|
| T1 | Conductor の侵害 | Conductor は秘密鍵/DNS/Store 資格情報を持たない.Runner 側の認可ポリシーが既定拒否.Container Apps では Conductor の ID は Job の開始ができない. |
| T2 | 転送中・保存中の JobSpec 改ざん | 厳密デコード.Ed25519 署名付きエンベロープ(JobSpec と Result の両方).改変は署名検証で拒否. |
| T3 | JobSpec のリプレイと期限 | 署名エンベロープの期限(既定 15 分)とノンス.Runner のリプレイ台帳.target 無効化・リビジョン変更時のキャンセル. |
| T4 | DNS 権限の悪用 | DnsBinding の設定検証(危険なプロバイダは拒否).実際のゾーンスコープはデプロイ側の運用上の責務として残る(残存リスク). |
| T5 | FQDN ポリシーのバイパス | 正規化+ラベル境界照合.重複 JSON キー拒否.Validate(自己整合性)と Authorize(信頼された設定による認可)の分離. |
| T6 | ログ/結果/エラーへのシークレット漏洩 | Result への生の外部出力コピーを禁止.固定テンプレートの error.summary.lego 出力の値ベース秘匿(網羅的ではない,残存リスク). |
| T7 | 同じ target の二重実行 | target ごとに最大 1 つのアクティブ run(DB の部分一意インデックス).楽観ロック.アドバイザリロックで store 書き込みを直列化. |
| T8 | サプライチェーン | lego はバージョン固定.ダイジェスト固定のベースイメージ.SBOM/provenance/署名付き証明.govulncheck.GitHub Actions のタグ固定は未対策. |
| T9 | 巨大/敵対的な文書による DoS | 64 KiB 上限.ネスト深さ上限(8 段)で超線形コストを回避. |
| T10 | Conductor/Runner の権限分離の失敗 | Bicep でレビュー可能な 2 つの ID.Conductor には DNS・Store 権限を与えない配線.ローカルランチャーの passthroughEnv は開発専用(残存リスク). |
| T11 | 無効化と purge の混同 | MVP に purge 操作なし.無効化は履歴を残す.target/run/policy の DELETE は監査で中断される. |
| T12 | テストからの本番 CA の誤用 | フェイルクローズの許可規則(ステージング/テスト以外は allowProductionCA が必須).テストは偽の lego のみを使用. |
| T13 | 意図しない呼び出し元による API 到達 | localhost-dev はループバック限定+Host/Origin/Sec-Fetch-Site 検査.oidc モードは署名・issuer・audience・ロールを検証するベアラートークン必須. |
| T14 | 盗まれた/過剰付与のベアラートークン | TLS 必須.GUI はトークンをタブのセッションストレージにのみ保持.監査ログは安定識別子+issuer で記録.失効検査は未対応(残存リスク). |
| T15 | 攻撃面としての GUI | DOM 描画のみで XSS を防止.厳格な CSP.frame-ancestors 'none'.PKCE 付き認可コードフロー. |
| T16 | (削除)入力チャネルとしての移行一覧 | 移行ツールの削除により対象がなくなりました(ADR 0025). |
残っているリスク
脅威モデルの「残存リスクと未対策事項」から,特に評価者が知っておくべきものを挙げます. 網羅的な一覧はthreat-model.mdを参照してください.
- 署名は生成者を認証するのであって,判断を認証しない.侵害された Conductor は署名鍵を持ち,好きな内容に署名できます.制限するのは Runner の認可ポリシーだけです.
- JobSpec が制御するコストのレバー.
renewBeforeDaysとkeyTypeは Runner の認可ポリシーで制限されておらず,すでに認可されたスコープ内でのコスト/レート制限の濫用余地があります. - 並行性制御は Conductor が起動する run だけを対象とする.手動や別のランチャーで起動された Runner,またはハードキルで孤児になった Runner は,同じ target に対して二重発行しうります.
- ファイルシステムの Certificate Store は開発/テスト専用です.本番は Azure Key Vault Store を使う前提です.
- DNS・ACME・Store の実際の資格情報スコープはこのリポジトリでは検証できません.Runner はバインディングがポリシーの許すものであることは確認できますが,その背後の資格情報が本当に意図したゾーンだけにスコープされているかは確認できません.
- lego 出力の秘匿は値ベースかつヒューリスティックで,網羅的ではありません.コードベース全体のログ秘匿テストスイートはまだありません.
- GitHub Actions はダイジェストではなくタグで固定されています.リリースワークフローの初回実行がその検証になります.
- localhost-dev は依然として開発モードです.ループバックのピアはすべて信頼され,ローカルユーザは誰でも管理者です.本番では
oidcモードを使う前提です. - ベアラートークンは期限切れまで有効です.失効やイントロスペクションの検査はなく,ロール割り当てはプロバイダ側の統制です.
- ローカルプロセスランチャーは Runner の資格情報を Conductor の環境に置きます.これはシングルユーザの開発ホスト向けであり,Azure Container Apps Job ランチャーには不要です.
- Container Apps デプロイの一部(同時リネームの原子性,実行停止アクション,SMB 上の flock)はまだ実デプロイで観測されていません.
脆弱性の報告
セキュリティ脆弱性が疑われる場合は,公開の issue や議論チャンネルには投稿せず,このリポジトリの GitHub のプライベート脆弱性報告を使ってください: CITS-NUE/acme-conductor → Security タブ → Report a vulnerability. これによりメンテナだけが閲覧できる非公開のアドバイザリが開かれます.
対象範囲は FQDN/ポリシーの迂回,シークレットの漏えい,ID 分離の破れ,JobSpec/Result コントラクトの迂回,
サプライチェーンの問題,コンテナ/ランタイム強化の退行です.すでに管理者権限を持っていることを前提とする発見や,
このページの「残っているリスク」にすでに挙げられている既知の未対策事項は対象外です.
詳しくはSECURITY.mdを参照してください.
本プロジェクトは専任のセキュリティチームも正式な SLA も持たない,活発に開発中の若いプロジェクト(v1 前の v0.x)であり,対応はベストエフォートです.