diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..31aa78a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Helm チャートのテキストは常に LF(CRLF はブロックスカラー等で問題を起こしうる) +* text=auto eol=lf +*.png binary +*.tgz binary diff --git a/.gitea/workflows/helm-release.yaml b/.gitea/workflows/helm-release.yaml new file mode 100644 index 0000000..8f0289a --- /dev/null +++ b/.gitea/workflows/helm-release.yaml @@ -0,0 +1,106 @@ +# .gitea/workflows/helm-release.yaml +# 汎用Helmチャート公開ワークフロー(全リポジトリ共通) + +name: Helm Chart Release + +on: + push: + branches: + - main + - master + workflow_dispatch: + +env: + REGISTRY_URL: https://git.cafepieters.com + OWNER: helmchart + +jobs: + release-chart: + runs-on: ubuntu-latest + steps: + - name: Checkout + run: | + echo "🔄 Cloning repository..." + git clone --depth 1 $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git . + echo "✅ Repository cloned" + + - name: Install Helm + run: | + echo "📦 Installing Helm..." + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + helm version + echo "✅ Helm installed" + + - name: Get Chart Info + id: chart_info + run: | + # Chart.yamlからチャート名とバージョンを取得 + CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}') + CHART_VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}') + + echo "Chart Name: ${CHART_NAME}" + echo "Chart Version: ${CHART_VERSION}" + + # GitHub Actionsの環境変数として設定 + echo "CHART_NAME=${CHART_NAME}" >> $GITHUB_ENV + echo "CHART_VERSION=${CHART_VERSION}" >> $GITHUB_ENV + + - name: Validate Chart + run: | + echo "🔍 Validating Helm chart..." + helm lint . + echo "✅ Chart validation passed" + + - name: Package Chart + run: | + echo "📦 Packaging Helm chart..." + helm package . + + CHART_FILE=$(ls *.tgz) + echo "✅ Packaged: ${CHART_FILE}" + echo "CHART_FILE=${CHART_FILE}" >> $GITHUB_ENV + + - name: Publish to Gitea Package Registry + run: | + echo "🚀 Publishing ${CHART_FILE} to Gitea Package Registry..." + + curl --fail-with-body \ + -u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" \ + -X POST \ + --upload-file "${CHART_FILE}" \ + "${REGISTRY_URL}/api/packages/${OWNER}/helm/api/charts" + + echo "✅ Chart published successfully!" + + - name: Summary + if: success() + run: | + echo "================================" + echo "✅ Deployment Successful!" + echo "================================" + echo "Repository: ${{ github.repository }}" + echo "Chart Name: ${CHART_NAME}" + echo "Chart Version: ${CHART_VERSION}" + echo "Chart File: ${CHART_FILE}" + echo "Branch: ${{ github.ref }}" + echo "Commit: ${{ github.sha }}" + echo "================================" + echo "" + echo "📦 Install with:" + echo "helm repo add cafepieters ${REGISTRY_URL}/api/packages/${OWNER}/helm" + echo "helm repo update" + echo "helm install my-${CHART_NAME} cafepieters/${CHART_NAME}" + + - name: Error Report + if: failure() + run: | + echo "================================" + echo "❌ Deployment Failed!" + echo "================================" + echo "Check the logs above for details" + echo "Common issues:" + echo "- Missing Chart.yaml" + echo "- Invalid Helm chart structure" + echo "- Missing REGISTRY_USER or REGISTRY_TOKEN secrets" + echo "- Insufficient permissions on Personal Access Token" + echo "================================" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea2a9a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Helm パッケージ成果物 +*.tgz +charts/ + +# 平文の資格情報を含む values ファイル(絶対にコミットしない) +secrets.values.yaml +secrets.*.yaml +*.secret.yaml +*.local.yaml +values.local.yaml + +# SOPS 復号の一時ファイル +*.dec +*.dec.yaml + +# OS / エディタ +.DS_Store +*.swp diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3024930 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,85 @@ +# CLAUDE.md — secret-manager Helm Chart + +## リポジトリ概要 + +クラスター内の複数プロジェクトで横断的に使う「認証情報」を 1 つの Helm チャートで +一元管理する。外部メールの SMTP/IMAP 設定・API キー・汎用資格情報を定義し、 +指定した複数ネームスペースへ同一の Kubernetes Secret として配布する。 + +**設計目標**: 認証情報の変更が発生したら、このチャートの values を更新して +`helm upgrade` するだけで、クラスター上の全プロジェクトに反映される。 + +- **Gitea リポジトリ**: `ssh://git@192.168.9.65/helmchart/secret-manager` +- **Helm リポジトリ**: `https://git.cafepieters.com/api/packages/helmchart/helm` + +## 作業ルール(必読) + +1. **機能を追加・変更したら、必ず `README.md` を更新してから作業完了とする。** + - 追加・変更した機能・パラメータ・挙動を README.md に反映する。 +2. **変更は必ず Git Commit / Push すること。** + - 作業単位ごとにコミットし、`origin(main)` へ push するまでが作業完了。 +3. **コミットメッセージに Claude クレジット(`Co-Authored-By` 等)を追記することは禁止。** + - `user.name = Claude` でコミットされるため、ユーザー名で判別可能。 +4. `git pull` で取り込まれた変更は常に尊重する。revert して push することは禁止。 + 不適切に見える場合はユーザーに改善提案を提示し、承認を得てから対応する。 + +## バージョン方針 + +- このチャートには「主アプリ」となる外部プロダクトが存在しない(チャート自身がアプリ)。 + そのため `Chart.yaml` の `version` / `appVersion` は **チャート独自に SemVer で採番**する。 + (他チャートの「主アプリのバージョンと同一」ルールは適用対象外) +- 破壊的変更 = メジャー、機能追加 = マイナー、修正 = パッチ。 +- 改修作業で臨時の手動リリースが必要な場合は末尾にアルファベットを付与する + (例: `0.2.0` → `0.2.0-a`)。 + +## リリースフロー + +- `main` への push で `.gitea/workflows/helm-release.yaml` が発火し、 + `helm lint` → `helm package` → Gitea Package Registry へ発行する。 +- 正式リリース時は `Chart.yaml` のバージョンを上げ、Git タグと Gitea リリースも作成する。 + +## 必要な Gitea Secrets + +| シークレット名 | 用途 | +|---|---| +| `GITEA_TOKEN` | Gitea API(リリース作成・タグ操作) | +| `REGISTRY_USER` | Gitea Package Registry ユーザー名 | +| `REGISTRY_TOKEN` | Gitea Package Registry トークン | + +## Git Configuration (local) + +| 項目 | 値 | +|---|---| +| user.name | Claude | +| user.email | claude@cafepieters.com | +| core.sshCommand | `ssh -i P:/Claude/.ssh/id_claude -o StrictHostKeyChecking=no` | + +## 実行環境 + +- **Helm はローカル未インストール**。テンプレートのレンダリング確認は CI(`helm lint`)に依存する。 + ローカルでは YAML 構文・Go テンプレート構文を目視で慎重に確認すること。 +- OS: Windows 11 Pro + +## アーキテクチャ要点 + +- `templates/_helpers.tpl` の `secret-manager.secret` が中核。 + 1 論理 Secret を受け取り、実効ネームスペースごとに複製した Secret マニフェストを出力する。 +- 配布先の決定順: + 1. エントリの `namespacesOverride`(非空ならそれを使用) + 2. `distribution.reflector.enabled` が true → リリース先ネームスペースのみに作成し reflector アノテーション付与 + 3. それ以外 → `distribution.namespaces` (+ `includeReleaseNamespace`) を重複排除した一覧 +- Helm はリリースマニフェストに含まれる別ネームスペースの Secret も追跡するため、 + `namespaces` からネームスペースを外して `helm upgrade` するとそのコピーは削除される。 +- 資格情報は `stringData` で書き込む(テンプレート側で全値を文字列へ強制)。 + +## チャート改修履歴 + +### 初版設計・実装(2026-08-31, v0.1.0) + +空リポジトリから初期実装。`distribution`(namespaces 複製 / reflector / reloader)、 +`common`(namePrefix・labels・annotations・immutable)、`mail.providers`(SMTP/IMAP/POP3 + +`smtp.env` + 任意の `msmtprc`)、`api.credentials`、`generic.secrets`(任意 type)を実装。 +`values.schema.json`、`examples/`、`.gitea/workflows/helm-release.yaml` を追加。 + +**対象ファイル**: `Chart.yaml`, `values.yaml`, `values.schema.json`, `templates/*`, +`examples/*`, `.gitea/workflows/helm-release.yaml`, `README.md`, `.gitignore`, `CLAUDE.md` diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..08fc057 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,22 @@ +apiVersion: v2 +name: secret-manager +description: >- + クラスター横断で使う認証情報(外部メールのSMTP/IMAP設定・APIキー・汎用資格情報)を + 一元管理し、複数ネームスペースへ同一Secretとして配布するHelmチャート。 +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - secrets + - credentials + - smtp + - mail + - api-keys + - configuration +home: https://git.cafepieters.com/helmchart/secret-manager +sources: + - https://git.cafepieters.com/helmchart/secret-manager +maintainers: + - name: Claude + email: claude@cafepieters.com +icon: https://kubernetes.io/images/favicon.png diff --git a/README.md b/README.md index e7b9537..c46b9df 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,332 @@ # secret-manager -Helm Chart to manage secrets in Kubernetes Cluster. \ No newline at end of file +クラスター内の複数プロジェクトで横断的に使う **認証情報(Secrets)を一元管理** する Helm チャートです。 + +外部メールの SMTP/IMAP 設定、外部 API の認証キー、その他の汎用資格情報を 1 箇所(この +チャートの `values.yaml`)で定義し、指定した **複数のネームスペースへ同一の Kubernetes +Secret として配布** します。 + +> **設計目標** — 認証情報の変更が発生したら、このチャートの values を更新して +> `helm upgrade` するだけで、クラスター上の全プロジェクトに反映される。 + +- Gitea リポジトリ: `ssh://git@192.168.9.65/helmchart/secret-manager` +- Helm リポジトリ: `https://git.cafepieters.com/api/packages/helmchart/helm` + +--- + +## 目次 + +- [なぜ必要か](#なぜ必要か) +- [仕組み](#仕組み) +- [セキュリティモデル](#セキュリティモデル) +- [インストール](#インストール) +- [values リファレンス](#values-リファレンス) + - [distribution — 配布先](#distribution--配布先) + - [common — 共通設定](#common--共通設定) + - [mail — 外部メール接続情報](#mail--外部メール接続情報) + - [api — API 認証情報](#api--api-認証情報) + - [generic — 汎用 Secret](#generic--汎用-secret) +- [生成される Secret の構造](#生成される-secret-の構造) +- [他プロジェクトからの参照方法](#他プロジェクトからの参照方法) +- [代表的なメールプロバイダの設定値](#代表的なメールプロバイダの設定値) +- [変更の反映(伝播)とライフサイクル](#変更の反映伝播とライフサイクル) +- [reflector / Reloader 連携](#reflector--reloader-連携) +- [トラブルシューティング](#トラブルシューティング) + +--- + +## なぜ必要か + +Kubernetes の Secret は **ネームスペース単位** です。同じ SMTP パスワードや API キーを +10 個のプロジェクトで使う場合、通常は 10 個のネームスペースそれぞれに Secret を作り、 +変更のたびに 10 箇所を更新することになります。 + +このチャートは「認証情報の定義」を 1 リポジトリに集約し、配布はチャートに任せます。 +パスワードローテーションやエンドポイント変更は **このチャートの 1 回の更新** で全体に +行き渡ります。 + +## 仕組み + +``` + 値の定義(1箇所) + ┌──────────────────────────────┐ + │ secret-manager / values.yaml│ + │ mail.providers.gmail │ + │ api.credentials.stripe │ + │ generic.secrets.registry │ + └───────────────┬──────────────┘ + │ helm upgrade + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ns: web ns: batch ns: ml + Secret cred-gmail Secret cred-... Secret cred-... + Secret cred-stripe ... +``` + +- 1 つの論理エントリ(`mail.providers.gmail` など)から、配布先ネームスペースの数だけ + **同名の Secret** が生成されます(`common.namePrefix` + エントリ名)。 +- 配布方式は 2 通り。既定は **Helm による複製**(外部依存なし)。オプションで + **kubernetes-reflector による自動同期** も選べます。 + +## セキュリティモデル + +**この `values.yaml` には平文の資格情報を書かないでください。Git にコミットしないでください。** + +Kubernetes の Secret は etcd 上では base64 で「エンコード」されているだけで「暗号化」では +ありません。同様に Helm values に平文で書けば、それが Git・CI ログ・Helm リリース履歴 +(`helm get values`)に残ります。実値は次のいずれかで注入します。 + +| 方法 | 用途 | 例 | +|---|---|---| +| **CI から `--set` / `--set-file`** | Gitea Actions が Gitea Secrets を注入(推奨) | `--set mail.providers.gmail.smtp.password="$GMAIL_APP_PASSWORD"` | +| **SOPS 暗号化 values** | 暗号化したまま Git 管理したい場合(`helm secrets` プラグイン) | `helm secrets upgrade -f secrets.enc.yaml ...` | +| **git 管理外の values ファイル** | 手元運用 | `-f secrets.values.yaml`(`.gitignore` 済み) | + +追加の推奨: + +- **etcd の保存時暗号化(EncryptionConfiguration)** をクラスターで有効化する。 +- 配布先を必要なネームスペースだけに絞る(`distribution.namespaces` を最小化、または + `reflector.namespaceRegex` で限定)。`default` への配布は避ける。 +- 変更頻度が低い資格情報は `common.immutable: true` にして誤更新を防ぐ。 +- Secret を参照する RBAC(`get secrets`)を各ネームスペースで最小権限に保つ。 +- このチャートは Secret 以外のリソース(Deployment 等)を一切作りません。監査が容易です。 + +## インストール + +```bash +# Helm リポジトリ経由 +helm repo add cafepieters https://git.cafepieters.com/api/packages/helmchart/helm +helm repo update +helm upgrade --install secrets cafepieters/secret-manager \ + -n secret-manager --create-namespace \ + -f my-secrets.values.yaml +``` + +```bash +# ローカルのチャートディレクトリから +helm upgrade --install secrets ./secret-manager \ + -n secret-manager --create-namespace \ + -f examples/mail-providers.values.yaml \ + --set mail.providers.gmail.smtp.password="$GMAIL_APP_PASSWORD" \ + --set mail.providers.m365.smtp.password="$M365_PASSWORD" +``` + +> 配布先ネームスペース(`distribution.namespaces`)は **事前に存在している必要があります**。 + +## values リファレンス + +### distribution — 配布先 + +| キー | 型 | 既定 | 説明 | +|---|---|---|---| +| `distribution.namespaces` | `[]string` | `["default"]` | 同一 Secret を複製配置するネームスペース。リストから外して `helm upgrade` すると、そのコピーは削除される。 | +| `distribution.includeReleaseNamespace` | `bool` | `true` | リリース先ネームスペース(`-n` の値)も配布対象へ自動追加する。 | +| `distribution.reflector.enabled` | `bool` | `false` | kubernetes-reflector による自動同期を使う。有効時、Secret はリリース先のみに作成し reflector アノテーションを付与する。 | +| `distribution.reflector.namespaceRegex` | `string` | `".*"` | 同期先ネームスペースを絞る正規表現。例 `"prod-.*\|shared-services"`。 | +| `distribution.reloader.enabled` | `bool` | `false` | 各 Secret に `reloader.stakater.com/match: "true"` を付与。参照側 Deployment に `reloader.stakater.com/auto: "true"` があれば Secret 変更時に自動ローリング再起動される。 | + +### common — 共通設定 + +| キー | 型 | 既定 | 説明 | +|---|---|---|---| +| `common.namePrefix` | `string` | `"cred-"` | 生成される Secret 名のプレフィックス。`cred-` + `gmail` → `cred-gmail`。 | +| `common.labels` | `map` | `{}` | 全 Secret に付与する追加ラベル。 | +| `common.annotations` | `map` | `{}` | 全 Secret に付与する追加アノテーション。 | +| `common.immutable` | `bool` | `false` | Secret を `immutable: true` にする。値変更には一度 `kubectl delete secret` が必要。 | + +### mail — 外部メール接続情報 + +`mail.providers.<名前>` ごとに 1 つの Secret を生成します。**パスワードだけでなく +SMTP/IMAP/POP3 のサーバ情報も一元管理** できます。 + +| キー | 型 | 必須 | 説明 | +|---|---|---|---| +| `enabled` | `bool` | ✓ | `true` で生成対象。 | +| `smtp.host` | `string` | ✓ | SMTP サーバホスト名。 | +| `smtp.port` | `int` | | 既定 `587`。 | +| `smtp.security` | `string` | | `none` / `starttls`(既定) / `tls`。 | +| `smtp.username` | `string` | | 認証ユーザー名。空なら認証なし。 | +| `smtp.password` | `string` | | 認証パスワード。`--set` / SOPS で注入。 | +| `smtp.from` | `string` | | 既定送信元アドレス。省略時は `username`。 | +| `smtp.fromName` | `string` | | 送信者表示名。 | +| `smtp.replyTo` | `string` | | Reply-To アドレス。 | +| `smtp.authMethod` | `string` | | `plain`(既定) / `login` / `cram-md5`。 | +| `imap.host` ほか | | | 受信 (IMAP) 情報。`host` があれば `IMAP_*` キーを追加。`username`/`password` 省略時は SMTP の値を流用。既定ポート `993`。 | +| `pop3.host` ほか | | | 受信 (POP3) 情報。同上。既定ポート `995`。 | +| `renderMsmtprc` | `bool` | | `true` で Secret に `msmtprc`(msmtp / PHP sendmail 用設定)を含める。 | +| `namespacesOverride` | `[]string` | | このエントリだけ別のネームスペースへ配布する。 | +| `annotations` | `map` | | このエントリの Secret だけに付ける追加アノテーション。 | + +### api — API 認証情報 + +`api.credentials.<名前>` ごとに 1 つの Opaque Secret を生成します。`data` 配下のキーが +そのまま Secret のキーになります(`envFrom` でまとめて注入しやすい形)。 + +| キー | 型 | 必須 | 説明 | +|---|---|---|---| +| `enabled` | `bool` | ✓ | `true` で生成対象。 | +| `data` | `map` | ✓ | 1 つ以上のキー/値。値は文字列化される。 | +| `namespacesOverride` | `[]string` | | 配布先の上書き。 | +| `annotations` | `map` | | 追加アノテーション。 | + +### generic — 汎用 Secret + +`generic.secrets.<名前>` ごとに 1 つの Secret を生成します。**任意の Secret type** を +指定できます(`kubernetes.io/dockerconfigjson`、`kubernetes.io/tls`、 +`kubernetes.io/basic-auth` など)。 + +| キー | 型 | 必須 | 説明 | +|---|---|---|---| +| `enabled` | `bool` | ✓ | `true` で生成対象。 | +| `type` | `string` | | Secret の type。既定 `Opaque`。 | +| `data` | `map` | ✓ | 1 つ以上のキー/値。`kubernetes.io/tls` なら `tls.crt` / `tls.key` を入れる。 | +| `namespacesOverride` | `[]string` | | 配布先の上書き。 | +| `annotations` | `map` | | 追加アノテーション。 | + +## 生成される Secret の構造 + +### mail エントリ(例: `cred-gmail`) + +``` +SMTP_HOST=smtp.gmail.com +SMTP_PORT=587 +SMTP_SECURITY=starttls +SMTP_USERNAME=no-reply@example.com +SMTP_PASSWORD=******** +SMTP_FROM=no-reply@example.com +SMTP_FROM_NAME=Example +SMTP_REPLY_TO= +SMTP_AUTH_METHOD=login +IMAP_HOST=imap.gmail.com # imap 定義時のみ +IMAP_PORT=993 +IMAP_SECURITY=tls +IMAP_USERNAME=no-reply@example.com +IMAP_PASSWORD=******** +smtp.env # ↓ source して使える env ファイル + export SMTP_HOST="smtp.gmail.com" + export SMTP_PORT="587" + ... +msmtprc # renderMsmtprc: true のとき。msmtp / PHP sendmail 用 +``` + +### api / generic エントリ + +`data` に書いたキーがそのまま Secret のキーになります。 + +## 他プロジェクトからの参照方法 + +配布先ネームスペースにいる Deployment から、通常の Secret として参照します。 +**このチャートに依存関係を張る必要はありません**(Secret が既にそのネームスペースにある)。 + +```yaml +# まとめて環境変数へ(api / generic 向き) +envFrom: + - secretRef: + name: cred-stripe + +# 個別キーを参照(mail 向き) +env: + - name: SMTP_HOST + valueFrom: { secretKeyRef: { name: cred-gmail, key: SMTP_HOST } } + - name: SMTP_PASSWORD + valueFrom: { secretKeyRef: { name: cred-gmail, key: SMTP_PASSWORD } } + +# env ファイル / msmtprc をファイルとしてマウント +volumes: + - name: smtp + secret: + secretName: cred-gmail + items: + - { key: smtp.env, path: smtp.env } + - { key: msmtprc, path: msmtprc, mode: 0600 } +volumeMounts: + - { name: smtp, mountPath: /etc/smtp, readOnly: true } +``` + +```sh +# コンテナ起動時に env ファイルを読み込む例 +set -a; . /etc/smtp/smtp.env; set +a +``` + +`helmchart/phpfpm` チャートと組み合わせる場合は、phpfpm 側の `phpEnv` や既存の SMTP +セクションに、ここで配布した `cred-*` Secret を `secretKeyRef` で渡してください。 + +## 代表的なメールプロバイダの設定値 + +| プロバイダ | SMTP host | port | security | username | 備考 | +|---|---|---|---|---|---| +| Gmail / Google Workspace | `smtp.gmail.com` | 587 | starttls | メールアドレス | 2 段階認証 + **アプリ パスワード**が必要 | +| Microsoft 365 | `smtp.office365.com` | 587 | starttls | メールアドレス | SMTP AUTH の有効化が必要な場合あり | +| SendGrid | `smtp.sendgrid.net` | 587 | starttls | `apikey`(固定) | password が API キー本体 | +| Amazon SES | `email-smtp..amazonaws.com` | 587 | starttls | SES SMTP 認証情報 | IAM キーとは別物。要リージョン | +| Mailgun | `smtp.mailgun.org` | 587 | starttls | `postmaster@mg.` | | +| 社内 SMTP リレー | クラスター内 Service 等 | 25 | none | (なし) | 認証・TLS なし構成 | + +IMAP 受信の代表値: Gmail `imap.gmail.com:993/tls`、M365 `outlook.office365.com:993/tls`。 + +そのまま使えるサンプル: [`examples/mail-providers.values.yaml`](examples/mail-providers.values.yaml)、 +[`examples/api-and-generic.values.yaml`](examples/api-and-generic.values.yaml)、 +[`examples/reflector.values.yaml`](examples/reflector.values.yaml)。 + +## 変更の反映(伝播)とライフサイクル + +- **値の変更** — values を更新して `helm upgrade` すると、全配布先の Secret が同時に + 更新されます。実行中の Pod の環境変数は**再起動するまで変わりません**。ファイル + マウント(`smtp.env` 等)は数十秒〜で更新されます。自動再起動が必要なら + `distribution.reloader.enabled: true` + 参照側の Reloader アノテーションを使います。 +- **配布先の追加** — `distribution.namespaces` に足して `helm upgrade`。 +- **配布先の削除** — `distribution.namespaces` から外して `helm upgrade` すると、 + そのネームスペースのコピーは Helm が削除します。 +- **エントリの削除** — `enabled: false` にするか、エントリごと削除して `helm upgrade`。 + 全ネームスペースから当該 Secret が削除されます。 +- **アンインストール** — `helm uninstall` で、このチャートが作った全ネームスペースの + Secret が削除されます。 + +## reflector / Reloader 連携 + +### kubernetes-reflector(配布の自動化) + +ネームスペースが頻繁に増減する環境では、`distribution.namespaces` の列挙が手間になります。 +[emberstack/kubernetes-reflector](https://github.com/emberstack/kubernetes-reflector) を +クラスターに導入し、`distribution.reflector.enabled: true` にすると、Secret はリリース先 +ネームスペースにのみ作られ、`namespaceRegex` に一致する全ネームスペースへ reflector が +複製・追従します。 + +```yaml +distribution: + namespaces: [] + reflector: + enabled: true + namespaceRegex: "prod-.*|shared-services" +``` + +### stakater/Reloader(参照側の自動再起動) + +[stakater/Reloader](https://github.com/stakater/Reloader) 導入済みなら +`distribution.reloader.enabled: true` で各 Secret に `reloader.stakater.com/match: "true"` +が付きます。参照側 Deployment に `reloader.stakater.com/auto: "true"` を付けておくと、 +Secret 更新時に自動でローリング再起動されます。 + +## トラブルシューティング + +| 症状 | 原因 / 対処 | +|---|---| +| `namespaces "xxx" not found` で install 失敗 | 配布先ネームスペースは事前作成が必要。`kubectl create namespace xxx`。 | +| `helm upgrade` しても Pod の挙動が変わらない | Pod の環境変数は再起動まで不変。Pod を再起動するか Reloader 連携を使う。 | +| reflector 有効なのに他 ns に複製されない | reflector コントローラー未導入 / `namespaceRegex` 不一致 / 対象 ns がまだ無い。 | +| `cannot convert int64 to string` 等の型エラー | 通常は発生しない(テンプレートで文字列化済み)。values の該当箇所を確認し Issue へ。 | +| Secret に平文が見える | Secret は設計上、参照側から平文で読める。RBAC で `get secrets` を最小化し、etcd 保存時暗号化を有効化する。 | +| `helm lint` で `metadata.namespace` 警告 | 意図的(クロス ns 配布)。エラーではない。 | + +--- + +## 変更履歴 + +各リリースの詳細は `CLAUDE.md` の「チャート改修履歴」を参照。 + +### v0.1.0(2026-08-31) + +- 初版。`distribution`(namespaces 複製 / reflector / reloader)、`common`、 + `mail.providers`(SMTP/IMAP/POP3 + `smtp.env` + 任意 `msmtprc`)、`api.credentials`、 + `generic.secrets`(任意 type)を実装。`values.schema.json`・`examples/`・ + Gitea リリースワークフローを同梱。 diff --git a/examples/api-and-generic.values.yaml b/examples/api-and-generic.values.yaml new file mode 100644 index 0000000..337e2b3 --- /dev/null +++ b/examples/api-and-generic.values.yaml @@ -0,0 +1,56 @@ +# ========================================================================= +# API 認証情報 / 汎用 Secret の設定例 +# ------------------------------------------------------------------------- +# helm upgrade --install secrets ./secret-manager -n secret-manager \ +# -f examples/api-and-generic.values.yaml \ +# --set api.credentials.stripe.data.STRIPE_SECRET_KEY="$STRIPE_SECRET_KEY" +# ========================================================================= + +distribution: + namespaces: + - default + - web + includeReleaseNamespace: true + +api: + credentials: + + stripe: + enabled: true + data: + STRIPE_SECRET_KEY: "REPLACE_VIA_--set" + STRIPE_WEBHOOK_SECRET: "REPLACE_VIA_--set" + + openai: + enabled: true + data: + OPENAI_API_KEY: "REPLACE_VIA_--set" + OPENAI_ORG_ID: "org-xxxxxxxx" + # このキーだけ AI ワークロード用ネームスペースに限定配布 + namespacesOverride: + - ml + - web + + slack: + enabled: true + data: + SLACK_BOT_TOKEN: "REPLACE_VIA_--set" + SLACK_SIGNING_SECRET: "REPLACE_VIA_--set" + +generic: + secrets: + + # プライベートコンテナレジストリの pull secret + private-registry: + enabled: true + type: kubernetes.io/dockerconfigjson + data: + ".dockerconfigjson": "REPLACE_VIA_--set-file" + + # Basic 認証(Ingress の auth-secret などで参照) + basic-auth: + enabled: false + type: kubernetes.io/basic-auth + data: + username: "admin" + password: "REPLACE_VIA_--set" diff --git a/examples/mail-providers.values.yaml b/examples/mail-providers.values.yaml new file mode 100644 index 0000000..c267e3a --- /dev/null +++ b/examples/mail-providers.values.yaml @@ -0,0 +1,113 @@ +# ========================================================================= +# 代表的な外部メールプロバイダの設定例 +# ------------------------------------------------------------------------- +# 使い方: +# helm upgrade --install secrets ./secret-manager \ +# -n secret-manager --create-namespace \ +# -f examples/mail-providers.values.yaml \ +# --set mail.providers.gmail.smtp.password="$GMAIL_APP_PASSWORD" +# +# パスワード等の実値はこのファイルに書かず、--set / SOPS 経由で注入すること。 +# ========================================================================= + +distribution: + namespaces: + - default + - web + - batch + includeReleaseNamespace: true + +common: + namePrefix: "cred-" + immutable: false + +mail: + providers: + + # ---- Gmail / Google Workspace ----------------------------------------- + # 事前に「アプリ パスワード」を発行しておくこと(2段階認証必須)。 + gmail: + enabled: true + smtp: + host: "smtp.gmail.com" + port: 587 + security: "starttls" + username: "no-reply@your-domain.com" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + fromName: "Your Service" + authMethod: "login" + imap: + host: "imap.gmail.com" + port: 993 + security: "tls" + renderMsmtprc: true + + # ---- Microsoft 365 / Exchange Online --------------------------------- + m365: + enabled: true + smtp: + host: "smtp.office365.com" + port: 587 + security: "starttls" + username: "no-reply@your-domain.com" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + authMethod: "login" + imap: + host: "outlook.office365.com" + port: 993 + security: "tls" + renderMsmtprc: true + + # ---- SendGrid ------------------------------------------------------- + # username は固定文字列 "apikey"、password が API キー本体。 + sendgrid: + enabled: true + smtp: + host: "smtp.sendgrid.net" + port: 587 + security: "starttls" + username: "apikey" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + fromName: "Your Service" + authMethod: "plain" + renderMsmtprc: true + + # ---- Amazon SES --------------------------------------------------- + # SES SMTP 認証情報(IAM のアクセスキーとは別物)を使う。リージョンをホスト名に反映。 + ses: + enabled: true + smtp: + host: "email-smtp.ap-northeast-1.amazonaws.com" + port: 587 + security: "starttls" + username: "REPLACE_VIA_--set" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + authMethod: "login" + renderMsmtprc: true + + # ---- Mailgun ----------------------------------------------------- + mailgun: + enabled: true + smtp: + host: "smtp.mailgun.org" + port: 587 + security: "starttls" + username: "postmaster@mg.your-domain.com" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + authMethod: "plain" + renderMsmtprc: true + + # ---- 汎用 / 社内 SMTP リレー(認証なし・TLSなし)-------------------- + internal-relay: + enabled: false + smtp: + host: "mail-relay.svc.cluster.local" + port: 25 + security: "none" + from: "no-reply@your-domain.com" + renderMsmtprc: true diff --git a/examples/reflector.values.yaml b/examples/reflector.values.yaml new file mode 100644 index 0000000..b53c87d --- /dev/null +++ b/examples/reflector.values.yaml @@ -0,0 +1,35 @@ +# ========================================================================= +# kubernetes-reflector を使った自動同期の設定例 +# ------------------------------------------------------------------------- +# 前提: クラスターに emberstack/kubernetes-reflector が導入済みであること。 +# helm repo add emberstack https://emberstack.github.io/helm-charts +# helm upgrade --install reflector emberstack/reflector -n kube-system +# +# この方式では Secret はリリース先ネームスペースにのみ作成され、 +# namespaceRegex に一致する全ネームスペースへ reflector が複製・追従する。 +# 新しいネームスペースが増えても values を書き換える必要がない。 +# ========================================================================= + +distribution: + # reflector 使用時 namespaces は無視される(ソースはリリース先のみ) + namespaces: [] + includeReleaseNamespace: true + reflector: + enabled: true + # 本番系ネームスペースにのみ配布する例 + namespaceRegex: "prod-.*|shared-services" + reloader: + enabled: true + +mail: + providers: + gmail: + enabled: true + smtp: + host: "smtp.gmail.com" + port: 587 + security: "starttls" + username: "no-reply@your-domain.com" + password: "REPLACE_VIA_--set" + from: "no-reply@your-domain.com" + renderMsmtprc: true diff --git a/templates/NOTES.txt b/templates/NOTES.txt new file mode 100644 index 0000000..ea48159 --- /dev/null +++ b/templates/NOTES.txt @@ -0,0 +1,47 @@ +secret-manager ({{ .Chart.Version }}) をデプロイしました。 + +{{- if .Values.distribution.reflector.enabled }} + +配布方式 : kubernetes-reflector による自動同期 + ソース : ネームスペース "{{ .Release.Namespace }}" + 同期先 : /{{ .Values.distribution.reflector.namespaceRegex }}/ に一致する全ネームスペース +{{- else }} +{{- $nsList := splitList "," (include "secret-manager.distributionNamespaces" .) }} + +配布方式 : Helm による複製({{ len $nsList }} ネームスペース) + {{- range $nsList }} + - {{ . }} + {{- end }} +{{- end }} +{{- if .Values.distribution.reloader.enabled }} +Reloader : 有効(参照側 Deployment に reloader.stakater.com/auto: "true" を付けると自動再起動) +{{- end }} + +生成された Secret(プレフィックス "{{ .Values.common.namePrefix }}"): +{{- $prefix := .Values.common.namePrefix }} +{{- range $name, $p := .Values.mail.providers }}{{- if $p.enabled }} + [mail] {{ $prefix }}{{ $name }} キー: SMTP_HOST/PORT/USERNAME/PASSWORD/FROM ほか, smtp.env{{ if $p.renderMsmtprc }}, msmtprc{{ end }} +{{- end }}{{- end }} +{{- range $name, $c := .Values.api.credentials }}{{- if $c.enabled }} + [api] {{ $prefix }}{{ $name }} キー: {{ keys $c.data | sortAlpha | join ", " }} +{{- end }}{{- end }} +{{- range $name, $g := .Values.generic.secrets }}{{- if $g.enabled }} + [gen] {{ $prefix }}{{ $name }} type: {{ $g.type | default "Opaque" }} +{{- end }}{{- end }} + +参照側の Deployment からの使い方: + + # まとめて環境変数に注入 + envFrom: + - secretRef: + name: {{ .Values.common.namePrefix }}<エントリ名> + + # 個別キーを参照 + env: + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.common.namePrefix }}gmail + key: SMTP_PASSWORD + +値を変更したいときは values を更新して `helm upgrade` するだけで全ネームスペースに反映されます。 diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..1b0e504 --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,116 @@ +{{/* +========================================================================== +secret-manager — 共通ヘルパー +========================================================================== +*/}} + +{{/* チャート短縮名 */}} +{{- define "secret-manager.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* フルネーム(リリース名 + チャート名) */}} +{{- define "secret-manager.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name (include "secret-manager.name" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* Chart.yaml 由来のチャートラベル */}} +{{- define "secret-manager.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* 生成する全 Secret に付ける共通ラベル(+ common.labels) */}} +{{- define "secret-manager.commonLabels" -}} +app.kubernetes.io/name: {{ include "secret-manager.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: secret-manager +helm.sh/chart: {{ include "secret-manager.chart" . }} +{{- with .Values.common.labels }} +{{ toYaml . }} +{{- end }} +{{- end -}} + +{{/* +配布先ネームスペースの実効リストをカンマ区切りで返す。 +distribution.namespaces + (includeReleaseNamespace のとき Release.Namespace) を +重複排除してソートしたもの。 +*/}} +{{- define "secret-manager.distributionNamespaces" -}} +{{- $ns := concat (list) (.Values.distribution.namespaces | default (list)) -}} +{{- if .Values.distribution.includeReleaseNamespace -}} +{{- $ns = append $ns .Release.Namespace -}} +{{- end -}} +{{- if not $ns -}} +{{- fail "distribution.namespaces が空です。少なくとも1つのネームスペースを指定するか、distribution.includeReleaseNamespace: true にしてください。" -}} +{{- end -}} +{{- $ns | uniq | sortAlpha | join "," -}} +{{- end -}} + +{{/* +1つの論理 Secret を、実効ネームスペースそれぞれに複製して出力する。 + +引数: dict + ctx (必須) ルートコンテキスト ($) + name (必須) プレフィックス適用前の Secret 名 + stringData (必須) map[string]string。全ての値は文字列であること + type (任意) Secret type。既定 "Opaque" + annotations (任意) map。このエントリ固有の追加アノテーション + namespacesOverride (任意) list。指定時はこのリストへ配布(distribution を無視) +*/}} +{{- define "secret-manager.secret" -}} +{{- $ctx := .ctx -}} +{{- $root := $ctx.Values -}} +{{- $fullName := printf "%s%s" $root.common.namePrefix .name -}} +{{- $type := .type | default "Opaque" -}} +{{- $reflector := $root.distribution.reflector -}} +{{- /* 配布先ネームスペースの決定 */ -}} +{{- $namespaces := list -}} +{{- $useReflector := false -}} +{{- if .namespacesOverride -}} +{{- $namespaces = .namespacesOverride | uniq -}} +{{- else if $reflector.enabled -}} +{{- $namespaces = list $ctx.Release.Namespace -}} +{{- $useReflector = true -}} +{{- else -}} +{{- $namespaces = splitList "," (include "secret-manager.distributionNamespaces" $ctx) -}} +{{- end -}} +{{- /* 共通アノテーションの組み立て */ -}} +{{- $ann := dict -}} +{{- range $k, $v := ($root.common.annotations | default dict) -}}{{- $_ := set $ann $k $v -}}{{- end -}} +{{- range $k, $v := (.annotations | default dict) -}}{{- $_ := set $ann $k $v -}}{{- end -}} +{{- if $useReflector -}} +{{- $_ := set $ann "reflector.v1.k8s.emberstack.com/reflection-allowed" "true" -}} +{{- $_ := set $ann "reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces" $reflector.namespaceRegex -}} +{{- $_ := set $ann "reflector.v1.k8s.emberstack.com/reflection-auto-enabled" "true" -}} +{{- $_ := set $ann "reflector.v1.k8s.emberstack.com/reflection-auto-namespaces" $reflector.namespaceRegex -}} +{{- end -}} +{{- if $root.distribution.reloader.enabled -}} +{{- $_ := set $ann "reloader.stakater.com/match" "true" -}} +{{- end -}} +{{- range $ns := $namespaces }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $fullName }} + namespace: {{ $ns }} + labels: + {{- include "secret-manager.commonLabels" $ctx | nindent 4 }} + secret-manager.cafepieters.com/entry: {{ $.name | quote }} + {{- if $ann }} + annotations: + {{- toYaml $ann | nindent 4 }} + {{- end }} +type: {{ $type }} +{{- if $root.common.immutable }} +immutable: true +{{- end }} +stringData: + {{- toYaml $.stringData | nindent 2 }} +{{- end -}} +{{- end -}} diff --git a/templates/api-secrets.yaml b/templates/api-secrets.yaml new file mode 100644 index 0000000..94ed37d --- /dev/null +++ b/templates/api-secrets.yaml @@ -0,0 +1,17 @@ +{{- /* +API 認証情報 Secret +api.credentials. ごとに 1 つの Opaque Secret を生成する。 +data 配下のキーがそのまま Secret のキーになる。 +*/ -}} +{{- range $name, $c := .Values.api.credentials }} +{{- if $c.enabled }} +{{- if not $c.data }} +{{- fail (printf "api.credentials.%s: data セクション(1つ以上のキー/値)は必須です" $name) }} +{{- end }} +{{- $sd := dict }} +{{- range $k, $v := $c.data }} +{{- $_ := set $sd $k (printf "%v" $v) }} +{{- end }} +{{- include "secret-manager.secret" (dict "ctx" $ "name" $name "stringData" $sd "type" "Opaque" "annotations" ($c.annotations | default dict) "namespacesOverride" ($c.namespacesOverride | default list)) }} +{{- end }} +{{- end }} diff --git a/templates/generic-secrets.yaml b/templates/generic-secrets.yaml new file mode 100644 index 0000000..e48e55c --- /dev/null +++ b/templates/generic-secrets.yaml @@ -0,0 +1,17 @@ +{{- /* +汎用 Secret +generic.secrets. ごとに 1 つの Secret を生成する。任意の Secret type を指定可能。 +(例: kubernetes.io/dockerconfigjson, kubernetes.io/tls, kubernetes.io/basic-auth) +*/ -}} +{{- range $name, $g := .Values.generic.secrets }} +{{- if $g.enabled }} +{{- if not $g.data }} +{{- fail (printf "generic.secrets.%s: data セクション(1つ以上のキー/値)は必須です" $name) }} +{{- end }} +{{- $sd := dict }} +{{- range $k, $v := $g.data }} +{{- $_ := set $sd $k (printf "%v" $v) }} +{{- end }} +{{- include "secret-manager.secret" (dict "ctx" $ "name" $name "stringData" $sd "type" ($g.type | default "Opaque") "annotations" ($g.annotations | default dict) "namespacesOverride" ($g.namespacesOverride | default list)) }} +{{- end }} +{{- end }} diff --git a/templates/mail-secrets.yaml b/templates/mail-secrets.yaml new file mode 100644 index 0000000..1db34d2 --- /dev/null +++ b/templates/mail-secrets.yaml @@ -0,0 +1,63 @@ +{{- /* +外部メール接続情報 Secret +mail.providers. ごとに 1 つの Secret を生成し、配布先ネームスペースへ複製する。 +*/ -}} +{{- range $name, $p := .Values.mail.providers }} +{{- if $p.enabled }} +{{- if not $p.smtp }} +{{- fail (printf "mail.providers.%s: smtp セクションは必須です" $name) }} +{{- end }} +{{- $smtp := $p.smtp }} +{{- if not $smtp.host }}{{- fail (printf "mail.providers.%s.smtp.host は必須です" $name) }}{{- end }} +{{- $security := $smtp.security | default "starttls" }} +{{- $username := $smtp.username | default "" }} +{{- $password := $smtp.password | default "" }} +{{- $from := $smtp.from | default $username }} +{{- $sd := dict }} +{{- $_ := set $sd "SMTP_HOST" (printf "%v" $smtp.host) }} +{{- $_ := set $sd "SMTP_PORT" (printf "%v" ($smtp.port | default 587)) }} +{{- $_ := set $sd "SMTP_SECURITY" (printf "%v" $security) }} +{{- $_ := set $sd "SMTP_USERNAME" (printf "%v" $username) }} +{{- $_ := set $sd "SMTP_PASSWORD" (printf "%v" $password) }} +{{- $_ := set $sd "SMTP_FROM" (printf "%v" $from) }} +{{- $_ := set $sd "SMTP_FROM_NAME" (printf "%v" ($smtp.fromName | default "")) }} +{{- $_ := set $sd "SMTP_REPLY_TO" (printf "%v" ($smtp.replyTo | default "")) }} +{{- $_ := set $sd "SMTP_AUTH_METHOD" (printf "%v" ($smtp.authMethod | default "plain")) }} +{{- /* IMAP(任意) */ -}} +{{- if and $p.imap $p.imap.host }} +{{- $imap := $p.imap }} +{{- $_ := set $sd "IMAP_HOST" (printf "%v" $imap.host) }} +{{- $_ := set $sd "IMAP_PORT" (printf "%v" ($imap.port | default 993)) }} +{{- $_ := set $sd "IMAP_SECURITY" (printf "%v" ($imap.security | default "tls")) }} +{{- $_ := set $sd "IMAP_USERNAME" (printf "%v" ($imap.username | default $username)) }} +{{- $_ := set $sd "IMAP_PASSWORD" (printf "%v" ($imap.password | default $password)) }} +{{- end }} +{{- /* POP3(任意) */ -}} +{{- if and $p.pop3 $p.pop3.host }} +{{- $pop3 := $p.pop3 }} +{{- $_ := set $sd "POP3_HOST" (printf "%v" $pop3.host) }} +{{- $_ := set $sd "POP3_PORT" (printf "%v" ($pop3.port | default 995)) }} +{{- $_ := set $sd "POP3_SECURITY" (printf "%v" ($pop3.security | default "tls")) }} +{{- $_ := set $sd "POP3_USERNAME" (printf "%v" ($pop3.username | default $username)) }} +{{- $_ := set $sd "POP3_PASSWORD" (printf "%v" ($pop3.password | default $password)) }} +{{- end }} +{{- /* source 可能な env ファイル */ -}} +{{- $lines := list }} +{{- range $k, $v := $sd }} +{{- $lines = append $lines (printf "export %s=%s" $k (quote $v)) }} +{{- end }} +{{- $_ := set $sd "smtp.env" (printf "%s\n" (join "\n" (sortAlpha $lines))) }} +{{- /* msmtprc スニペット(任意) */ -}} +{{- if $p.renderMsmtprc }} +{{- $tls := "tls on" }} +{{- $starttls := "tls_starttls on" }} +{{- if eq $security "tls" }}{{- $starttls = "tls_starttls off" }}{{- end }} +{{- if eq $security "none" }}{{- $tls = "tls off" }}{{- $starttls = "tls_starttls off" }}{{- end }} +{{- $auth := "auth off" }} +{{- if $username }}{{- $auth = printf "auth on\nuser %s\npassword %s" $username $password }}{{- end }} +{{- $msmtprc := printf "# secret-manager が生成 (%s)\ndefaults\n%s\n%s\ntls_trust_file /etc/ssl/certs/ca-certificates.crt\nlogfile -\naccount default\nhost %s\nport %v\nfrom %s\n%s\n" $name $tls $starttls $smtp.host ($smtp.port | default 587) $from $auth }} +{{- $_ := set $sd "msmtprc" $msmtprc }} +{{- end }} +{{- include "secret-manager.secret" (dict "ctx" $ "name" $name "stringData" $sd "type" "Opaque" "annotations" ($p.annotations | default dict) "namespacesOverride" ($p.namespacesOverride | default list)) }} +{{- end }} +{{- end }} diff --git a/values.schema.json b/values.schema.json new file mode 100644 index 0000000..d0ad8ac --- /dev/null +++ b/values.schema.json @@ -0,0 +1,152 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "secret-manager values", + "type": "object", + "additionalProperties": true, + "properties": { + "nameOverride": { "type": "string" }, + "fullnameOverride": { "type": "string" }, + "distribution": { + "type": "object", + "additionalProperties": false, + "required": ["namespaces", "includeReleaseNamespace", "reflector", "reloader"], + "properties": { + "namespaces": { + "type": "array", + "items": { "type": "string", "minLength": 1 } + }, + "includeReleaseNamespace": { "type": "boolean" }, + "reflector": { + "type": "object", + "additionalProperties": false, + "required": ["enabled", "namespaceRegex"], + "properties": { + "enabled": { "type": "boolean" }, + "namespaceRegex": { "type": "string", "minLength": 1 } + } + }, + "reloader": { + "type": "object", + "additionalProperties": false, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + } + } + }, + "common": { + "type": "object", + "additionalProperties": false, + "required": ["namePrefix", "labels", "annotations", "immutable"], + "properties": { + "namePrefix": { "type": "string" }, + "labels": { "type": "object" }, + "annotations": { "type": "object" }, + "immutable": { "type": "boolean" } + } + }, + "mail": { + "type": "object", + "additionalProperties": false, + "required": ["providers"], + "properties": { + "providers": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": ["enabled", "smtp"], + "properties": { + "enabled": { "type": "boolean" }, + "renderMsmtprc": { "type": "boolean" }, + "namespacesOverride": { "type": "array", "items": { "type": "string" } }, + "annotations": { "type": "object" }, + "smtp": { + "type": "object", + "additionalProperties": false, + "required": ["host"], + "properties": { + "host": { "type": "string", "minLength": 1 }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "security": { "type": "string", "enum": ["none", "starttls", "tls"] }, + "username": { "type": "string" }, + "password": { "type": "string" }, + "from": { "type": "string" }, + "fromName": { "type": "string" }, + "replyTo": { "type": "string" }, + "authMethod": { "type": "string", "enum": ["plain", "login", "cram-md5"] } + } + }, + "imap": { + "type": "object", + "additionalProperties": false, + "properties": { + "host": { "type": "string" }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "security": { "type": "string", "enum": ["none", "starttls", "tls"] }, + "username": { "type": "string" }, + "password": { "type": "string" } + } + }, + "pop3": { + "type": "object", + "additionalProperties": false, + "properties": { + "host": { "type": "string" }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "security": { "type": "string", "enum": ["none", "starttls", "tls"] }, + "username": { "type": "string" }, + "password": { "type": "string" } + } + } + } + } + } + } + }, + "api": { + "type": "object", + "additionalProperties": false, + "required": ["credentials"], + "properties": { + "credentials": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": ["enabled", "data"], + "properties": { + "enabled": { "type": "boolean" }, + "data": { "type": "object", "minProperties": 1 }, + "namespacesOverride": { "type": "array", "items": { "type": "string" } }, + "annotations": { "type": "object" } + } + } + } + } + }, + "generic": { + "type": "object", + "additionalProperties": false, + "required": ["secrets"], + "properties": { + "secrets": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": ["enabled", "data"], + "properties": { + "enabled": { "type": "boolean" }, + "type": { "type": "string" }, + "data": { "type": "object", "minProperties": 1 }, + "namespacesOverride": { "type": "array", "items": { "type": "string" } }, + "annotations": { "type": "object" } + } + } + } + } + } + } +} diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..323d29f --- /dev/null +++ b/values.yaml @@ -0,0 +1,148 @@ +# ========================================================================== +# secret-manager +# -------------------------------------------------------------------------- +# クラスター内の複数プロジェクトで横断的に使う「認証情報」を、この1チャートで +# 一元管理する。ここを更新して `helm upgrade` するだけで、配布先の全ネーム +# スペースの Secret がまとめて更新される。 +# +# セキュリティ上の注意(必読): +# - この values.yaml には平文の資格情報を "書かない"。Git にコミットしない。 +# - 実際の値は次のいずれかで注入する: +# 1) CI(Gitea Actions)から `--set` / `--set-file` で Gitea Secrets を注入 +# 2) SOPS で暗号化した values ファイル(helm-secrets プラグイン) +# 3) git 管理外の `secrets.values.yaml` を `-f` で渡す(.gitignore 済み) +# - 詳細は README.md「セキュリティモデル」を参照。 +# ========================================================================== + +# -------------------------------------------------------------------------- +# distribution: 生成した Secret を「どこへ」配るか +# -------------------------------------------------------------------------- +distribution: + # 同一 Secret を複製配置するネームスペースのリスト。 + # ここに挙げるネームスペースは事前に存在している必要がある。 + # リストから外して `helm upgrade` すると、そのネームスペースのコピーは削除される。 + namespaces: + - default + + # true の場合、リリース先ネームスペース (.Release.Namespace) も配布対象に自動追加する。 + includeReleaseNamespace: true + + # emberstack/kubernetes-reflector を使った自動同期(任意 / クラスターにコントローラーが必要) + # 有効化すると Secret はリリース先ネームスペースにのみ作成し、reflector アノテーションを + # 付与する。namespaceRegex に一致する全ネームスペースへコントローラーが複製・追従する。 + # namespaces の列挙より柔軟だが、外部コンポーネントへの依存が増える。 + reflector: + enabled: false + # 同期先ネームスペースを絞る正規表現(例: "team-.*" / "prod|staging") + namespaceRegex: ".*" + + # stakater/Reloader 連携(任意 / クラスターにコントローラーが必要) + # 有効化すると各 Secret に `reloader.stakater.com/match: "true"` を付与する。 + # 参照側の Deployment/StatefulSet に `reloader.stakater.com/auto: "true"` があれば、 + # Secret 更新時に自動でローリング再起動される。 + reloader: + enabled: false + +# -------------------------------------------------------------------------- +# common: 生成する全 Secret に共通の設定 +# -------------------------------------------------------------------------- +common: + # 生成される Secret 名のプレフィックス。エントリキーの前に付く。 + # 例: namePrefix="cred-" + mail プロバイダ "gmail" -> Secret 名 "cred-gmail" + namePrefix: "cred-" + + # 全 Secret に付与する追加ラベル + labels: {} + + # 全 Secret に付与する追加アノテーション + annotations: {} + + # Secret を immutable にする(kind: Secret の immutable: true)。 + # 誤編集を防ぎ apiserver 負荷を下げるが、値を変えるには一度 delete が必要。 + immutable: false + +# -------------------------------------------------------------------------- +# mail: 外部メールの接続情報(パスワード + サーバ情報) +# -------------------------------------------------------------------------- +# providers.<キー> ごとに 1 つの Secret を生成する。Secret には以下が入る: +# 個別キー: +# SMTP_HOST / SMTP_PORT / SMTP_SECURITY / SMTP_USERNAME / SMTP_PASSWORD +# SMTP_FROM / SMTP_FROM_NAME / SMTP_REPLY_TO / SMTP_AUTH_METHOD +# IMAP_HOST / IMAP_PORT / IMAP_SECURITY / IMAP_USERNAME / IMAP_PASSWORD (imap 定義時) +# POP3_HOST / POP3_PORT / POP3_SECURITY / POP3_USERNAME / POP3_PASSWORD (pop3 定義時) +# まとめ: +# smtp.env ... `export KEY=VALUE` 形式。コンテナ内で `source` して利用可能 +# msmtprc ... msmtp / PHP sendmail 用設定(renderMsmtprc: true のとき) +# +# 代表的なプロバイダの設定値は examples/ および README.md の一覧表を参照。 +mail: + providers: {} + # gmail: + # enabled: true + # smtp: + # host: "smtp.gmail.com" + # port: 587 + # security: "starttls" # none | starttls | tls + # username: "no-reply@example.com" + # password: "APP_PASSWORD" # ← 実値は --set / SOPS 経由で注入する + # from: "no-reply@example.com" + # fromName: "Example" + # replyTo: "" + # authMethod: "plain" # plain | login | cram-md5 + # imap: + # host: "imap.gmail.com" + # port: 993 + # security: "tls" # none | starttls | tls + # username: "" # 空なら smtp.username を流用 + # password: "" # 空なら smtp.password を流用 + # # pop3: + # # host: "pop.gmail.com" + # # port: 995 + # # security: "tls" + # renderMsmtprc: true + # # このプロバイダだけ別ネームスペースへ配る場合(省略時は distribution 準拠) + # namespacesOverride: [] + # # このプロバイダ Secret だけに付ける追加アノテーション + # annotations: {} + +# -------------------------------------------------------------------------- +# api: API 認証情報(任意のキー/値の集合) +# -------------------------------------------------------------------------- +# credentials.<キー> ごとに 1 つの Opaque Secret を生成する。 +# data 配下のキーがそのまま Secret のキーになる(`envFrom` でまとめて注入しやすい)。 +api: + credentials: {} + # stripe: + # enabled: true + # data: + # STRIPE_SECRET_KEY: "sk_live_xxx" + # STRIPE_WEBHOOK_SECRET: "whsec_xxx" + # namespacesOverride: [] + # annotations: {} + # openai: + # enabled: true + # data: + # OPENAI_API_KEY: "sk-xxx" + +# -------------------------------------------------------------------------- +# generic: 汎用 Secret(type を指定したい場合など) +# -------------------------------------------------------------------------- +# 上記カテゴリに当てはまらない資格情報。任意の Secret type を指定できる。 +generic: + secrets: {} + # private-registry: + # enabled: true + # type: kubernetes.io/dockerconfigjson + # data: + # .dockerconfigjson: '{"auths":{"registry.example.com":{"auth":"BASE64"}}}' + # namespacesOverride: [] + # internal-tls: + # enabled: true + # type: kubernetes.io/tls + # data: + # tls.crt: | + # -----BEGIN CERTIFICATE----- + # ... + # tls.key: | + # -----BEGIN PRIVATE KEY----- + # ...