AWS CLIで多要素認証を楽にする仕組みを紹介する

この記事は検索エンジンプロダクトを一緒に開発してた同窓会 Advent Calendar 2023の14日目の記事です。

2段階認証（Multi-Factor Authentication）が必要な環境において、AWS clientを「[公式の手順](https://repost.aws/ja/knowledge-center/authenticate-mfa-cli)」よりも楽に使用する方法を紹介します。

# TL;DR
  - MFA認証/Assume Role必須の環境でAWS CLIを（割と簡単に）使用する方法を記載
  - 作業毎にMFA Codeを入力するのが面倒くさいと思う方向け
  - MFA Codeを最初の1回入力が必要であるが、一定期間入力が不要になり快適になる

# 前提条件
  - python, pipがインストールされている
  - 使用するIAMユーザのaccess keyが発行されている

# 参考資料
https://qiita.com/ogady/items/c17ffe8f7c8e15b15f77

# 設定手順
手順の概要は以下の通りです。
* pythonライブラリを使用する。これを使う
    * **[GitHub - broamski/aws-mfa](https://repost.aws/ja/knowledge-center/authenticate-mfa-cli)**
* 接続情報を **[Account Name]-long-term** の形式でcredentialファイルに記載
* Let's Try!!

## aws-mfaのセットアップ
* Install
```
$ pip install aws-mfa
```
* Install後確認
ヘルプが表示されれば成功
```
$ aws-mfa -h

usage: aws-mfa [-h] [--device arn:aws:iam::123456788990:mfa/dudeman] [--duration DURATION]
               [--profile PROFILE] [--long-term-suffix LONG_TERM_SUFFIX]
               [--short-term-suffix SHORT_TERM_SUFFIX]
               [--assume-role arn:aws:iam::123456788990:role/RoleName]
               [--role-session-name ROLE_SESSION_NAME] [--force]
               [--log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}] [--setup]

options:
  -h, --help            show this help message and exit
  --device arn:aws:iam::123456788990:mfa/dudeman
                        The MFA Device ARN. This value can also be provided via the environment variable  
                        'MFA_DEVICE' or the ~/.aws/credentials variable 'aws_mfa_device'.
  --duration DURATION   The duration, in seconds, that the temporary credentials should remain valid.     
                        Minimum value: 900 (15 minutes). Maximum: 129600 (36 hours). Defaults to 43200 (12
                        hours), or 3600 (one hour) when using '--assume-role'. This value can also be     
                        provided via the environment variable 'MFA_STS_DURATION'.
  --profile PROFILE     If using profiles, specify the name here. The default profile name is 'default'.  
                        The value can also be provided via the environment variable 'AWS_PROFILE'.        
  --long-term-suffix LONG_TERM_SUFFIX, --long-suffix LONG_TERM_SUFFIX
                        The suffix appended to the profile name toidentify the long term credential       
                        section
  --short-term-suffix SHORT_TERM_SUFFIX, --short-suffix SHORT_TERM_SUFFIX
                        The suffix appended to the profile name toidentify the short term credential      
                        section
  --assume-role arn:aws:iam::123456788990:role/RoleName, --assume arn:aws:iam::123456788990:role/RoleName 
                        The ARN of the AWS IAM Role you would like to assume, if specified. This value can
                        also be provided via the environment variable 'MFA_ASSUME_ROLE'
  --role-session-name ROLE_SESSION_NAME
                        Friendly session name required when using --assume-role
  --force               Refresh credentials even if currently valid.
  --log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}
                        Set log level
  --setup               Setup a new log term credentials section
```

## AWS credential に設定追加をする
### 情報が追加されるファイル
- ~/.aws/credential

### アカウント接続情報の設定
[Account Name]-long-term で作成することが重要。aws-mfaコマンド実行後に、[Account Name]のプロファイルが自動で作成される寸法。
```
$ aws configure --profile [Account Name]-long-term

AWS Access Key ID [None]: XXXXXX   <= AccessKeyを入力
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXX <= SecretAccessKeyを入力
Default region name [None]: ap-northeast-1
Default output format [None]: json (お好みで）
```

###  (初回のみ)MFA device、Assume Roleの追加
* 設定しない場合は、aws-mfaコマンド実行時に以下の引数が必要になる（少し上にhelpが貼ってあるので、詳細はそちらを見てください）
  * MFA Device：--device引数
  * Assume Role：--assume-role引数

* 設定方法
```
$ aws configure set aws_mfa_device "[mfa device ARN]" --profile [AWSアカウント名]-long-term

$ aws configure set assume_role "[assume role ARN]" --profile [AWSアカウント名]-long-term
```

* MFA DeviceのArn取得方法
```
$ aws iam list-mfa-devices --user-name [IAMユーz名] --profile [AWSアカウント名]-long-term

{
    "MFADevices": [
        {
            "UserName": "XXXXXXXXXXXXXXXX",
            "SerialNumber": "arn:aws:iam::123456789012:mfa/device_name",  <= この値がMFA DeviceのARNです
            "EnableDate": "2023-12-31T23:59:55+00:00"
        }
    ]
}
```

* 使用するAssume Role名の取得方法
```
マネージドコンソールで確認（取得する方法を知っていたら教えてください）
フォーマット arn:aws:iam::[AccountID]:role/[Assume Role名]
```

# 利用方法
設定完了後に、日々使用する手順です
## MFA認証の実施
* duration（保持期間）の間、再認証が不要に。利便性とセキュリティのバランスをもって数字を決めてください
* --profileにはlong-termをつけない（設定とは違う）

```
$ aws-mfa --profile [AWSアカウント名] --duration 129600

INFO - Validating credentials for profile: xxxxxxxxx
INFO - Short term credentials section xxxxxxxxx is missing, obtaining new credentials.
Enter AWS MFA code for device [arn:aws:iam::123456789012:mfa/device_name] (renewing for 129600 seconds): 
<= [MFA Code（6桁のアレ）を入力]
INFO - Fetching Credentials - Profile: xxxxxxxxx, Duration: 129600
INFO - Success! Your credentials will expire in 129600 seconds at: 2024-01-01 13:01:00+00:00
```

結果、AWS session tokenが作成されて、credentialファイルにshort termの接続profileが生成されている

```
$ cat ~/.aws/credential

[test-account-long-term]
aws_access_key_id = [key]
aws_secret_access_key = [key]

[test-account]
assumed_role = False
aws_access_key_id = [key]
aws_secret_access_key = [key]
aws_session_token = [token]
aws_security_token = [token]
expiration = 2023-12-31 23:29:55
```

## AWSコマンドの実行
以降、long termの接続profile（上記の例ではtest-account）を使用すると、AWSコマンドは自由に実行できる。

* 例：ECSクラスタの一覧を取得
```
$ aws ecs list-clusters --profile [AWSアカウント名]

(結果)
{
    "clusterArns": [
        "arn:aws:ecs:ap-northeast-1:123456789012:cluster/***************",
        "arn:aws:ecs:ap-northeast-1:123456789012:cluster/***************"
    ]
}
```

# その他
* MFA Code（6桁のアレ）を取得するライブラリ（otp）があり、これを使うとMFA認証の取り扱いをコードで完結することは可能（別途記載をする）