フューチャー技術ブログ

AWSアクセスキー管理について考えてみる

はじめに

Technology Innovation Groupの神崎です。

社内Slackにてユーザーが利用するAWSアクセスキー管理のベスプラとはなんぞや?という話題が出たことをキッカケにして、改めて自分の理解の棚卸も兼ねて、どういう管理方法をすればいいのかを考えてみました。

そもそもAWSアクセスキーを使わないのがベスプラではあるのは間違いないのですが、 顧客へのアカウント引き渡し等がある、フェデレーション先に基盤がある社内環境と繋がっていないなど、様々な理由でアクセスキーを使わざるを得ない場面はあり、そういった場面でどうすべきかを整理しました。

TL;DR

  • アクセスキーは、STSトークン(Security Token Serviceトークン)を取得する時のみ利用する
  • STSトークン取得時は、アクセスキーだけでなく、MFAによる認証を必須とする
  • アクセスキーやSTSトークンはプレーンテキストで保存せず、暗号化して保存する

上記を満たすツールとして、aws-vaultを使う!

頭の体操編

どのようなツールを使えばよいのか?を考える前に、要件めいたものを整理してみたいと思います。

認証情報管理の考え方

AWSアクセスキーに限らずですが、認証の設計を行うに当たっては、認証の強度など、”認証”が”認証”として働くために必要な機能を考える他に、認証情報が漏れてしまった時の被害範囲の最小化と、認証情報を取り扱う際の利便性のバランスを考える必要があります。

例えば、みなさんも馴染み深い認証連携の仕組みであるSingle Sign-On(SSO)では、ID・パスワードという半永続的な認証情報を直接連携せず、代わりにCookieであったり、トークンであったり(それがXMLだったりJWTだったりはするのですが)を使って一時的な認証情報を作り出してやり取りをします。

一時的な認証情報を利用することで、仮にその認証情報が悪用されたとしても、被害範囲を小さくすることができます。それは、トークンの有効期限であったり、相手によって渡すトークンを変えることによって実現されます。これが仮にID・パスワードを直接連携するようなレガシーなシステムであると、そのシステムに侵入されてしまったら最後、その認証情報がアクセスできる範囲すべてへのアクセス権を得ることと同義になってしまいます。

一方、認証情報を何回も入力することはストレスです。一時的な認証情報を使うことがセキュリティ上どんなに好ましかったとしても、毎度毎度認証が要求されるとそれを回避するモチベーションが出てきてしまいます。そのため、SSOの仕組みでは、IdPがユーザーの認証を代行した後は、システムが自動的に一時的な認証情報を作り出しやり取りを行い、個別に認証情報を入力する手間が省かれています。

上記のような一時的な認証情報がやり取りできる前提で、半永続的な認証情報を利用する際は本人確認を厳格に行う、具体的には複数の要素を利用した認証を行うことがスタンダードになってきています。そうすることで、認証の強度を上げつつも、利便性を損なわないような仕組みが可能となっています。

AWSの世界では?

Well-architected Frameworkでは以下のように書かれています。

SEC02-BP02 一時的な認証情報を使用する

一般的なアンチパターン:
開発者が、フェデレーションを使って CLI から一時的な認証情報を取得するのではなく、IAM ユーザーからの長期的なアクセスキーを使用する。

そもそもアクセスキーを使うべきではない、というのが公式回答ではありますが、冒頭述べたような理由でどうしてもアクセスキーが必要な場合は、どうすべきかを考えてみます。
先ほど述べた、①認証の強度、②被害範囲の最小化、③利便性の獲得の3つの観点から何ができるかを整理します。

①認証の強度

アクセスキー利用時に必要な認証強度を考えてみます。
まずは、通常のコンソールアクセス時にどのような制限をかけるべきかを参考にしましょう。

SEC02-BP01 強力なサインインメカニズムを使用する

IAM ポリシーを作成して、MFA サインインを適用し、ユーザーが自分のパスワードと MFA デバイスを管理できるようにします。

何かしらの手段で他要素での認証をかけることは必須と言えそうです。

しかし、アクセスキーを利用する場合は、一部の操作を除き、多要素での認証を行うことができません1。たとえ、ログイン用にMFAデバイスの登録がある場合でも、それを使い追加認証をする手段が現時点ではありません。

そのためアクセスキーを使う場合は必要な認証の強度を実現できないという前提に立つ必要があります。

②被害範囲の最小化

被害範囲の最小化を実現するためには、前段で述べたように、認証情報を利用して作業ができる範囲を絞ることが必要です。

①の場合と同様に、通常のコンソールアクセス時にどのような制限をかけるべきかを参考にします。

IAM: IAM ユーザーに MFA デバイスの自己管理を許可する 2

{
"Sid": "BlockMostAccessUnlessSignedInWithMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ListMFADevices",
"iam:ListUsers",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}

上記ポリシー例では、MFAデバイスなしでサインインしている場合は、MFAデバイスの登録に必要な権限のみを与え、他の作業を行う際はMFAを必須としています。
「アクセスキーを単独で利用している状態」は「MFAデバイスなしでサインインしている状態」と同じですので、アクセスキーを使う場合についても、自身のロックアウトを防ぐ権限以外は付与しないことが、被害拡大の防止の観点からは必要と言えます。

また、本記事の趣旨と外れますが、アクセスを許可するIPアドレスを絞ることも強力な手段の一つです。

③利便性の獲得

①・②の考察を総合すると通常の作業を行う際はMFA認証を行った状態で作業を行うことが要求されそうです。

前述の通り、アクセスキー+MFAという組み合わせで認証を行うことはできませんので、STSを利用した一時的な認証情報3が必要となります。

また、合わせて、認証情報の保管についても考慮が必要です。

SEC02-BP03 シークレットを安全に保存して使用する

一般的なアンチパターン:
認証情報をローテーションしない。
ソースコードまたは設定ファイルに長期的認証情報を保管する。
認証情報を暗号化せずに保管する。

通常、aws configure等で設定をしたアクセスキーは~/.aws/credentialsに平文で保存されてしまいますが、こちらは非推奨な設定であって、アクセスキー自体の保管にもシェルスクリプトやツールを使った暗号化が必要そうです。

また、セッショントークンには有効期限があるため、実利用時には現時点で取得できているセッショントークンの有効期限の管理を組み込む必要があります。

実践編

上記を簡単に解決する手段として、aws-vaultを使うことができます。

このツールは、アクセスキーの暗号化ができるだけではなく、セッショントークンの取得や暗号化、有効期限管理と自動更新、さらにあたかもEC2内のトークンサーバーのように動く機能を持つなど、認証情報管理の十徳ナイフ的なツールです。また、暗号化のバックエンドを選ぶことができ、Windows Credential Manager、OS X Keychain、Gnome Keyringなど、各OSに付随する暗号化サービスを利用することができます。

どのような設定で利用すれば、先に述べた要求を満たすツールとなるのでしょうか。

STSを取得するようにする

aws-vaultはデフォルトでsts get-session-tokenを使ったSTSを利用します。
ただし、MFAを利用して認証をかけるためには~/.aws/configファイルにmfa_serial設定が必要になります。

[profile jumpuser]
mfa_serial=arn:aws:iam::{account-id}:mfa/{mfa-device}

こちらは、AssumeRoleをする場合も同様で、mfa_serialがあるプロファイルに関しては、sts assume-roleにてSTSを取得しますが、ないプロファイルについてはSTSを取得しにいきません。

# STSを使う
[profile dev]
source_profile=jumpuser
role_arn=arn:aws:iam::{account-id}:role/{role-name}
mfa_serial=arn:aws:iam::{account-id}:mfa/{mfa-device}

# STSを使わない
[profile dev]
source_profile=jumpuser
role_arn=arn:aws:iam::{account-id}:role/{role-name}

わかりにくいですが、ここに詳細が載っています。

WSL2上で使うには…?

WindowsのWSL2を利用する方も多いと思います。

その際の悩ましい問題として、暗号化バックエンドとしてWindowsを使うべきか、Linuxを使うべきかの問題があると思います。
結論、どちらでもよいと思う4のですが、簡単にPros/Consをまとめてみました。

比較観点 Windows Credential Manager pass (参考) Secret Service
aws-vaultを実行できる環境 Windowsのみ Linuxのみ Linuxのみ
暗号キーの管理 ✅️不要(OS側で管理) ⚠️自身で管理が必要 ✅️不要(OS側で管理)
復号化できる人 ログインユーザー GPG鍵へのアクセス権を持つ人 ログインユーザー
WSL2上での動作 Windowsバイナリを実行させる形で可能 WSL上で通常のLinuxコマンドと同様に実行 WSLg等の設定が必要かも
その他考慮点 aws-vault execがcmd.exeを呼ぶため、execコマンドを使いたい場面では不適 Windows上でawsコマンドを打ちたい場合は不適 WSLgとDBUSの相性がよくないため、うまく動かすまで時間がかかる印象

私はWindows Credential Managerを使うのがわかりやすいと感じましたので、その設定例を示します。

WSLを越境してCredential Managerを使う

このケースでは、Windows側の%UserProfile%\.aws\configファイルと、Linux側の~/.aws/configファイルをお互いに意識して管理することが必要です。

仕組みとしては、Windows側で認証情報の管理を行い、Linux側でaws-cli等を呼び出す場合は、credential_processでaws-vault.exeを叩いて、セッショントークンをWindows側から取得するようにします。

こうすることで、Linux側では通常のように--profileオプションによるAssumeRoleを行いつつ、アクセスキーや取得した一時認証情報をWindows側に保持し続けることができます。

# Windows側のファイル
[default]
region=ap-northeast-1

[profile jumpuser]
mfa_serial=arn:aws:iam::{account-id}:mfa/{mfa-device}

[profile dev]
include_profile=jumpuser
source_profile=jumpuser
role_arn=arn:aws:iam::{account-id}:role/{role-name}
# Linux側のファイル
[default]
region=ap-northeast-1

[profile dev]
credential_process=aws-vault.exe export --format=json dev

さいごに

今回初めての寄稿だったのですが、普段の業務で感覚値的にはこうだろうと思っていることを紐解いて、改めてなんでなんだっけ?と考えるのは非常によい機会になりました。

改めてWell-architected Frameworkを読み込んだり、aws-vaultのマニュアルを読んだり、あるいはWindows Credential Managerの仕様を調べたりしてみて、一次リソースを読むのはやっぱり大事だなと思うと共に、実はこうするのがよかったのでは?ということも色々発見できたので、やはり感覚だけではなく、人に読んで貰う読み物を作るという行為は大事だなと改めて感じました。


  1. 1.STSを取得する場合を除く
  2. 2.iam:ListUsersがなくとも、iam:GetUserに変更することで、さらに最小権限の設定をすることができます
  3. 3.aws sts get-session-tokenaws sts assume-roleなどで得られる
  4. 4.keyctlへも対応しているため、WSL上でTPMが利用できるようになったタイミングではkeyctlを使う方式がよくなると思われるが、現時点ではTPM上の暗号化キーを使えるバックエンドがない