# バッチ設計書 37-X509IssuerCrlCheckWorker

## 概要

本ドキュメントは、X509証明書の失効リスト（CRL）をチェックするバッチ処理「X509IssuerCrlCheckWorker」の設計書です。

### 本バッチの処理概要

**業務上の目的・背景**：GitLabではX.509証明書を使用したコミット署名の検証機能を提供しています。証明書は認証局（CA）によって発行されますが、セキュリティ上の理由で失効される場合があります。本バッチは、各証明書発行者（Issuer）が公開するCRL（Certificate Revocation List）を定期的にダウンロードし、GitLab内で管理されている証明書が失効していないかをチェックします。失効した証明書のステータスを更新することで、セキュリティを維持し、信頼性の低い署名を識別できるようにします。

**バッチの実行タイミング**：日次で午前1時30分に実行されます。cronスケジュールは`30 1 * * *`で設定されています。

**主要な処理内容**：
1. CRL URLを持つすべてのX509 Issuer（証明書発行者）を取得
2. 各Issuerについて、CRL URLからCRLをダウンロード
3. GitLab内の該当Issuerの証明書のシリアル番号リストを取得
4. CRL内の失効リストとシリアル番号を照合
5. 失効した証明書のステータスを「revoked」に更新

**前後の処理との関連**：独立したセキュリティ監査バッチとして機能し、コミット署名検証機能と連携します。

**影響範囲**：x509_issuersテーブル、x509_certificatesテーブル、外部CRL URLへのHTTPリクエスト

## バッチ種別

データクレンジング / セキュリティ監査

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 日次 |
| 実行時刻 | 01:30 |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Sidekiq稼働 | Sidekiqワーカーが正常に稼働していること |
| データベース接続 | メインデータベースへの接続が確立されていること |
| 外部接続 | CRL URLへのHTTPアクセスが可能であること |

### 実行可否判定

- 特別な実行可否判定ロジックは実装されていません
- CRL URLが設定されていないIssuerはスキップされます

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | パラメータなしで実行 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| x509_issuers | DB | CRL URLを持つ証明書発行者 |
| x509_certificates | DB | 各発行者に紐づく証明書 |
| CRL URL | HTTP | 各発行者が公開するCRL（DER形式） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| x509_certificates | DB | certificate_statusを'revoked'に更新 |
| ログ | Stdout | 失効証明書情報、CRLダウンロードエラー |

### 出力ファイル仕様

ファイル出力はありません。

## 処理フロー

### 処理シーケンス

```
1. バッチ開始
   └─ Gitlab::GitLoggerを初期化
2. Issuer取得
   └─ X509Issuer.with_crl_urlで対象発行者を取得
3. Issuerループ（find_each）
   └─ 各発行者について以下を実行
4. CRLダウンロード
   └─ HTTP GETでCRL URLからCRLを取得
5. CRLパース
   └─ OpenSSL::X509::CRL.newでパース
6. 証明書シリアル番号取得
   └─ X509Certificate.serial_numbers(issuer)で取得
7. 失効シリアル番号照合
   └─ CRL内失効リストとの積集合を計算
8. 証明書更新（1,000件ずつ）
   └─ certificate_statusを'revoked'に更新
9. ログ出力
   └─ 失効証明書の詳細情報をログ記録
10. バッチ終了
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[GitLoggerを初期化]
    B --> C[CRL URLを持つIssuerを取得]
    C --> D{Issuerが存在?}
    D -->|なし| E[バッチ終了]
    D -->|あり| F[CRLをダウンロード]
    F --> G{ダウンロード成功?}
    G -->|いいえ| H[警告ログ出力]
    H --> I{次のIssuerあり?}
    G -->|はい| J[CRLをパース]
    J --> K{パース成功?}
    K -->|いいえ| H
    K -->|はい| L[証明書シリアル番号取得]
    L --> M{証明書が存在?}
    M -->|なし| I
    M -->|あり| N[失効シリアル番号照合]
    N --> O{失効証明書あり?}
    O -->|なし| I
    O -->|あり| P[1,000件ずつ処理]
    P --> Q[失効ログ出力]
    Q --> R[certificate_statusを更新]
    R --> I
    I -->|あり| F
    I -->|なし| E
```

## データベース操作仕様

### 操作別データベース影響一覧

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Issuer取得 | x509_issuers | SELECT | CRL URLを持つ発行者を取得 |
| シリアル番号取得 | x509_certificates | SELECT | 各発行者の証明書シリアル番号を取得 |
| ステータス更新 | x509_certificates | UPDATE | 失効証明書のステータスを更新 |

### テーブル別操作詳細

#### x509_issuers

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, subject, crl_url | crl_url IS NOT NULL | with_crl_urlスコープ |

#### x509_certificates

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | serial_number | x509_issuer_id = issuer.id | シリアル番号のリスト取得 |
| UPDATE | certificate_status | 'revoked' | serial_numberがCRL内に存在 かつ certificate_status = 'good' |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | HTTP通信エラー | CRL URLへの接続失敗 | 警告ログを出力して次のIssuerへスキップ |
| - | CRLパースエラー | CRLの形式が不正 | 警告ログを出力して次のIssuerへスキップ |
| - | HTTPステータス非200 | CRL取得失敗 | 警告ログを出力して次のIssuerへスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | すべての例外（個別Issuerエラーは内部でハンドリング） |

### 障害時対応

- idempotent!が設定されているため、再実行しても問題なし
- 個別のCRLダウンロードエラーは他のIssuerに影響を与えない
- worker_has_external_dependencies!により外部依存が明示されている

## トランザクション仕様

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 1,000件単位のバッチ更新 |
| コミットタイミング | update_all実行時（暗黙コミット） |
| ロールバック条件 | update_all失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | Issuer数 × 証明書数（環境依存） |
| 目標処理時間 | 特に制限なし（urgency: low） |
| メモリ使用量上限 | 制限なし（find_eachにより制御） |

## 排他制御

- CronjobQueueにより同時実行は自動的に防止される
- idempotent!により冪等性が保証される

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | バッチ開始時 | Sidekiq標準ログ |
| 失効ログ | 証明書失効時 | id, email, subject, serial_number, issuer情報 |
| 警告ログ | CRLダウンロード失敗時 | issuer id, subject, crl_url |
| 警告ログ | CRLパース失敗時 | issuer id, subject, crl_url |
| 終了ログ | バッチ終了時 | Sidekiq標準ログ |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ジョブ失敗 | 1件以上 | Sidekiqダッシュボード |
| CRLダウンロード失敗 | 警告ログで記録 | Gitlab::GitLogger |

## 備考

- feature_category: source_code_management
- data_consistency: always
- urgency: low
- worker_has_external_dependencies!: 外部CRL URLへのHTTPアクセスが必要
- 失効証明書のバッチ更新: 1,000件ずつ（each_slice(1000)）
- Gitlab::HTTP.try_getを使用した安全なHTTPリクエスト
