aws-ssm-env

AWS EC2 Parameter Storeから取得したパラメータを環境変数として設定します。

デフォルトでは、パラメータ名の最後の階層が環境変数名として設定されます。

例えば、/staging/secure/DB_PASSWORDというパラメータ名であれば、ENV['DB_PASSWORD']にパラメータ値が設定されます。
この環境変数のネーミングはオプションでカスタマイズ可能です。(後述)

Installation

gem install aws-ssm-env

Rails

# Gemfile
gem 'aws-ssm-env', group: :aws
# config/application.rb
if defined?(AwsSsmEnv)
  AwsSsmEnv.load(path: "/myapp/#{ENV['RAILS_ENV']}", recursive: true)
end

Other ruby program

require 'aws-ssm-env'
AwsSsmEnv.load!(begins_with: "myapp.ENV['RACK_ENV'].")

Quick Start

事前にAWS EC2 Parameter Storeにパラメータを登録しておく必要があります。

# 例) /myservice/staging/RDS_PASSWORDをSecureStringで登録
aws ssm --region ap-northeast-1 put-parameter \
  --name /myservice/staging/RDS_PASSWORD \
  --type SecureString --value <secret value>

AWSの認証情報を設定します。例えば、以下のように環境変数を利用したり、

export AWS_ACCESS_KEY_ID=YOURACCESSKEYID
export AWS_SECRET_ACCESS_KEY=YOURSECRETKEY
bundle exec rails start

引数でssm_client_argsを渡したり、

AwsSsmEnv.load(
  fetch: "/myservice/#{ENV['RAILS_ENV']}",
  ssm_client_args: {
    access_key_id: 'ACCESS_KEY_ID',
    secret_access_key: 'SECRET_ACCESS_KEY',
    region: 'ap-northeast-1',
  }
)

Aws.configを利用することもできます。


if defined?(AwsSsmEnv)
  AWS.config({
    access_key_id: 'ACCESS_KEY_ID',
    secret_access_key: 'SECRET_ACCESS_KEY',
    region: 'ap-northeast-1',
  })
  AwsSsmEnv.load(path: "/myservice/#{ENV['RAILS_ENV']}")
end

詳細はaws-sdkのドキュメントを参照してください。

Usage

AwsSsmEnv#loadに渡すオプションの説明です。

decryption: [Boolean]

SecureStringのパラメータを復号化するかどうかを表すフラグ。
trueを指定した場合は取得したSecureStringパラメータの値は復号化されている。
falseの場合は暗号化されたまた環境変数値として設定される。
なお、このためのgemなのでデフォルトはtrue(復号化する)。

overwrite: [Boolean]

すでに設定されている環境変数を上書きするかどうかを指定する。
trueを指定した場合、環境変数が設定されていても取得したパラメータ値で上書きする。
falseを指定した場合はすでに設定されている環境変数を上書きしない。
デフォルトはfalse(上書きしない)。
なお、AwsSsmEnv#load!を実行した場合、このフラグは自動的にtrueになる。

client: [Aws::SSM::Client]

Aws::SSM::Clientのインスタンスを指定する。
すでに生成済みのインスタンスがある場合にそれを設定するためのオプション。
生成済みのインスタンスがない場合はssm_client_argsを利用する。

ssm_client_args: [Hash]

Aws::SSM::Clientのコンストラクタに渡すハッシュを指定する。
指定しなかった場合は引数なしでAws::SSM::Client.newが呼ばれる。
環境変数やEC2インスタンスプロファイルによる認証情報を利用する場合は不要。

fetch: [Symbol, AwsSsmEnv::Fetcher, Object]

パラメータ取得方法を指定する。
指定可能な値は:path, :begins_withまたはAwsSsmEnv::Fetcherを実装したクラスのインスタンス、eachメソッドを 持ったクラスのインスタンスのいずれか。
何も指定されていない場合は:pathとして扱われるが、後述のbegins_withが指定されていた場合は自動的に:begins_withとなる。

:fetch => :path

:pathを指定した場合はパラメータ階層をパス指定で取得するAwsSsmEnv::PathFetcherが利用される。
この場合は後述のpath引数が必須となる。また、後述のrecursive引数を利用する。
この方法でパラメータを取得する場合は指定するパスに対してssm:GetParametersByPathの権限が必要。 以下、IAMポリシーの例を示す。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:GetParametersByPath",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter/YOUR_PATH"
    }
  ]
}

:fetch => :begins_with

:begins_withを指定した場合はパラメータ名が指定した文字列から開始するパラメータを取得するAwsSsmEnv::BeginsWithFetcherが利用される。
この場合は後述のbegins_with引数が必須となる。 この方法でパラメータを取得する場合は指定するパスに対してssm:DescribeParametersおよびssm:GetParametersの権限が必要。 以下、IAMポリシーの例を示す。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:DescribeParameters",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:GetParameters",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter/YOUR_PREFIX*"
    }
  ]
}

other

fetchAwsSsmEnv::Fetcherを実装したクラスのインスタンス、もしくはeachメソッドを持つインスタンスを指定した場合はそのインスタンスをそのまま利用する。

naming: [Symbol, AwsSsmEnv::NamingStrategy, Object]

環境変数名を導出方法を指定する。
指定可能な値は:basename, :snakecaseまたはAwsSsmEnv::NamingStrategyを実装したクラスのインスタンス、parse_nameメソッドを持ったクラスのインスタンスのいずれか。
デフォルトは:basename

:naming => :basename or :naming => nil

namingを指定しなかった場合、もしくは:basenameを指定した場合はパラメータ階層の最後の階層を変数名とするAwsSsmEnv::BasenameNamingStrategyが利用される。
この場合、例えば/myapp/production/DB_PASSWORDというパラメータ名であればENV['DB_PASSWORD']にパラメータ値がインジェクションされる。

:naming => :snakecase

:snakecaseを指定した場合はパラメータ名のスラッシュ区切りをアンダースコア区切りにした結果を大文字に変換して環境変数名とするAwsSsmEnv::SnakeCaseNamingStrategyが利用される。
この場合、例えば/myapp/production/DB_PASSWORDというパラメータ名であればENV['MYAPP_PRODUCTION_DB_PASSWORD']にパラメータ値がインジェクションされる。
後述のremoved_prefix引数で除外する先頭文字列を指定することができる。
また、後述のdelimiterオプションでアンダースコアに変換する文字を指定できる。
以下の例では/myapp/production/db/passwordというパラメータがENV['DB_PASSWORD']にインジェクションされる。

AwsSsmEnv.load(naming: :snakecase, removed_prefix: '/myapp/production')

other

AwsSsmEnv::NamingStrategyを実装したクラスのインスタンス、もしくはparse_nameメソッドを持つ
インスタンスを指定した場合はそのインスタンスをそのまま利用する。

path: [String]

fetchに何も指定していない場合、もしくは:pathを指定した場合は必須となる。
パラメータを取得するパス階層を指定する。
下の例では/myapp/web/production直下のパラメータが取得される。

AwsSsmEnv.load(path: '/myapp/web/production')

recursive: [Boolean]

fetchに何も指定していない場合、もしくは:pathを指定した場合に利用する。
指定したパス階層以下のパラメータをすべて取得する。
下の例では/myapp/web/production以下すべてのパラメータが取得される。

AwsSsmEnv.load(path: '/myapp/web/production', recursive: true)

begins_with: [String, Array]

fetch:begins_withを指定した場合は必須となる。
取得するパラメータ名のプレフィクスを指定する。配列で複数指定することも可能(OR条件となる)。
下の例ではmyapp.web.productionで始まる名前のパラメータが取得される。

AwsSsmEnv.load(path: 'myapp.web.production')

removed_prefix: [String]

naming:snakecaseを指定した場合に利用される。
環境変数名から除外するパラメータ名のプレフィクスを指定する。
:removed_prefixが指定されておらず、:begins_withもしくは:pathが指定されていた場合はそれを利用する。

delimiter: [String, Regexp]

naming:snakecaseを指定した場合に利用される。
アンダースコアに変換する文字列もしくは正規表現を指定する。
デフォルトはスラッシュ(/)。

fetch_size: [Integer]

一度のAWS API実行で取得するパラメータ数を指定する。 :path指定の場合は最大値は10でデフォルトも10
:begins_with指定の場合は最大値は50でデフォルトも50である。通常このパラメータを指定することはない。

Motivation

RailsアプリケーションをECSで起動する場合、環境変数を渡すのが面倒だったので作りました。

Security

シークレット情報を取得するための権限を付与しなければならないため、セキュリティ運用には十分な注意が必要です。

EC2インスタンスプロファイルが設定されていた場合、そのEC2上であればどのアカウントでもパラメータが見えるようになるため、
EC2インスタンスプロファイルとは別にIAMユーザを用意するなどセキュリティレベルを上げる工夫が必要です。

EC2にログインできるのが管理者のみであればファイルで持つのと大差ありません。

AWS Fargateであればコンテナ上でコマンドの実行はできないので、シークレット情報が見られることはありません。

License

Apache License 2.0

Contributors