電通総研 テックブログ

電通総研が運営する技術ブログ

AWSへのアクセスキーアクセスにMFAを強制するセットアップ

こんにちは。X(クロス)イノベーション本部 ソフトウェアデザインセンター セキュリティグループの耿です。

AWSのマネジメントコンソールへのユーザー認証ではMFAの利用が一般的になってきましたが、アクセスキー利用時にMFAを必須とするには少しコツがいるので、この記事でまとめてみます。

ちなみに Security Hub のコントロール IAM.19「すべての IAM ユーザーに対して MFA を有効にする必要があります」を満たすには、マネジメントコンソールのユーザーだけではなく、アクセスキーで利用するIAMユーザーに対してもMFAの設定をしなければなりません。

前提:IAMユーザーの状態

IAMユーザーを作成し、MFAを設定した状態を前提として、この後の話を進めます。(IAMユーザー・アクセスキーを配布するケースは事前にMFAを設定しておくことはできませんが、この記事では割愛します。)
このユーザーに対してアクセスキーを発行しておきます。

IAMユーザーの状態

~/.aws/credentialstest-user というプロファイル名で、アクセスキーとシークレットアクセスキーを保存しておきます。

[test-user]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <シークレットアクセスキー>
region = ap-northeast-1

通常通りポリシーをつけても、MFA不要でアクセスできてしまう

このユーザーに対して、以下のようなポリシーを付けてみます。
この記事では一貫して ec2:DescribeRegions を例としていますが、一般的なリソースアクセスの許可に適宜置き換えて考えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeRegions",
            "Resource": "*"
        }
    ]
}

通常通りポリシーを付けた状態

このユーザーを利用し DescribeRegions APIをコールすると、MFAを設定しているにも関わらず、MFAコードを入力しなくてもアクセスが成功してしまいました。

APIにアクセスできてしまう

Security Hub のIAM.19コントロールをクリアするだけであれば、このようにMFAを有効にするだけで良いのですが、それだとMFAを利用しなくても権限を使用できてしまいます。せっかくMFAを有効にするのですから、MFAを利用しないとAPIアクセスできないようにきっちり設定したいですね。

MFAを必須にする方法1:ユーザーポリシーに条件を追加する

MFAの利用を必須とする方法の1つ目として、IAMユーザーのポリシーを次のように書き換えます。Condition 句を追加し、aws:MultiFactorAuthPresent を利用してMFAを行ったことを条件とします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeRegions",
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        }
    ]
}

再び DescribeRegions APIをコールすると、MFAを利用していないので今度はちゃんとエラーとなります。

APIにアクセスできてしまう

MFAを利用するためには STSGetSessionToken APIを呼び出し、一時的な認証情報を取得します。
このとき --serial-number オプションには設定した MFA デバイスの識別子(ARN)を、--token-code にはMFAトークンを指定します。

GetSessionToken API呼び出し

発行された一時的な認証情報を使用するために ~/.aws/credentials に新しくプロファイルを作成しても良いですが、ここではシンプルに環境変数に設定します。
(環境変数に設定されたAWS認証情報は、認証情報ファイルよりも優先されます。)

環境変数に認証情報を設定

そして環境変数に設定した認証情報を利用してもらうために、AWS CLI--profile オプションをつけずにDescribeRegions APIをコールすると、再びアクセスできるようになりました。

APIにアクセス可能になった

以上の方法でMFAの利用を必須にはできましたが、

  • GetSessionToken APIを呼び出して一時的な認証情報を取得する
  • 取得した認証情報を利用するように設定する

といった手間がかかってしまいます。

MFAを必須にする方法2:スイッチロールを利用し、コードの取得を簡単にする

上で説明した手間を省くために、スイッチロールを利用してMFAを強制する方法があります。

IAMロールを1つ作成します。信頼ポリシーでは同じアカウントからの AssumeRole を許可すると同時に、aws:MultiFactorAuthPresent を利用してMFAを行ったことを条件とします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<アカウントID>:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        }
    ]
}

IAMロールの信頼ポリシー

このロールの許可ポリシーは、上で作成したユーザーと同じとします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeRegions",
            "Resource": "*"
        }
    ]
}

IAMロールの許可ポリシー

そして元のIAMユーザーの許可ポリシーから DescribeRegions の許可を除外し、作成した IAMロールへの AssumeRole のみを許可します。(aws:MultiFactorAuthPresent の条件も不要です。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<アカウントID>:role/<ロール名>",
            "Effect": "Allow"
        }
    ]
}

スイッチロールを簡単にするために、 ~/.aws/config に以下のように追記します。 [profile test-user] の部分は ~/.aws/credentials に記載したプロファイル名 test-user と名前を合わせる必要があります。

[profile test-user]
region = ap-northeast-1

[profile my-test-role] 
region = ap-northeast-1
source_profile = test-user
role_arn = arn:aws:iam::<アカウントID>:role/<スイッチロール用のロール名>
mfa_serial = arn:aws:iam::<アカウントID>:mfa/<デバイス名>

DescribeRegions APIを呼び出す時に、スイッチロールした先のプロファイル名(ここでは my-test-role)を指定すると、そのままMFAコードを尋ねられます。入力するとAPI実行が成功します。

スイッチロールしてAPI実行

この方法の良いところは、実行したいAPIをそのままコールすればよく、デバイスの識別子(ARN)も ~/.aws/config にあらかじめ保存してあるので毎回入力する必要がないことです。

Tips:OSSツールAWSumeで簡単にスイッチロール

MFAを強制するスイッチロールを簡単にするツールとして、OSSAWSume もおすすめです。
必要なロール・ポリシー構成は「方法2」と同じで、ローカルでAWS SDKを利用する時にも一時的なクレデンシャル情報を簡単なコマンドでセットアップできたりするので便利です。

セットアップ後、awsume <プロファイル名> で一時的な認証情報を取得でき、MFAが必要な場合はここで尋ねられます。そのあとは、一時的な認証情報を利用してAPIコールできるようになります。

AWSumeでスイッチロール

まとめ

これまで説明したパターンを図にまとめてみました。

MFAのパターン

IAMアクセスキーは漏えいしないように細心の注意を払うことが大前提ですが、万が一漏えいしたとしてもすぐに使えないように、MFAで二重のガードをしておきましょう。


私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。

セキュリティエンジニア

執筆:@kou.kinyo、レビュー:寺山 輝 (@terayama.akira)
Shodoで執筆されました