AWS SecretsManagerからSecretリソースを作成するHelmチャートを作成してみた
1. はじめに
1-1. 記事について
「External Secretsを利用してAWS SecretsManagerの秘匿情報からSecretリソースを作成するHelmチャート」を作成したので、実装例として紹介します。
1-2. External Secretsとは
External Secretsはクラウドプロバイダが提供するシークレット管理サービスからSecretリソースを作成するCRDです。Kubernetes(=以下k8s)のSecret管理方法には他にも色々あります[1]が、この記事ではExternal Secretsを採用しています。
2. 実装
2-1. ディレクトリ構成
$ tree .
.
└── secretsmanager
├── Chart.lock
├── Chart.yaml
├── charts
│ └── external-secrets-0.5.9.tgz
├── templates
│ ├── _helpers.tpl
│ ├── externalsecret.yaml
│ ├── secretstore.yaml
│ └── serviceaccount.yaml
└── values.yaml
2-2. Chart.yaml
secretsmanager/Chart.yaml
はhelm create secretsmanager
コマンドにより生成したデフォルトからコメントを削除したものをベースとしています。ここでは、External Secretsを利用してSecretリソースを作成するので、.dependencies
としてexternal-secrets
を指定しています。
apiVersion: v2
name: secretsmanager
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16.0"
dependencies:
- name: external-secrets
version: "0.5.9"
repository: "https://charts.external-secrets.io"
そして、上記の状態でhelm dependency update
コマンド[2]を実行し、指定バージョンのExternal Secrets
チャートをリポジトリからダウンロードします。これにより、secretsmanager/charts/external-secrets-0.5.9.tgz
とsecretsmanager/Chart.lock
が作成されます。
2-3. _helpers.tpl
テンプレート内で汎用的に使い回す定義は、tpl関数としてsecretsmanager/templates/_helpers.tpl
に記述しています。
{{/*
Expand the name of the chart.
*/}}
{{- define "secretsmanager.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "secretsmanager.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "secretsmanager.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "secretsmanager.labels" -}}
helm.sh/chart: {{ include "secretsmanager.chart" . }}
app.kubernetes.io/name: {{ include "secretsmanager.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
ここでは、テンプレートから直接呼び出しているtpl関数は以下の2つだけです。
secretsmanager.fullname
secretsmanager.labels
まず、secretsmanager.fullname
は全リソースの.metadata.name
を動的に変更させるために使用しています。こうすることで、テンプレートを変更することなくhelm
コマンドからユニークな名前でリソースをデプロイすることができ、k8sの開発効率が上がります。
また、secretsmanager.labels
は全てのリソースに対して共通ラベルを付与するために使用しています。k8sでは、全てのツールで解釈可能な共通ルールに則ってラベル付けすることが推奨[3]されており、こうすることでツールに依存せずオブジェクトの管理と可視化が可能になるようです。
2-4. ServiceAccount
External SecretsがAWS SecretsManagerから秘匿情報を取得してSecretリソースを作成するためのServiceAccountを作成しています。このServiceAccountにはcreate-secret-from-secrets-manager-role
という名のロールを紐づけています。
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "secretsmanager.fullname" . }}
labels:
{{- include "secretsmanager.labels" . | nindent 4 }}
app.kubernetes.io/component: account
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::{{ .Values.awsAccount }}:role/{{ .Values.roleName }} # ロール名はcreate-secret-from-secrets-manager-role
このIAMロール(=create-secret-from-secrets-manager-role
)はmain
ネームスペース内の任意のServiceAccountがAssumeRoleできるように定義しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::AWSアカウント:oidc-provider/oidc.eks.リージョン名.amazonaws.com/id/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"oidc.eks.リージョン名.amazonaws.com/id/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:sub": "system:serviceaccount:main:*"
}
}
}
]
}
また、SecretsManagerから任意のリソースを取得できるポリシーを先述のIAMロールにアタッチしています。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetResourcePolicy",
"secretsmanager:GetSecretValue",
"secretsmanager:ListSecretVersionIds"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
2-5. SecretStore
2-4節で作成したServiceAccountを用いてAWS SecretsManagerへアクセスするよう定義しています。
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: {{ include "secretsmanager.fullname" . }}
labels:
{{- include "secretsmanager.labels" . | nindent 4 }}
spec:
provider:
aws:
service: SecretsManager
region: {{ .Values.awsRegion | quote }}
auth:
jwt:
serviceAccountRef:
name: {{ include "secretsmanager.fullname" . }}
2-6. ExternalSecret
2-5節で定義したアクセス情報を利用して、指定のシークレット/シークレットキーからシークレットの値を取得して、Secretリソースを作成しています。
apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
metadata:
name: {{ include "secretsmanager.fullname" . }}
labels:
{{- include "secretsmanager.labels" . | nindent 4 }}
spec:
refreshInterval: {{ .Values.secretRefreshInterval | quote }}
secretStoreRef:
name: {{ include "secretsmanager.fullname" . }}
kind: SecretStore
target:
name: {{ .Values.secretsManagerName | quote }}
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: {{ .Values.secretsManagerName | quote }}
property: password
- secretKey: dbname
remoteRef:
key: {{ .Values.secretsManagerName | quote }}
property: dbname
- secretKey: port
remoteRef:
key: {{ .Values.secretsManagerName | quote }}
property: port
- secretKey: host
remoteRef:
key: {{ .Values.secretsManagerName | quote }}
property: host
- secretKey: username
remoteRef:
key: {{ .Values.secretsManagerName | quote }}
property: username
AWS SecretsManagerに登録されているシークレット情報は以下の通りです。
2-7. values.yaml
fullnameOverride: ""
nameOverride: ""
awsRegion: us-east-1
awsAccount: "123456789012"
secretsManagerName: database-secrets
secretRefreshInterval: 1h
3. デプロイ後の確認
デプロイ後、database-secrets
というSecretリソースが作成されていることを確認できます。
$ kubectl describe secret/database-secrets -n main
Name: database-secrets
Namespace: main
Labels: app.kubernetes.io/instance=secretsmanager
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=secretsmanager
app.kubernetes.io/version=1.16.0
helm.sh/chart=secretsmanager-0.1.0
Annotations: reconcile.external-secrets.io/data-hash: 8d2b82dda5c193f29cd8e510be4a8801
Type: Opaque
Data
====
username: 8 bytes
dbname: 15 bytes
host: 72 bytes
password: 30 bytes
port: 4 bytes
4. まとめ
この記事では、External Secretsを利用してAWS SecretsManagerの秘匿情報からSecretリソースを作成するHelmチャートの作成例を紹介しました。Helmに精通しているわけではありませんが、実装例から参考になることもあるかもしれないので記録として残しておきます。誰かの何かのお役に立てれば幸いです^^
-
他にどのようなサービスがあるのかについては、悩みに悩んだ Kubernetes Secrets の管理方法、External Secrets を選んだ理由などが分かりやすそうです。 ↩︎
-
詳細はHelm Dependency Updateをご参照ください。 ↩︎
Discussion