FLINTERS Engineer's Blog

FLINTERSのエンジニアが綴る技術ブログ

aws-vault でアクセスキーを安全に

あけましておめでとうございます。河内です。

数ヶ月前に aws-vault を使い始めて安全の高まりを感じるので紹介します。

AWSのサービス上では IAM Role をできるだけ使ってアクセスキーを使わないようにしていますが、ローカルでの開発時にIAMのアクセスキーを利用することはあります。 アクセスキーが漏れると困ったことになります。 本番環境で利用している AWS アカウントで漏れると、付与している権限次第では機密情報が漏洩したりサービスが壊される可能性があります。 また開発環境としてのみ利用しているAWSアカウントであっても、ビットコインのマイニングなどで容赦なくリソースを消費され高額な請求が来たりする可能性ががあります。 そのようなことが起きないようにアクセスキーは安全に管理する必要があります。

通常はAWS SDK のデフォルト参照先である ~/.aws/credentials にアクセスキーを保存しますが、平文で保存しているためディスクが盗難にあった場合に不安が残ります。 aws-vault は KeyChain にアクセスキーを保存します。 ディスクを暗号化していない場合にはこれだけでも少し安心感が増します*1

アクセスキーは開発中のプログラムで利用することが多いと思います。 その中で利用しているライブラリやフレームワークに悪さをするものが居ないと言い切れるでしょうか? 開発中には新しいライブラリを試す機会も多くあります。 もしアクセスキーを抜き出して外部に送信するコードが混じっていると、、怖いですね。 昨年は有名ライブラリに似た名前の malware が npm に公開されたことがニュースになりました。

The npm Blog — `crossenv` malware on the npm registry

不審なプログラムを不用心に実行することはほとんど無いと思いますが、開発中のプログラムだけではなく同一ホスト上で実行するいずれかのプログラムが悪さをする可能性もあります。

aws-vaultSTS を利用し一時的なアクセスキーをプロセスに渡します。 プロセスが悪さをした場合には漏れる可能性がありますが、その場合でも攻撃者がアクセスキーを利用できるのは短時間です。

使い方

さて使い方を見ていきましょう。 brew cask でインストールできます。

$ brew cask install aws-vault

home というプロファイルでアクセスキーを登録します。

$ aws-vault add home
Enter Access Key ID: AKIAIOSFODNN7EXAMPLE
Enter Secret Access Key: 

aws-vault ls で登録したプロフィールが確認できます。

$ aws-vault ls 
home

aws-vault から一時的なアクセスキーをわたしてプロセスを実行します。

$ aws-vault exec home -- aws s3 ls

--debug を付けて実行すると動作の様子が出力されます。

$ aws-vault exec --debug home aws s3 ls
2018/01/06 23:10:57 Parsing config file /Users/kawachi/.aws/config
2018/01/06 23:10:57 Session not found in keyring
2018/01/06 23:10:57 Looking up keyring for home
2018/01/06 23:10:57 Getting new session token for profile home
2018/01/06 23:10:58 Writing session for home to keyring
2018/01/06 23:10:58 Adding service="aws-vault", label="aws-vault session for home", account="home session (30366365326436383761)" to osx keychain aws-vault.keychain
2018/01/06 23:10:58 Using session ****************RNWQ, expires in 3h59m59.105955s
2018/01/06 23:10:58 Parsing config file /Users/kawachi/.aws/config
2018/01/06 23:10:58 Setting subprocess env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
2018/01/06 23:10:58 Setting subprocess env: AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN

一時的なアクセスキーが発行され、 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN がプロセスの環境変数に設定されていることがわかります。 AWS SDK のデフォルトでは認証情報としてこれらの環境変数が参照されます。 AWS SDK for Java に関しては以前 AWS SDK for Java がデフォルトで参照する credential に書きましたのでご参照ください。

基本的な使い方はここまでです。 シンプルですね。良い。

AWS management console へのログイン

aws-vault login <profile>AWS management console へログインできます。

$ aws-vault login home
# ログインした状態でブラウザが開く

これで嬉しいのは IAM ユーザにパスワードが必要ないこと。 アクセスキーさえあれば良いのです。 パスワードが無ければ漏れることもない。 またひとつ安全になりました。

内部的には フェデレーションユーザーに対して AWS マネジメントコンソール へのアクセスを許可する URL の作成 が行われています。

AssumeRole

IAMユーザには最低限の権限を付与しておき、必要な時に多くの権限を持つロールに AssumeRole するという運用をしていることもあるかと思います。 ~/.aws/config に AssumeRole 用の設定を書いておけば、その名前で aws-vault exec できます。

たとえば arn:aws:iam::123456789012:role/admin という IAM ロールを作成しておき、 home プロファイルから AssumeRole するとします。 次の内容を ~/.aws/config に記述します。

[profile home-admin]
source_profile = home
role_arn = arn:aws:iam::123456789012:role/admin

次のように利用できます。

$ aws-vault exec home-admin -- some-command

長時間プロセスの実行

aws s3 ls のように短時間で終了するプロセスは問題ないのですが、長時間動作し続けるプロセスを aws-vault 経由で動作させたいこともあります。 aws-vault exec は一時的なアクセスキーを取得してプロセスへ渡すため、長時間動作し続けるプロセスの場合には途中でアクセスキーの期限が切れることがあります。

AssumeRole を使わない場合は GetSessionToken API を利用するので36時間まで有効期限を伸ばせます。 AssumeRole を使う場合は伸ばせるのは1時間までです。 (参考: AWS STS API の比較) それぞれ -t あるいは --session-ttl オプションと --assume-role-ttl オプションを aws-vault exec 時に指定します。

伸ばせる上限以上の時間に渡って処理が続く場合には -s あるいは --server オプションを付けて aws-vault exec を実行します。 するとローカルに インスタンスメタデータ サーバが立ち上がります*2。 loopback device に 169.254.169.254 を割り当てて 169.254.169.254:80 としてサーバが立ち上がるので、sudo のパスワードが必要になります。

AWS SDK はこのサーバにアクセスして一時的なアクセスキーを受取ります。 この方法の良い点は、アクセスキーの期限が切れた場合に自動的に再取得するようにAWS SDK が実装されている点です。 そのため、長時間プロセスでも問題なく動作します。

$ aws-vault exec -s home -- long-running-process

インスタンスメタデータサーバにアクセスしてみると確かに一時的なアクセスキーが取得できます。

$ aws-vault exec -s home -- curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/local-credentials | jq
{
  "AccessKeyId": "ASIAEXAMPLE",
  "Code": "Success",
  "Expiration": "2018-01-07T17:04:27Z",
  "LastUpdated": "2018-01-07T22:27:02Z",
  "SecretAccessKey": "N+3gexample",
  "Token": "FQoDexample==",
  "Type": "AWS-HMAC"
}

一方でこの方法で動かせるプロセスは同時にひとつまでです。ふたつめ以降の aws-vault exec -s を並列して実行しようとすると、 address already in use でエラーとなります。

aws-vault exec 終了後も aws-vault server プロセスがのこっていることが確認できます。

$ ps ax | grep aws-vault
16482 s002  S      0:00.00 sudo -b aws-vault server
16483 s002  S      0:00.17 aws-vault server

AWS SDK のデフォルトではインスタンスメタデータより ~/.aws/credentials の方が先にアクセスされます。 従って ~/.aws/credentials が存在していると期待したとおりに動かないことがあることには注意が必要です。 平文で残さないことが aws-vault の良い点でもあるので、全てを aws-vault add したら ~/.aws/credentials は消してしまうのが良いでしょう。

MFA

Multi-Factor Authentication (多要素認証) を有効にしている場合には ~/.aws/config に次のように書きます。

[profile home]
mfa_serial = arn:aws:iam::123456789012:mfa/home

すると aws-vault exec 時にトークンの入力を求められます。

生のアクセスキーを使いたい時

aws-vault exec の際に -n あるいは --no-session を付けることで、KeyChain に保存した生のアクセスキーをプロセスに渡すことが出来ます。 「せっかく一時的なアクセスキーを利用する仕組みがあるのに、生を使いたくなることあるの?」という声が聞こえてきそうです。

多く場合で一時的なアクセスキーを利用できますが、次のパターンはありそうです。

MFA を設定していない IAM ユーザで、AssumeRole しない状態で IAM の API を呼び出したいとき。 AssumeRole しない場合は GetSessionToken を用いて一時的なアクセスキーを得ます。 GetSessionToken で得られたアクセスキーはMFA を使用しない場合に IAM の API を呼び出せないという制限があります。 terraform で IAM API を呼び出したいがMFA設定していない…ということがありました。

長時間実行したいプロセスがふたつ以上あるとき。 -s によるメタデータサーバは同時にひとつしか実行できないので、ふたつ以上の場合には泣く泣く生を使うことになりそうです。

アクセスキーのローテート

aws-vault rotate <profile> でアクセスキーのローテートができます。

$ aws-vault rotate home
Rotating credentials for profile "home"
Done!

aws-vault rotate に最低限必要な権限は次のとおりです。(READMEより)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:GetUser"
            ],
            "Resource": [
                "arn:aws:iam::*:user/${aws:username}"
            ]
        }
    ]
}

手軽にできるので、頻繁にローテートすると良さそうですね*3

デメリット

デメリットとして次のことが挙げられます。

  • KeyChain のパスワード入力が煩わしい
  • コマンドの入力が長くなる
  • 長時間実行する場合に考慮点がある

というわけで

デメリットもありますが、それに見合うだけの良さがあるのではないでしょうか。 アクセスキーが安全に保管され一時的なアクセスキーが利用されるという点も良いのですが、個人的には management console にログインする際にパスワードが不要な点が気に入っています。 アクセスキー漏洩が心配で少しでも安全にしたい方は利用を検討してみてはいかがでしょう。

github.com

*1:なお弊社では全ての開発者マシンでディスクを暗号化しています。

*2:EC2 にインスタンスプロファイルを設定した場合にアクセスキーを取得する方法と同じです

*3:aws-vault 4.0.0 までは少なくとも私の環境では動作しませんでした。4.1.0 で改善されたようです。