AWS で MFA 必須の IAM アカウントを作成し、ユーザに受け渡す手順について

多段階認証 (MFA) が必須の IAM アカウントを作成して、ユーザに受け渡す手順について。

## MFA 必須でパスワードリセット可のポリシーについて

公式のドキュメントは以下の2つです。

 * https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/tutorial_users-self-manage-mfa-and-creds.html
 * https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_aws_my-sec-creds-self-manage.html

基本的にはドキュメントの通りにやればいいのですが、このままだと、初回のパスワードリセットが許可されません。

[このドキュメント](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_aws_my-sec-creds-self-manage.html)の注記にあるとおり、以下の2つを `DenyAllExceptListedIfNoMFA` に追加します。

 * `iam:ChangePassword`
 * `iam:GetAccountPasswordPolicy`

また、[こちらの記事](https://blog.serverworks.co.jp/change-iam-policy-for-multiple-mfa-devices)を参考に、MFA のデバイス名をユーザ名のみに制限したほうが良いかもしれません。`*` のままだと他人の MFA を削除できてしまいます。

```
            "Resource": "arn:aws:iam::*:mfa/*"
            ↓
            "Resource": "arn:aws:iam::*:mfa/${aws:username}"
```

ただし、ユーザにデバイス名を正しく入力してもらう必要があり、運用は難しくなります。IAM の MFA を削除しても時間稼ぎにしかならなそうな気もします。

## 初期ユーザ用・MFA 設定済みユーザ用ポリシー

ポリシーを2つ作ります。

 1. MFA 無しでもパスワードリセットできるポリシー (初期ユーザ用)
 2. パスワードリセットにMFA が必要なポリシー (既存ユーザ用)

最初は1のポリシーを設定し、ユーザが MFA の登録を終えたら2に切り替えます。

### 1. 初期ユーザ用ポリシー

公式ドキュメントのポリシーに、冒頭に書いた、パスワード変更を許可する修正と、デバイス名を制限する修正を行います。JSON は以下のようになります。
(実際に設定するときは、公式のドキュメントからコピペして編集してください)

```json
{
    "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",
                "iam:GetAccessKeyLastUsed"
            ],
            "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"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "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:GetMFADevice",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "sts:GetSessionToken",

                "iam:ChangePassword",
                "iam:GetAccountPasswordPolicy"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}
```

ポリシー名と説明を、以下のように設定します。
説明文に URL が書けないので、後から参照できるように公式ドキュメントのタイトルを書くことにしました。

```
MFARequiredOnlyForTheFirstLogin
Created YYYY-MM-DD - Copied and edited the official sample JSON to enable password reset - AWS Allows MFA-Authenticated IAM Users to Manage Their Own Credentials on the My Security Credentials Page
```

そして、このポリシーだけを設定したグループ `NotYetLoginUsers` を作成します。

### 2. 既存ユーザ用ポリシー

後者のポリシー名と説明は、以下のように設定します。

```
MFARequiredForCurrentUsers
Created YYYY-MM-DD - Copied from the official document - AWS Allows MFA-Authenticated IAM Users to Manage Their Own Credentials on the My Security Credentials Page
```

ポリシーの JSON は、公式ドキュメントのものを使います。

## 自動化

一連の操作を行うスクリプトを以下に作成しました。`setup.sh プロファイル名` でポリシー 2 つと、`NotYetLoginUsers` グループを作成します。

 * https://gist.github.com/koseki/d5d0ce8af3a38bdefc0e92af02bc933b

ポリシーの JSON は古くなっているので、公式のものを使ってください。

## IAM アカウントの受け渡し

以下の手順で受け渡します。

 1. アカウントを作成し、`NotYetLoginUsers` グループにだけ所属させる
 2. メール等で仮のパスワードを伝えて、パスワードリセットと MFA 登録を依頼する
 3. 本人の MFA 登録が確認できたら、`NotYetLoginUsers` グループから外し、`MFARequiredForCurrentUsers` ポリシーと、他に必要な権限を設定する

2 の時点では何も権限がないので、パスワードはメールで送ってもよいことにしています。


## パスワードポリシーの影響について

パスワードポリシーに、ユーザにパスワードの変更を許可する `Allow users to change their own password` というオプションがあります。

<img alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/ea17765a-72e2-d35b-a31d-80b5ce31f46e.png">

このオプションは、**オンにしておく方が良さそう**でした。

オフの場合、

 * ポリシーで明示的に指定しない限り、パスワードを変更できなくなる
 * ユーザ作成時にパスワードリセットを求めると、自動的に `IAMUserChangePassword` が追加される

という挙動になるようです。

<img alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/1f42bd04-4fec-72e6-21d6-454ec2a4fd4b.png">

逆に、ポリシーを設定さえすれば、そちらが優先されるようです。全ユーザに、上記 MFA 必須のポリシーを設定するのであれば、どちらでも基本的には同じ動作になります。

ただし、オフにすると、新規ユーザ作成時に勝手に `IAMUserChangePassword` ポリシーが設定されてしまうので、オンにしておくのが無難だと思います。