gokigenmaruのブログ

40から始めるクラウドエンジニア

MFAを設定しなければ権限が付与されないポリシー

こんにちわ、ごきげんまるです。
やる気があるうちにいろいろと書こうと思っています。じゃないとまた書かなくなるのが目に見えているので…。

今日はMFAを設定しなければ権限が付与されないポリシーの説明です。
なぜこんなポリシーが必要かと言うと…。

AWSではコンソールログイン時にMFAを強制することは出来ない

語弊を恐れずに言うと、AWSではコンソールログイン時にMFAを強制するように設定することは出来ません。
個々のIAMユーザに対し、MFAの画面を強制的に出すことは出来ません。
Projectの方針でMFAが必須となった際に色々と調べたのですが、MFAの設定は個々のIAMユーザが個人的に設定する以外、管理者側等で強制的にMFAを設定することは出来ないのです。
※もしやれる方法があったら教えてください。

これで困りました。
というのも、MFAをProjectの方針で「やらないとダメ!」としても、実際の設定は個人に委ねられてしまっている以上、「忘れた」、「面倒くさい」、「やる必要を感じない」みたいに個人が考え、設定をしてくれないとProjectの方針通りとなりません。

じゃあどうしようとなった時に使える方法があります。
それが「ログイン時にMFA認証を通ってない場合、AWSの操作権限がなくなる」ということができます。
具体的にはMFAを通っていない場合に必要以外の権限をすべてDenyするポリシーをIAMユーザやグループにアタッチすることで、各担当者にMFAの設定をしてもらうようにするとなります。

MFAをしないと権限がもらえないポリシー

このポリシーはAWSから提供されています。
以下のWebサイトにポリシーの内容(JSON)がありますので、この内容でポリシーを作成し、ポリシー名を好きにつけてあげ、(例えばForce_policy)IAMユーザやグループにアタッチすればOKです。
docs.aws.amazon.com

下にポリシーを記載しますが、この内容はAWS側でupdateが入っていく可能性があるので、できれば上記リンクから最新の情報を持ってくることをお勧めします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowViewAccountInfo",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },       
        {
            "Sid": "AllowManageOwnPasswords",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:GetUser"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSigningCertificates",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSigningCertificate",
                "iam:ListSigningCertificates",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSSHPublicKeys",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSSHPublicKey",
                "iam:GetSSHPublicKey",
                "iam:ListSSHPublicKeys",
                "iam:UpdateSSHPublicKey",
                "iam:UploadSSHPublicKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnGitCredentials",
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ListServiceSpecificCredentials",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnUserMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

ここに書かれている内容については上記AWSのサイトに説明が載っているので見ていただくのがいいと思います。
ざっくり説明をすると、

MFAでログインすれば許可するもの
"Sid": "AllowViewAccountInfo",
"Sid": "AllowManageOwnPasswords",
"Sid": "AllowManageOwnAccessKeys",
"Sid": "AllowManageOwnSigningCertificates",
"Sid": "AllowManageOwnSSHPublicKeys",
"Sid": "AllowManageOwnGitCredentials",
"Sid": "AllowManageOwnVirtualMFADevice",
"Sid": "AllowManageOwnUserMFA",

MFAでログインしていない場合はこのアクション以外許可しないもの
"Sid": "DenyAllExceptListedIfNoMFA",

これはポリシーなので、IAMユーザにはほかにもポリシーを付けてAWSの操作を許可していますが、ポイントは他につけているポリシー(PowerUserAccess等)もMFAでログインしていない場合は拒否されるということです。
これはこのポリシーの書き方にミソがあるのですが、その前にAWSのポリシーの論理評価方法について、超簡単に説明します。

"AWSの場合、許可と拒否が一緒に設定されている場合は拒否となります"

この拒否には2つのパターンが存在しています。
・明示的な拒否
・暗黙の拒否
明示的な拒否は、「このアクションを拒否する」という書き方です。
暗黙の拒否は「許可するリストに入れていない」という書き方です。
暗黙の拒否は明示的に記載をしていないので、書いてないから許可していないという緩い拒否なのですが、明示的な拒否は、このアクションはさせない!という強い意志の拒否です。
このあたりの書き方はポリシーを作る方は覚えた方がいいです。ちなみにAWSではIAMポリシーの設定以外にもよく明示的な拒否と暗黙の拒否は出てきます。

じゃあ上記のMFAをしないと権限がもらえないポリシーでは何がミソなのかというと、"Sid": "DenyAllExceptListedIfNoMFA",の書き方が明示的な拒否になっているということです。

"Sid": "DenyAllExceptListedIfNoMFA",では、以下のように書いています。

            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [

これは、
・EffectでDenyをすることで、このルールは明示的な拒否であることを宣言
・NotActionとすることで、これ以降に記載したアクションは許可され、記載していないアクションは全て明示的な拒否となる。
ということです。
当初このポリシーを使い始めたときはあまり考えていなかったのですが、ある程度慣れてきてからに改めて見ると、なるほどなぁと感心しました。
これをすることで、MFAを使ってログインしたとき以外は必要な権限が付与されない状態を作ることができます。

じゃあ権限が付与されていない場合ってどうなるのかというと、簡単には
・ログインは出来ます
・ログインした後にEC2とかのページに行くと何も表示されません
・MFAなしでやれる操作はDenyAllExceptListedIfNoMFA に書かれている操作のみです
こんな感じになります。
実際にMFAなしでログインすると、以下のように感じになります。

f:id:gokigenmaru:20210524101654p:plain
MFAポリシー(権限が無い場合)

実はちょっと古い画像なのですが、こんな感じで「表示する権限がありません」という風になります。

このポリシーはAWSCLIに対しても有効となる

ちょいと厄介なのですが、このMFAポリシーはAWSCLIを使うときにも有効になります。
つまり、AWSCLIでちょっとRoute53のレコード屋DynamoDBのテーブルのリストを取ろうかなと考えてやると、以下のようなエラーが出て何もできません。

An error occurred (AccessDenied) when calling the ListResourceRecordSets operation: User: arn:aws:iam::xxxxxxxxxxxx:user/xxxxxxxxxxxxxxxxxxx is not authorized to access this resource

なので、AWSCLIを使うときもMFA認証が必要です。
以下のAWSの技術サイトにやり方がありますが、
aws.amazon.com

今だとこちらのサイトの方が見やすいかもですね。
oji-cloud.net


MFAをしないと権限がもらえないポリシーの問題点

このポリシーにはいくつかの問題があります。

パスワードの変更が出来ません

このポリシーですが、MFAなしの場合はパスワード変更が出来ません。
例えばですが、以下の場合とかは考慮が必要です。

1.とあるProjectのIAMユーザの担当者が依頼に基づき新規でIAMユーザの作成を実施
2.作成したユーザはカスタムパスワードで初回ログイン用パスワードを作成し、次回ログイン時にパスワードのリセットおよび新しいパスワードの作成を実施させる
3.このポリシーを作成したユーザにアタッチする

とすると、このポリシーではパスワードの変更が許可されていないため、初回パスワード変更が出来ずに以降ログインができなくなります。
対応策としては、このポリシーの「DenyAllExceptListedIfNoMFA」に「iam:ChangePassword」 と 「iam:GetAccountPasswordPolicy」を付けてあげるとパスワード変更が出来るようになりますが、実はAWSではMFAをしていないユーザがパスワードを変更出来るようにすることを推奨していません。
シングル認証で入った人がアカウントのパスワードを変更できるというのはリスクという考え方なのかなぁと思います。
AWSでは、新しいIAMユーザの人が初回ログインをした後にこのポリシーを設定することを推奨しています。
正しいと思いますが、運用としてはかなり面倒です。。。

初回ログインしたらこのポリシーをアタッチするスクリプトを書いて、毎日実行すれば解決!?

MFAデバイスの故障・交換等が発生するとログイン出来ない

このポリシーというよりかはMFA設定の問題点ですが、MFAデバイスの故障や交換をするとログインできなくなります。
例えば昨日のブログで仮想MFAデバイスとしてAuthyやGoogle Authenticatorを紹介していますが、これはスマホアプリを利用しています。
スマホの買い替えや故障などで端末が変わってしまった場合、MFA認証が出来ず、AWSにログインができなくなります。
その場合はIAM Fullの権限を持った人がMFAの設定を解除してあげる必要があります。

プログラム用や共通のアカウントにかけると大変

プログラム内でAWSにログインする場合、よくプログラム用のIAMユーザ(AWSコンソールにはログイン出来ない、ログイン用にアクセスキーIDとシークレットアクセスキーを発行し、その情報でログインする)を作成し対応しますが、このプログラム用のIAMユーザに今回のMFAポリシーをアタッチするとAWSにログインが出来なくなり結果プログラムが動かなくなります。運用中の環境だと大惨事です。。。
その為、このポリシーは個人のIAMユーザにのみアタッチしプログラム用にはアタッチしないようにしなければならず、一律適用が出来なくてちょっと面倒です。
まぁ、これは仕方がないので、運用でカバーするしかないのかなと思います。



ということでざっくりMFAをしないと権限がもらえないポリシーについて書きました。
MFAは今や必須のセキュリティ対策です。簡単に出来るのでどんどんやっていきましょー