【#awscli】あらかじめ指定した接続元IPアドレス以外からの「AWS CLI v2」実行時はMFA(ワンタイムパスワードでの認証)が必要になるようIAMを設定する #AWS #awscliv2 #jawsug #環境構築 #環境設定

特定の接続元IPアドレス以外からの「AWSコマンドラインインターフェイス(AWS CLI v2)」実行時のみMFA(Multi-Factor Authentication)が必要になるようにIAMの設定をしたので、その際の手順を自分用のメモとしてまとめました。

# MFA(Multi-Factor Authentication:多要素認証)とは

**参考: Wikipedia - 多要素認証**
https://ja.wikipedia.org/wiki/多要素認証

簡単に説明すると次の4つの要素のうち2要素以上を使用して認証を行うことです
1.知る要素
2.持つ要素
3.備える要素
4.場所の要素

認証を多段階で実施しても(多段階認証をしても)、同じ要素での認証ではセキュアではないとされています(例: ”印鑑”と”通帳”など)

ちなみに、
天空の城ラピュタにおける崩壊の呪文は、

* **”バルス”** という呪文を【←知る要素】
* **飛行石の結晶** を持った状態で【←持つ要素】
* **王族の人間** が【←備える要素】
* **ラピュタ内の聖域と呼ばれる場所** で【←場所の要素】

唱えた場合のみ発動するので四要素認証になっています∑d(ﾟ∀ﾟd)ｲｶｽ!!

# MFA(Multi-Factor Authentication)を設定しないとどうなるのか？

「AWS CLI」を使用するためにはアクセスキー(アクセスキーIDとシークレットキーのキーペア)が必要になるのですが、このアクセスキーが漏洩すると怖いことが起こります。

**参考: 初心者がAWSでミスって不正利用されて$6,000請求、泣きそうになったお話。**
https://qiita.com/mochizukikotaro/items/a0e98ff0063a77e7b694

**参考: GitHub に AWS キーペアを上げると抜かれるってほんと？？？試してみよー！**
https://qiita.com/saitotak/items/813ac6c2057ac64d5fef

# AWSではどういった対策ができるのか？

[AWSマネージメントコンソールへのアクセス]時に ”スマーフォンデバイス(TOTP:Time-based One-Time Password)” での認証を有効にする↓というのは比較的知られていますが、「AWS CLI」でも同様に設定することが可能です。

<img width="903" alt="capture_001_20032020_061743.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/0e42bf79-0a03-4228-a824-913b2017faf6.jpeg">

少し違う点は、「AWS CLI」の場合、1コマンド実行するごとにワンタイムパスワード認証を求められるのはツライので、
「AWS Security Token Service (AWS STS)」より払い出された有効期限付き(デフォルト1時間)のセッショントークンを使用するという点です。

まず、前半の部分で「AWS STS」のセッショントークンを払い出す際はMFAを必要とする設定を行います。

加えて、後半の部分で、特定の接続IPアドレスからのアクセス時のみは、ワンタイムパスワードの入力なしでアクセスキー(アクセスキーIDとシークレットキーのキーペア)だけで実行できるようにする設定を行います。

---

# 1. IAMグループとIAMユーザーの作成 (「管理者」側手順)

まず、AWS CLI を実行する際に使用するIAMユーザーを作成します。

設定はIAMポリシーで制御するのですが、AWSのベストプラクティスに従って、IAMユーザーに直接ポリシーをアタッチするのではなく、IAMグループを作成して、作成したIAMグループに対してMFAを有効化するためのポリシーをアタッチしていきます

### □手順 1-1: [Identity and Access Management(IAM)]サービスの[グループ]メニュー画面で[新しいグループの作成]をクリックします

<img width="903" alt="capture_009_09032020_085018.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/e0f6e21b-3474-222c-18a0-c340fdab7699.jpeg">

### □手順 1-2: [グループ名]に任意の名前(ここでは`MyUsers`)を入力し[次のステップ]をクリックします

<img width="903" alt="capture_010_09032020_085048.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/6069b510-e530-c08f-fe60-decf8d8a4f32.jpeg">

### □手順 1-3: [ポリシーのアタッチ]画面で、任意のAWS管理ポリシー(ここでは`PowerUserAccess`ポリシー)を選択し[次のステップ]をクリックします

<img width="903" alt="capture_012_09032020_085544.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/a0be56df-f948-9e3a-defc-1713c39c2a6a.jpeg">

### □手順 1-4: 内容を確認し[グループの作成]をクリックします

<img width="903" alt="capture_013_09032020_085551.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/40bf1ea2-eea9-3b3c-7626-949d9624400e.jpeg">

### □手順 1-5: IAMグループ(`MyUsers`)が作成されたことを確認します

<img width="903" alt="capture_014_09032020_085609.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/ebb22f1f-8f7a-81f3-ed87-d39d0b0cb992.jpeg">

### □手順 1-6: 続いて[Identity and Access Management(IAM)]サービスの[ユーザー]メニュー画面に移り[ユーザーを追加]をクリックします

<img width="903" alt="capture_029_09032020_095629.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/9458f647-30df-156f-81a4-cdbb0b047e0d.jpeg">

### □手順 1-7: [ユーザー名]に任意の名前(ここでは`cli-user`)を入力し[プログラムによるアクセス]にチェックを入れて[次のステップ:アクセス権限]をクリックします

※必要ではないのでチェックを入れていませんが[AWSマネージメントコンソールへのアクセス]にチェックを入れても問題はないと思います

<img width="903" alt="capture_047_09032020_100029.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/e6580bca-b501-8c75-dcb0-10dae270018b.jpeg">

### □手順 1-8: [ユーザーをグループに追加]で先ほど作成したIAMグループを選択し[次のステップ:タグ]をクリックします

<img width="903" alt="capture_048_09032020_100035.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/d6baa381-be30-4a9f-41db-f26bfc2fb26f.jpeg">

### □手順 1-9: 必要に応じてタグを設定し(ここでは何も設定せずに)[次のステップ:確認]をクリックします

<img width="903" alt="capture_049_09032020_100037.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/cbf4dbfd-a0bc-fbca-9d0f-109157d235c2.jpeg">

### □手順 1-10: 内容を確認し[ユーザーの作成]をクリックします

<img width="903" alt="capture_050_09032020_100040.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/bff9bea8-f1a4-bb11-fea8-391b63d60501.jpeg">
 
### □手順 1-11: "成功"のメッセージを確認し[.csvのダウンロード]をクリックして完了です

※ダウンロードした CSVファイル は後ほど使用します

<img width="903" alt="capture_052_09032020_100051.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/a93f57b9-85e5-fe02-2a39-df221deabaa8.jpeg">

---

# 2. 「スマートフォンデバイス」(←持つ要素)によるMFA(多要素認証)の有効化 (「管理者」側手順)

AWSでは「仮想MFAデバイス」と「AWS Security Token Service (AWS STS)」の仕組みを使う事で「AWS CLI」でも「スマートフォンデバイス」(←持つ要素)でのMFA(多要素認証)を実現することができます

### □手順 2-1: 作成したIAMグループ(`MyUsers`)をクリックします

<img width="903" alt="capture_014_09032020_085609.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/ebb22f1f-8f7a-81f3-ed87-d39d0b0cb992.jpeg">

### □手順 2-2: [アクセス許可]タブをクリックします

<img width="903" alt="capture_015_09032020_085614.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/1f66a7ec-c3aa-b4c0-6638-108ed247dd16.jpeg">

### □手順 2-3: [インラインポリシー]の”ここをクリックしてください”部分をクリックします

<img width="903" alt="capture_016_09032020_093219.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/29964faf-fee0-ae77-d5aa-ccaa124220a4.jpeg">

### □手順 2-4: [カスタムポリシー]にチェックを入れます

<img width="903" alt="capture_017_09032020_093245.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/159e29a3-6679-e508-06e1-0ca8eaf23330.jpeg">

### □手順 2-5: [ポリシー名]に任意の名前(ここでは`MyUsersPolicy`)、[ポリシードキュメント]に次の「AWSユーザーガイド」記載の内容を入力して[ポリシー]の適用をクリックします

**AWSユーザーガイド - IAM: IAM ユーザーに MFA デバイスの自己管理を許可する**
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html

```json:MyUsersPolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListActions",
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowIndividualUserToListOnlyTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:ListMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/*",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToManageTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToDeactivateOnlyTheirOwnMFAOnlyWhenUsingMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ],
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "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なしでは実行できないようになります

### □手順 2-6: 「管理者」側手順はこれで終了です

<img width="903" alt="capture_020_09032020_093410.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/b6a028c4-c86f-6174-35ef-fc432bd022dc.jpeg">

# 4. 仮想MFAデバイスの作成 (「ユーザー」側手順)

ここから「AWS CLI v2」を使用する「ユーザー」側の手順です

### □手順 4-1: 「AWS CLI v2」の新機能である`aws configure import`コマンドを使ってIAMユーザー作成時にダウンロードしたCSVファイルからクレデンシャル情報(アクセスキーの情報)を読み取ります

```bash:実行コマンド
aws configure import --csv file://<CSVファイル名>
```

```bash:実行例
[user@workstaion ~]$ aws configure import --csv file://credentials.csv

Successfully imported 1 profile(s)
```

### □手順 4-2: この状態でコマンドを実行すると`Access Denied`となってしまうことを確認します

```bash:実行例
[user@workstaion ~]$ aws s3 ls --proflie "cli-user"

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
```

### □手順 4-3: `aws iam create-virtual-mfa-device`コマンドを実行して仮想MFAデバイスと共有鍵情報が保存されたQRコードを生成します

`--virtual-mfa-device-name`パラメーターに設定する名前はIAMユーザーと同じにしてください

```bash:実行コマンド
aws iam create-virtual-mfa-device --virtual-mfa-device-name "<IAMユーザー名>" --outfile <QRコードを出力する任意のパス> --bootstrap-method "QRCodePNG" --profile "<IAMユーザープロファイル名>"
```

```bash:実行例
[user@workstaion ~]$ aws iam create-virtual-mfa-device --virtual-mfa-device-name "cli-user" --outfile .\QRCode.png --bootstrap-method "QRCodePNG" --profile "cli-user"

{
    "VirtualMFADevice": {
        "SerialNumber": "<作成された仮想MFAデバイスのリソースネーム(ARN)>"
    }
}
```

ここで出力される”仮想MFAデバイスのリソースネーム(ARN)"の値は後ほど使用するのでメモしておきます

AWS管理コンソールからも確認可能です(該当IAMユーザーの[認証情報]タブ)↓

![capture_002_20032020_062701.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/cc0fa643-e193-4e1c-9e51-17c1213d76e6.jpeg)

### □手順 4-4: 「Google 認証システム(Google Authenticator)」などをスマートフォンにインストールして先ほど生成した共有鍵情報が保存されたQRコードを読み取ります

**App Store -  Google Authenticator**
https://apps.apple.com/jp/app/google-authenticator/id388497605

**Google Play -  Google 認証システム**
https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2

### □手順 4-5: `aws iam enable-mfa-device`コマンドを実行して作成した仮想MFAデバイスの有効化(スマートフォンデバイスとの同期)を行います

```bash:実行コマンド
aws iam enable-mfa-device --user-name "<IAMユーザー名>" --serial-number "<仮想MFAデバイスのリソースネーム(ARN)>" --authentication-code1 "<MFAデバイスに表示されている6桁の数字>" --authentication-code2 "<`--authentication-code1`に設定した値の次に表示される6桁の数字>" --profile "<IAMユーザープロファイル名>"
```

```bash:実行例
[user@workstaion ~]$ aws iam enable-mfa-device --user-name "cli-user" --serial-number "<仮想MFAデバイスのリソースネーム(ARN)>" --authentication-code1 "123456" --authentication-code2 "234567" --profile "cli-user"
```

### □手順 4-6: `aws sts get-session-token`コマンドを実行することで有効期限付き(デフォルト1時間)のセッショントークンを取得することができます

```bash:実行コマンド
aws sts get-session-token --serial-number "<仮想MFAデバイスのリソースネーム(ARN)>" --token-code <MFAデバイスに表示されている6桁の数字> --profile "<IAMユーザープロファイル名>"
```

```bash:実行例
[user@workstaion ~]$ aws sts get-session-token --serial-number "<仮想MFAデバイスのリソースネーム(ARN)>" --token-code <MFAデバイスに表示されている6桁の数字> --profile "cli-user"

{
    "Credentials": {
        "AccessKeyId": "<アクセスキーID>",
        "SecretAccessKey": "<シークレットアクセスキー>",
        "SessionToken": "<セッショントークン>",
        "Expiration": "<セッショントークンの有効期限(yyyy-MM-ddTHH:mm:ss+00:00)>"
    }
}
```

### □手順 4-7: `aws sts get-session-token`コマンドで得られた値を次の環境変数に設定します

|環境変数名 |設定する値 |説明 |
|:---|:---|:---|
|AWS_ACCESS_KEY_ID |Credentials.AccessKeyIdの値 |アクセスキーID |
|AWS_SECRET_ACCESS_KEY |Credentials.SecretAccessKeyの値 |シークレットアクセスキー |
|AWS_SESSION_TOKEN |Credentials.SessionTokenの値 |セッショントークン |


#### [MacOSやLinux環境での実行例]`jq`コマンドが使える環境であれば次のようなスクリプトを作成して実行します

```bash:Bashの場合
virtualMFADevice = "<仮想MFAデバイスのリソースネーム(ARN)>"
profileName = "cli-user"

echo "MFAデバイスに表示されている6桁の数字を入力してください"
read tokenCode

sessionToken=$(aws sts get-session-token --serial-number "$virtualMFADevice" --token-code "$tokenCode" --profile "$profileName")

export AWS_ACCESS_KEY_ID=$(echo $sessionToken | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $sessionToken | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $sessionToken | jq -r .Credentials.SessionToken)
```

#### [Windows環境での実行例]PowerShellの`ConvertFrom-Json`コマンドレットを使えばJSONのパースが可能なので次のようなスクリプトを作成して実行します

```powershell:PowerShellの場合
$virtualMFADevice = "<仮想MFAデバイスのリソースネーム(ARN)>"
$profileName = "cli-user"

$tokenCode = Read-Host "MFAデバイスに表示されている6桁の数字を入力してください"

$sessionToken = $(& aws sts get-session-token --serial-number "$virtualMFADevice" --token-code "$tokenCode" --profile "$profileName" | ConvertFrom-Json)

$env:AWS_ACCESS_KEY_ID=$sessionToken.Credentials.AccessKeyID
$env:AWS_SECRET_ACCESS_KEY=$sessionToken.Credentials.SecretAccessKey
$env:AWS_SESSION_TOKEN=$sessionToken.Credentials.SessionToken
```

### □手順 4-8: この状態で「AWS CLI」を実行し Access Denied などのエラーメッセージが表示されなくなっていれば成功です

※この時`--profile`オプションの付与は不要です

```bash:実行例
[user@workstaion ~]$ aws s3 ls

yyyy-MM-dd HH:mm:ss xxxx
:
```

---

# 5. 特定の接続IPアドレス(←場所の要素)によるMFA(多要素認証)の有効化 (「管理者」側手順)

続いて、特定の接続IPアドレスからのアクセス時のみは、ワンタイムパスワードの入力なしでアクセスできるように設定します。

### □手順 5-1: 作成したIAMグループ(`MyUsers`)をクリックします

<img width="903" alt="capture_014_09032020_085609.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/ebb22f1f-8f7a-81f3-ed87-d39d0b0cb992.jpeg">

### □手順 5-2: [アクセス許可]タブをクリックします

<img width="903" alt="capture_015_09032020_085614.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/1f66a7ec-c3aa-b4c0-6638-108ed247dd16.jpeg">

### □手順 5-3: [インラインポリシー]の”ポリシーの編集”部分をクリックします

<img width="903" alt="capture_020_09032020_093410.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44745/b6a028c4-c86f-6174-35ef-fc432bd022dc.jpeg">

### □手順 5-4: ポリシードキュメントの ”Condition” 句を次のように修正して[ポリシーの適用]をクリックします

```json:修正前
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
```

```json:修正後
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                },
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx",
                        "yyy.yyy.yyy.yyy"
                    ]
                }
            }
```


### □手順 5-5: 「管理者」側手順はこれで終了です

# 6.  「AWS CLI」の実行(「ユーザー」側手順)

再び「AWS CLI」を使用する「ユーザー」側の手順です

### □手順 6-1: `--profile`オプションを付与して「AWS CLI」を実行し、問題なく実行できれば成功です

```bash:実行例
[user@workstaion ~]$ aws s3 ls --proflie "cli-user"

yyyy-MM-dd HH:mm:ss  xxxx
:
```

---

以上、特定の接続元IPアドレス以外から「AWSコマンドラインインターフェイス(AWS CLI v2)」を実行した際のみMFA(Multi-Factor Authentication)が必要になるようにIAMの設定をした際の手順でした。