SSMとは?
インフラ運用を便利にするサービス
SSM ParameterStoreとは?
パスワードなど値を管理
SecureStringsは裏でKMSを使って文字列を暗号化して保持も出来るよ!
(料金かかるけど、そんなたいしたことない。前提知識として持っててね! 料金 - AWS Key Management Service (KMS)| AWS)
DBのパスワードを保管して呼び出す、みたいなので使えそう
実際にやってみよう
1. ParameterStoreの登録
SystemManager->Parameter Store->パラメーターの作成
安全な文字列にチェックをいれて"hogehoge"と入れてみます
2. aws-cliで試してみる
### --with-decryptionを外すと暗号化された文字が出力されます $ aws ssm get-parameters --region ap-northeast-1 --name sion_test --with-decryption { "Parameters": [ { "Name": "sion_test", "Type": "SecureString", "Value": "hogehoge", "Version": 1 } ], "InvalidParameters": [] }
3. Goで動かす
あとでテストでSSM mockを使うので、ssmiface.SSMAPIを使います
package main import ( "fmt" "os" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/aws-sdk-go/service/ssm/ssmiface" ) func main() { d := newssmDecrypter() s, err := d.decrypt("sion_test") if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println(s) } // ssmDecrypter stores the AWS Session used for SSM decrypter. type ssmDecrypter struct { sess *session.Session svc ssmiface.SSMAPI } // newssmDecrypter returns a new ssmDecrypter. func newssmDecrypter() *ssmDecrypter { sess := session.New() svc := ssm.New(sess) return &ssmDecrypter{sess, svc} } // decrypt decrypts string. func (d *ssmDecrypter) decrypt(encrypted string) (string, error) { params := &ssm.GetParameterInput{ Name: aws.String(encrypted), WithDecryption: aws.Bool(true), } resp, err := d.svc.GetParameter(params) if err != nil { return "", err } return *resp.Parameter.Value, nil } // $ export AWS_REGION=ap-northeast-1 && export AWS_PROFILE=xxxxxx && go run main.go // hogehoge
4. テストを書く
SSMのmockを使います
aws-sdk-go/interface.go at master · aws/aws-sdk-go · GitHub
こちらをみればわかると思いますが、色々とinterfaceが用意されてる + サンプルコードも書いてます
今回はGetParameterメソッドをmock用に作ります("decrypted"の文字列を返すようにする)
package main import ( "reflect" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/aws-sdk-go/service/ssm/ssmiface" ) // mockSSMClient stores SSM interface for mock type mockSSMClient struct { ssmiface.SSMAPI } // newTestssmDecrypter returns a new ssmDecrypter for mock. func newTestssmDecrypter(mock ssmiface.SSMAPI) *ssmDecrypter { return &ssmDecrypter{ svc: mock, } } // GetParameter returns "decrypted" that is Decrypted SSM parameter. func (m *mockSSMClient) GetParameter(i *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) { parameter := &ssm.Parameter{ Value: aws.String("decrypted"), } return &ssm.GetParameterOutput{ Parameter: parameter, }, nil } func TestDecrypt(t *testing.T) { mock := &mockSSMClient{} d := newTestssmDecrypter(mock) cases := []struct { value string expected interface{} }{ // string decrypt { "a", "decrypted", }, } for _, c := range cases { s, err := d.decrypt(c.value) if err != nil { t.Fatalf("failed decrypt: %s", err) } if !reflect.DeepEqual(c.expected, s) { t.Errorf("want %s got %s", c.expected, s) } } }
$ go test -v . s-koyama-m:hoho s-koyama$ go test -v . === RUN TestDecrypt --- PASS: TestDecrypt (0.00s) PASS ok my/ssm 0.017s