# 機能設計書 159-外部JWT署名

## 概要

本ドキュメントは、外部JWT署名サービスとの統合を提供する「外部JWT署名」機能の設計について記述する。

### 本機能の処理概要

**業務上の目的・背景**：Kubernetesのサービスアカウントトークンは、デフォルトではkube-apiserver内部で署名される。しかし、エンタープライズ環境やセキュリティ要件の厳しい環境では、JWT署名鍵をHSM（Hardware Security Module）や外部KMS（Key Management Service）で管理したいというニーズがある。外部JWT署名機能は、gRPCプロトコルを通じて外部の署名サービスにJWTの署名を委譲することで、鍵管理のセキュリティを向上させる。

**機能の利用シーン**：kube-apiserverがサービスアカウントトークンを発行する際に、外部の署名プラグインにJWTペイロードの署名を委譲する場合。また、OIDC Discovery用の公開鍵をプラグインから取得する場合に利用される。

**主要な処理内容**：
1. Sign RPC：JWTペイロードの署名リクエストを外部プラグインに送信し、ヘッダーと署名を受け取る
2. FetchKeys RPC：検証用公開鍵セットの取得（OIDC Discovery用）
3. Metadata RPC：プラグインのメタデータ取得（最大トークン有効期限等）
4. gRPCプロトコル定義（v1alpha1/v1）
5. Unix Domain Socketによるローカル通信

**関連システム・外部連携**：外部JWT署名プラグイン（gRPCサーバー）、kube-apiserver（gRPCクライアント）、OIDC Discoveryエンドポイント。

**権限による制御**：外部JWT署名プラグインはローカルUnix Domain Socket経由で通信するため、ノードレベルのアクセス制御が適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | バックエンドgRPCサービスのため関連画面なし |

## 機能種別

データ連携 / セキュリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| SignJWTRequest.claims | string | Yes | URL-safe Base64エンコードされたJWTペイロード | 空でないこと |
| FetchKeysRequest | - | - | パラメータなし | - |
| MetadataRequest | - | - | パラメータなし | - |

### 入力データソース

kube-apiserverからのgRPCリクエスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| SignJWTResponse.header | string | Base64エンコードされたJWTヘッダー（alg, kid, typ） |
| SignJWTResponse.signature | string | Base64エンコードされたJWT署名 |
| FetchKeysResponse.keys | []Key | 検証用公開鍵の一覧 |
| FetchKeysResponse.data_timestamp | Timestamp | 鍵データの取得時刻 |
| FetchKeysResponse.refresh_hint_seconds | int64 | 鍵更新の推奨間隔（秒） |
| MetadataResponse.max_token_expiration_seconds | int64 | 最大トークン有効期限（秒） |

### 出力先

gRPCレスポンスとしてkube-apiserverに返却

## 処理フロー

### 処理シーケンス

```
1. kube-apiserver起動時
   └─ Metadata RPCでプラグインメタデータ（max_token_expiration_seconds）を取得
2. トークン発行リクエスト
   └─ Sign RPCでJWTペイロードの署名を外部プラグインに委譲
3. 公開鍵取得
   └─ FetchKeys RPCでOIDC Discovery用公開鍵を取得（定期的＋未知keyID検出時）
```

### フローチャート

```mermaid
flowchart TD
    A[kube-apiserver起動] --> B[Metadata RPC呼び出し]
    B --> C[max_token_expiration_seconds取得]
    C --> D[トークン発行リクエスト受信]
    D --> E[JWTペイロード構築]
    E --> F[Sign RPC呼び出し]
    F --> G[header + signature受信]
    G --> H[JWT組み立て: header.payload.signature]
    H --> I[トークン返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-159-1 | JWTヘッダー制約 | headerにはalg, kid, typのみ許可。typは"JWT"必須 | Sign RPC |
| BR-159-2 | keyID必須 | kidは空でなく1024文字以下 | Sign RPC |
| BR-159-3 | 署名アルゴリズム | algはRS256, ES256, ES384, ES512のいずれか | Sign RPC |
| BR-159-4 | 最小トークン有効期限 | max_token_expiration_secondsは600秒以上 | Metadata RPC |
| BR-159-5 | max-token-expiration整合性 | --service-account-max-token-expirationがmax_token_expiration_secondsを超える場合はエラー | 起動時 |
| BR-159-6 | OIDC除外鍵 | exclude_from_oidc_discovery=trueの鍵はOIDC Discoveryに含まれない | FetchKeys RPC |
| BR-159-7 | リフレッシュ間隔 | refresh_hint_seconds <= 0は設定ミスとして扱う | FetchKeys RPC |

### 計算ロジック

Extended expiration = min(1 year, max_token_expiration_seconds)（--service-account-extend-token-expirationが有効時）

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

データベース操作なし。gRPCプロトコルによるプロセス間通信のみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | gRPC接続エラー | プラグインプロセスが停止 | プラグインプロセスの再起動 |
| - | 署名エラー | プラグインの署名処理失敗 | プラグインのログを確認 |
| - | 設定ミス | max_token_expiration_seconds < 600 | プラグイン設定を修正 |
| - | 起動エラー | max-token-expiration > max_token_expiration_seconds | フラグ値を修正 |

### リトライ仕様

gRPCレベルのリトライポリシーはクライアント実装に依存。FetchKeysは未知keyID検出時にオンデマンドで呼び出される。

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

gRPCの単一RPC呼び出しが1トランザクション。複数RPC間のトランザクションはない。

## パフォーマンス要件

Sign RPCはトークン発行のクリティカルパスにあるため、低レイテンシ（ミリ秒オーダー）が求められる。FetchKeysはrefresh_hint_secondsで指定された間隔で呼び出される。

## セキュリティ考慮事項

- 署名鍵はプラグインプロセス内で管理され、kube-apiserverには露出しない
- Unix Domain Socketによるローカル通信でネットワーク攻撃を回避
- PKIX形式で公開鍵のみが返却される
- HSM/KMS統合によるハードウェアレベルの鍵保護が可能

## 備考

- v1alpha1とv1の2つのAPIバージョンが存在
- Unix Domain Socketベースの通信
- ExternalJWTSignerサービスとしてgRPCで定義

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: gRPCプロトコル定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | api.proto (v1alpha1) | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api.proto` | gRPCサービスとメッセージ定義 |
| 1-2 | api.proto (v1) | `staging/src/k8s.io/externaljwt/apis/v1/api.proto` | v1のプロトコル定義 |

**読解のコツ**: **27-48行目**でExternalJWTSignerサービスが3つのRPC（Sign, FetchKeys, Metadata）を定義。**50-68行目**でSignJWTRequest/Responseを定義（claims入力、header+signature出力）。**70-83行目**でFetchKeysResponseが公開鍵リスト・タイムスタンプ・リフレッシュヒントを返す。**85-99行目**でKey構造体にkey_id、key（PKIX形式）、exclude_from_oidc_discoveryフラグが定義されている。**103-114行目**でMetadataResponseにmax_token_expiration_secondsが定義されている。

#### Step 2: 生成されたgRPCコードを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | api_grpc.pb.go | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api_grpc.pb.go` | 生成されたgRPCクライアント/サーバーインターフェース |
| 2-2 | api.pb.go | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api.pb.go` | 生成されたProtobufメッセージ型 |

### プログラム呼び出し階層図

```
kube-apiserver
    |
    +-- ExternalJWTSigner (gRPCクライアント)
           |
           +-- Metadata() (起動時1回)
           |      └─ MetadataResponse (max_token_expiration_seconds)
           |
           +-- Sign() (トークン発行時)
           |      └─ SignJWTResponse (header + signature)
           |
           +-- FetchKeys() (定期的 + 未知keyID時)
                  └─ FetchKeysResponse (keys + timestamp + refresh_hint)
```

### データフロー図

```
[入力]                       [処理]                          [出力]

JWTペイロード ──────> Sign RPC ──────────────> JWTヘッダー + 署名
(Base64エンコード)     (外部プラグイン)           (Base64エンコード)

(なし) ────────────> FetchKeys RPC ─────────> 公開鍵セット
                     (外部プラグイン)            (PKIX形式)

(なし) ────────────> Metadata RPC ──────────> max_token_expiration
                     (外部プラグイン)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| api.proto (v1alpha1) | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api.proto` | プロトコル定義 | v1alpha1のgRPCサービス定義 |
| api.proto (v1) | `staging/src/k8s.io/externaljwt/apis/v1/api.proto` | プロトコル定義 | v1のgRPCサービス定義 |
| api_grpc.pb.go (v1alpha1) | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api_grpc.pb.go` | 生成コード | gRPCクライアント/サーバーコード |
| api.pb.go (v1alpha1) | `staging/src/k8s.io/externaljwt/apis/v1alpha1/api.pb.go` | 生成コード | Protobufメッセージ型 |
| api_grpc.pb.go (v1) | `staging/src/k8s.io/externaljwt/apis/v1/api_grpc.pb.go` | 生成コード | v1 gRPCコード |
| api.pb.go (v1) | `staging/src/k8s.io/externaljwt/apis/v1/api.pb.go` | 生成コード | v1 Protobufメッセージ型 |
| docs.go | `staging/src/k8s.io/externaljwt/docs.go` | ソース | パッケージドキュメント |
