Secrets 一元管理チャート secret-manager v0.1.0 を実装
Helm Chart Release / release-chart (push) Successful in 6s
Helm Chart Release / release-chart (push) Successful in 6s
クラスター横断で使う認証情報を1チャートで管理し、複数ネームスペースへ 同一 Secret として配布する仕組みを実装。 - distribution: namespaces への複製 / kubernetes-reflector 自動同期 / stakater-Reloader 連携を選択可能 - common: namePrefix・共通ラベル/アノテーション・immutable - mail.providers: SMTP/IMAP/POP3 のサーバ情報 + パスワードを一元管理。 source 可能な smtp.env と任意の msmtprc スニペットを Secret に同梱 - api.credentials: 任意のキー/値集合を Opaque Secret 化(envFrom 向け) - generic.secrets: 任意 type(dockerconfigjson / tls / basic-auth 等) - values.schema.json による厳格バリデーション - examples/(Gmail・M365・SendGrid・SES・Mailgun・reflector) - .gitea/workflows/helm-release.yaml で Package Registry へ自動発行 - README.md にセキュリティモデル・values リファレンス・参照方法を記載 - 平文資格情報の混入防止に .gitignore を整備
This commit is contained in:
@@ -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` するだけで全ネームスペースに反映されます。
|
||||
@@ -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 -}}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- /*
|
||||
API 認証情報 Secret
|
||||
api.credentials.<name> ごとに 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 }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- /*
|
||||
汎用 Secret
|
||||
generic.secrets.<name> ごとに 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 }}
|
||||
@@ -0,0 +1,63 @@
|
||||
{{- /*
|
||||
外部メール接続情報 Secret
|
||||
mail.providers.<name> ごとに 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 }}
|
||||
Reference in New Issue
Block a user