AWSで秘密定数を外部に公開せず環境変数として定義するためのGo製ツール、「ssm2env」作った

概要

AWS SSM (EC2 Parameter Store)のパラメーターを取ってきて環境変数として定義するツールを作った。 これによってEC2に立てたサーバーに秘密情報を外部に公開せずに、手間なく環境変数として定義できる。

詳細

環境ごとに異なる秘密情報をAPIに渡す際、その管理方法は、twelve-factor appにもある通りデプロイ対象のサーバー内部の環境変数として定義するべき。

ただ、自動化されたデプロイフローでは、どういった手順で秘密情報を渡せばいいか悩むことが多い。

自分が考えた範囲では、

1. AWSのSSMにTerraform経由*1でシークレットな変数を設定
2. APIのデプロイ時にSSMから特定prefixのついたパラメーターを取得
3. パラメーターを全て環境変数として読み込ませる
4. APIが起動する際に秘密情報を定数として環境変数から読み込む

*1) 正確にはCircleCIの環境変数設定に事前に入れた状態で、CircleCIのビルドフローの中でterraform applyを叩き、terraformがその環境変数を読み込み、SSMに流し込む。

という手順で、基本的には機密性の高い情報を外部に見えない状態で環境変数として定義、読み込みできると考えた。

そこで問題になるのが、AWS SSMからどうやって環境変数に秘密情報を持ってくるのか?ということだった。

既存手段について

実はこの問題を解決してくれる、以下のようなenv-injectorという既存ツールがある。

ただ、このツールには若干一手間必要で、事前に環境変数を空文字で定義する必要がある

これの意味するところは、

1. 環境変数のキーが露出するから、厳密には外部非公開ではない
2. どの変数が定義されたか、開発者が認知して、デプロイスクリプトにそのキーを書き写さなきゃいけない

ということ。

特に2が個人的には課題に感じることが多く、「あっ、このキー忘れた」ということが度々あって、起動に失敗するとかそう行った事態に出くわす。

そういう認知上の問題は極力避けるべき。

ssm2envについて

上記の問題点を解決しつつ、SSMに定義済みの情報を環境変数として定義できるツールが、ssm2env

使い方は簡単で、以下のようにサーバー内部(ないしデプロイスクリプト)で、prefixを指定しつつ呼べば、/etc/profile.dに環境変数を定義するinit scriptがつくられる。

SSM2ENV_PREFIX=testapi.prod ssm2env

この場合、例えば以下のようなパラメーターが定義されてるとき、

$ aws ssm get-parameters --with-decryption --names testapi.prod.DB_USER testapi.prod.DB_PASSWORD
{
"InvalidParameters": [],
"Parameters": [
{
"Type": "String",
"Name": "testapi.prod.DB_USER",
"Value": "root"
},
{
"Type": "SecureString",
"Name": "testapi.prod.DB_PASSWORD",
"Value": "password"
}
]
}

以下のように環境変数がexportされることを想定している。

export DB_USER=root
export DB_PASSWORD=password

まとめ

SSMを使ってる前提で、EC2にセキュアに情報を渡す手段を開発、解説しました。

デプロイフローの違い等により、問題点等あればご指摘ください。

追記

env-injectorの作者様が下記のようなpostを書かれていました。

書き方の面に関しては申し訳ありませんでした、配慮不足でしたので、修正いたしました。

というか僕もenv-injectorをそのまま使っていて、イイゾイイゾと思っていたものの、アップデートがあったのか途中で動かなくなったので、それを機にDescribeParameterを使いつつ書き直した、というのが経緯だったりします。なのでenv-injector自体便利なものとして利用していました。

DescribeParameterに関する3つ背景は、確かに納得のいく意見です。特に3に関しては「なるほどなあDockerfileに書くケースか」と勉強になりました。

ただ、僕の場合は環境変数のキーが露出するから、厳密には外部非公開ではない というのは「まあ、全然問題ではないよね、別にいいじゃん」と思うのですが、どちらかというと、キーを書き忘れることが多くて、たとえprefixがマッチしたものを全て転記するとしても、繰り返し作業を無くしたかった、というのが本音です。

なので、DescribeParameterを使った点は、僕個人の手作業への趣味趣向によるものが大きいかもしれないので、絶対的なツールの良し悪しには影響がないな、と反省を込めてここに記載します。