DBのパスワードやAPIトークンといった認証情報をバージョン管理するコードや設定ファイル上に書くとOSS化など公開範囲を広げるときにやや困るし漏れるリスクが高まるのでなるべく避けたい。 そこでSSMのParameter Storeに値を置き、実行時やデプロイ時に参照する。
SSMのParameter StoreとSecrets Manager
Systems Manager (SSM)はAWSのリソースを可視化したり操作を自動化したりするサービス群で、 設定を持つParameter Storeはその一つ。値は暗号化して持つこともできる。 料金はかからない。
SSMのParameter Storeと似たような別のAWSのサービスに Secrets Managerというのがあって、RDSなどと連携してLambdaによって定期的に新しい値を生成しローテーションさせることができる。 ただし料金がシークレットの件数($0.4/月)とAPIコール($0.05/10000回)でかかる。
CloudFormationでVPCを作成してLambdaをデプロイしAurora Serverlessを使う - sambaiz-net
今はParamter StoreとSecrets Managerが統合されていて、Parameter StoreのAPIでどちらも参照できるようだ。 今回はローテーションしないので単純に料金がかからないParameter Storeの方に書き込むことにする。 ただし、Parameter Storeは現状一度に大量のリクエストが飛ぶような使い方をするとRate exceededになってしまう問題がある。
実行時の値取得
実行時に値を取得するのはこんな感じ。
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
)
type Parameter struct {
ssm *ssm.SSM
}
func newParameter(sess *session.Session) *Parameter {
return &Parameter{
ssm: ssm.New(sess),
}
}
func (s *Parameter) Get(name string, decrypt bool) (string, error) {
param, err := s.ssm.GetParameter(&ssm.GetParameterInput{
Name: aws.String(name),
WithDecryption: aws.Bool(decrypt),
})
if err != nil {
return "", err
}
var secret string
if param.Parameter.Value != nil {
secret = *param.Parameter.Value
}
return secret, nil
}
func main() {
secret := newParameter(session.New())
param, err := secret.Get("test", true)
if err != nil {
panic(err)
}
fmt.Println(param) // => ok
param, err = secret.Get("notfound", true)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
fmt.Println(awsErr.Code()) // => ParameterNotFound
}
}
}
$ AWS_SDK_LOAD_CONFIG=1 AWS_PROFILE=**** go run main.go
Serverless Framework
Serverless Frameworkの設定ファイルserverless.ymlでもSSMを参照でき、デプロイ時に解決される。
Serverless FrameworkでLambdaをデプロイする - sambaiz-net
provider:
name: aws
...
environment:
SECRET: ${ssm:test~true}
参考
AWSのParameter StoreとSecrets Manager、結局どちらを使えばいいのか?比較 - Qiita