Gitに秘匿情報をcommitする必要がなくなるKustomize Pluginを作った
AWS Systems Manager Parameter Storeを使って、SecretsリソースをGenerateするKustomize Pluginを作りました。
作ったもの github.com
この記事では、なぜこのPluginが必要だったのか、どのようにPluginを作ったかといったことを紹介します。
このPlugin自体はAWS環境のみとなりますが、同様のPluginを作ることでその他の環境でも似たようなことができると思います。
また、GitOpsにおける秘匿情報管理の参考例になるのではと思います。
なお、今回の内容はKustomize v3.8.4を前提としたものになります。
モチベーション
以下の条件を満たすために、独自の仕組みを用意することにしました。
- GitOpsにおいて秘匿情報をGitにcommitしたくない
- Secretsリソースの環境変数が変更されたらPodに変更が反映される
- PodがCustom Resourceで作成されていても問題なく動作する
以下、細かい説明です。
GitOpsでは、KubernetesのManifestファイルをGitに登録する必要があります。
これは秘匿情報を保持するSecretsリソースも例外ではありません。
Secretsリソースのデータはbase64でエンコードされているだけなので、Gitのリポジトリを見ることができれば簡単に秘匿情報を取得できてしまいます。
これではマズいので、Kubernetes関連のOSSでは、秘匿情報を扱うためのプログラムがいくつかあります。有名どころでは、以下のものです。
GitHub - bitnami-labs/sealed-secrets: A Kubernetes controller and tool for one-way encrypted Secrets
これらはCustom Resourceという形で独自のリソースをKubernetes内で動作させて、代わりにSecretsリソースを作るプログラムです。
これによって、Gitに秘匿情報をcommitせずに済みます。
ただこのやり方だと、Secretsリソースが作り直された時にSecretsリソースを参照するPodを作り直すといったことができません。
Podを作り直す処理はSecretsで定義された環境変数をPodに反映させるために必要な処理になります。
実はSecretsリソースとの差分を検出して、Podを作り直すプログラムもあります。
ただDeploymentsリソースといったKubernetes標準のリソースしか対応しておらず、例えばArgo RolloutsといったPodをCustom Resourceで作成するケースの場合には対応できませんでした。
Kustomize Pluginについて
ここからはgeneratorに分類されるPluginを作成する前提での説明になります。
generatorはManifestの内容をプログラムで生成する機能です。
このgeneratorを独自に作ることでSecretsリソースのデータをファイル上では隠蔽しつつ、Secretsリソースを作ることができます。
Pluginの利用方法
まずは、KustomizeでPluginを利用方法です。
利用するgeneratorをkustomization.yamlで指定します。
kustomization.yaml
... generators: - aws-ssm-secret.yaml ...
Pluginで利用するパラメータを定義するためのyamlファイルを用意します。
apiVersion: hi1280.com/v1 kind: AwsSsmSecret metadata: name: secret version: 1 envs: - PASSWORD=/hello-service/password region: ap-northeast-1
apiVersion
およびkind
の値がPluginを配置するPathと関連しています。
例えば、上記のyamlの場合、以下にPluginを配置します。
$XDG_CONFIG_HOME/kustomize/plugin/hi1280.com/v1/awsssmsecret/
$XDG_CONFIG_HOME
はデフォルトでは$HOME/.config
になります。
$XDG_CONFIG_HOME/kustomize/pluginフォルダ以下にapiVersionの値、小文字にしたkindの値という形でPathが決められます。
Pluginの実装
Pluginの実装については、Builtinで用意されているsecret generatorの実装が参考になります。
kustomize/SecretGenerator.go at master · kubernetes-sigs/kustomize · GitHub
SecretGenerator.go
package main import ( "sigs.k8s.io/kustomize/api/kv" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/yaml" ) type plugin struct { h *resmap.PluginHelpers types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` types.SecretArgs } //noinspection GoUnusedGlobalVariable var KustomizePlugin plugin func (p *plugin) Config(h *resmap.PluginHelpers, config []byte) (err error) { p.SecretArgs = types.SecretArgs{} err = yaml.Unmarshal(config, p) if p.SecretArgs.Name == "" { p.SecretArgs.Name = p.Name } if p.SecretArgs.Namespace == "" { p.SecretArgs.Namespace = p.Namespace } p.h = h return } func (p *plugin) Generate() (resmap.ResMap, error) { return p.h.ResmapFactory().FromSecretArgs( kv.NewLoader(p.h.Loader(), p.h.Validator()), p.SecretArgs) }
plugin型でPluginが受け取るパラメータを定義します。
Configメソッドはパラメータを実際にセットする処理を実行します。
Generateメソッドは値を生成する処理を実行します。
今回のPluginではGenerateメソッドで、AWS SSMから秘匿情報を取得し、値をセットしています。
... func (p *plugin) Generate() (resmap.ResMap, error) { ... for _, e := range p.Envs { env := strings.Split(e, "=") getParamsInput := &ssm.GetParameterInput{ Name: aws.String(env[1]), WithDecryption: aws.Bool(true), } resp, err := svc.GetParameter(getParamsInput) if err != nil { return nil, err } args.LiteralSources = append(args.LiteralSources, env[0]+"="+*resp.Parameter.Value) } return p.h.ResmapFactory().FromSecretArgs( kv.NewLoader(p.h.Loader(), p.h.Validator()), args) } ...
Goのプログラムが完成したら、ビルドを行います。
Pluginとして動作するようにビルドを行い、適切なPathにPluginを配置します。
例えば、以下のコマンドになります。
$ go build -buildmode plugin -o $XDG_CONFIG_HOME/kustomize/plugin/hi1280.com/v1/awsssmsecret/AwsSsmSecret.so .
Pluginの実行
Go Pluginを利用する場合、PluginをコンパイルしたGoでkustomizeもコンパイルする必要があります。
これについての詳細は下記にあります。
Go plugin Caveats | Kustomize
kustomizeをコンパイルするには以下を実行します。
$ go get sigs.k8s.io/kustomize/kustomize/v3
既にkustomizeをインストール済みの場合、コンパイルしたkustomizeを利用するようにPathを通す必要があります。
kustomize build
の実行には、--enable_alpha_plugins
の指定が必要になります。
$ kustomize build --enable_alpha_plugins .
まとめ
GitOpsで秘匿情報を扱うにはどうすれば良いかと試行錯誤した結果、Kustomize Pluginを作ることに行き着きました。
今回の取り組みを通して、GitOpsで秘匿情報を扱うことについて、まだデファクトスタンダードが存在しないということが分かりました。
今回紹介したPluginの使い方はGitHubのREADMEを見てもらえればと思います。