AWS IAM Access Analyzerによる継続的ポリシーチェックを自動化する方法

# はじめに
AWSのIAMロールに付与するIAMポリシーの権限は、セキュリティリスクを考えれば、必要最小限にとどめる必要があります。
AWSの任意のインスタンスに関連付けられたIAMロールでは、設定時は最小権限になっていても、その後の運用で過剰な権限が付与される可能性があります。
また、開発者や運用者が作るIAMポリシーの権限も、必ずセキュリティベストプラクティスに沿っているかどうかは分かりません。
このIAMポリシーの検査を継続的に実施可能な環境を作るために、自動化されたメカニズムの導入を検討します。

# アプローチ
**AWS IAM Access Analyzer**には、IAMポリシーのチェック機能があり、この機能を利用してポリシーを検証できます。
マネジメントコンソール上だけではなく、AWS CLIやSDKでも実行可能であり、自動化できます。

IAM Access Analyzerのポリシーチェック機能では、IAM ポリシーの文法、ベストプラクティスに対する違反を検査します。
検査でセキュリティ警告、エラー、一般的な警告、ポリシーの提案があれば、結果が表示されます。

# 実装
EventBridgeで、ポリシーチェックするLambda関数を毎日実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/3713fcde-c172-9f39-7a0b-76cf23beaa40.png)
以下のLambda関数のコードは、最低限の動作を確認したものです。

```python:lambda_handler
def lambda_handler(event, context):
    # TODO implement
    iam_client = boto3.client('iam')
    analyzer_client = boto3.client('accessanalyzer')
    
    iam_policy_list_istruncated = True
    iam_policy_list_marker = None
    iam_policy_arn_list = []
    iam_policy_document_list = {}
    iam_policy_document_list['Documents'] = []
    
    # To get all iam policy ARNs
    while iam_policy_list_istruncated == True:
        if iam_policy_list_marker:
            iam_policy_list = iam_client.list_policies(
                Scope = 'Local',
                OnlyAttached = False,
                Marker = iam_policy_list_marker
            )
        else: 
            iam_policy_list = iam_client.list_policies(
                Scope = 'Local',
                OnlyAttached = False,
            )
            
        for iam_policy in iam_policy_list['Policies']:
            iam_policy_arn_list.append(iam_policy['Arn'])

        iam_policy_list_istruncated =  iam_policy_list['IsTruncated']
        if 'Marker' in iam_policy_list:
            iam_policy_list_marker = iam_policy_list['Marker']
    
    # To get all iam policy documents
    for arn in iam_policy_arn_list:
        iam_policy = iam_client.get_policy(
            PolicyArn = arn
        )

        iam_policy_version = iam_client.get_policy_version(
            PolicyArn = arn,
            VersionId = iam_policy['Policy']['DefaultVersionId']
        )
        
        iam_policy_document_list['Documents'].append(
            {
                'PolicyArn' :  arn,
                'PolicyVersionId' : iam_policy['Policy']['DefaultVersionId'],
                'PolicyDocument' : iam_policy_version['PolicyVersion']['Document']
            }
        )
    
    # To check all policies
    iam_policy_check_results = {}
    iam_policy_check_results['Results'] = []
    
    for document in iam_policy_document_list['Documents']:
        iam_policy_check = analyzer_client.validate_policy(
            locale = 'JA',
            policyDocument = json.dumps(document['PolicyDocument']),
            policyType='IDENTITY_POLICY'
        )
        iam_policy_check_results['Results'].append(
            {**document, **iam_policy_check}
        )
    return iam_policy_check_results
```

実装上のポイントは以下となります。
* IAMとIAM Access AnalyzerはAWS内では別サービスの扱いなので、clientを2つ作る必要があります。
* IAM Access Analyzerのvalidate_policyの引数には「policyDocument」、つまりIAMポリシーのJSONを指定する必要があります。
    * このJSONを取得するまでには、IAMのlist_policiesで各ポリシーのARNを取得し、get_policyでボリシーのバージョン文字列を取得した後、この両方を引数にしてget_policy_versionを実行しなければなりません。

# 結果
テストのために、わざとセキュリティ違反が起きるようなポリシーを作っておき、Lambda関数を実行します。

```json:結果
{
      "PolicyArn": "arn:aws:iam::xxxxxxxxxxxx:policy/Testpolicy-201502161625",
      "PolicyVersionId": "v2",
      "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": "iam:*",
            "Resource": "*"
          }
        ]
      },
      (中略)
      "findings": [
        {
          "findingDetails": "ワイルドカード (*) をアクションおよびリソースで使用すると、すべてのリソースで iam:CreateServiceLinkedRole アクセスが許可されるので、意図しないサービスにリンクされたロールが作成されることがあります。これを避けるために、代わりにリソース ARN を指定することをお勧めします。",
          "findingType": "WARNING",
          "issueCode": "CREATE_SLR_WITH_STAR_IN_ACTION_AND_RESOURCE",
          "learnMoreLink": "https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access-analyzer-reference-policy-checks.html#access-analyzer-reference-policy-checks-general-warning-create-slr-with-star-in-action-and-resource",
          "locations": [
            {
              "path": [
                {
                  "value": "Statement"
                },
                {
                  "index": 0
                },
                {
                  "value": "Action"
                }
              ],
              "span": {
                "end": {
                  "column": 77,
                  "line": 1,
                  "offset": 77
                },
                "start": {
                  "column": 70,
                  "line": 1,
                  "offset": 70
                }
              }
            },
            {
              "path": [
                {
                  "value": "Statement"
                },
                {
                  "index": 0
                },
                {
                  "value": "Resource"
                }
              ],
              "span": {
                "end": {
                  "column": 94,
                  "line": 1,
                  "offset": 94
                },
                "start": {
                  "column": 91,
                  "line": 1,
                  "offset": 91
                }
              }
            }
          ]
        },
        (以下略)
```

以上のように、AWS IAM Access Analyzerによるポリシーチェックの結果を得ることができました。

# 応用
`validate_policy` の policyType='`IDENTITY_POLICY`' の部分ですが、この他に `RESOURCE_POLICY` と `SERVICE_CONTROL_POLICY` を指定できますので、このコードをベースに作り込めば、IAMポリシー以外の検査もできます。

## (詳細) AWSにおける「ポリシー」の種類とは
そもそもAWS全体でポリシー呼ばれる設定のタイプは3種類あり、それぞれ **(1)IDポリシー**、**(2)リソースポリシー**、**(3)サービスコントロールポリシー** と呼ばれています。

(1)IDポリシーは、**IAM**のプリンシパルにアクセス許可を付与するものです。
また、IAMの管理ポリシーやインラインポリシーは、**IDポリシー**の細かい分類です。

(2)リソースポリシーは、**AWSリソース**に対するアクセス許可を付与するものです。
IAMロールの信頼ポリシーとS3バケットのバケットポリシーは、リソースポリシーの細かい分類です。

(3)サービスコントロールポリシーは、AWS Organizations、組織単位 (OU)、アカウントにアタッチされる **SCP (Service Control Policy)** を指し、そのままの意味となります。

# IAM Access Analyzerがチェックする観点
記事執筆時点では、以下のリンク先に記載されている項目がチェックされます。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access-analyzer-reference-policy-checks.html

かなり多くの項目がチェックされますが、肝心のセキュリティの観点については、現時点では項目が少なめとなっています。

例えば、S3バケットポリシーでNotPrincipalでAllowしている場合に警告してくれるなど、クリティカルな穴を塞げる点では非常に助かります。
ただ、単独のAction (NotAction) の広さに対しての警告はまだ弱く、例えば `ec2:*` と書いた場合に、権限が広すぎるとは警告してくれません。
セキュリティに関しては、今後のアップデートに期待したいポイントと言えます。

# まとめ
IAMの最小権限の継続的検査を自動化することは、AWSがセキュリティの推奨事項として提唱しているものであり、実施すべきことです。

自動検査方法の一つとして、IAM Access Analyzerのvalidate_policyを定期的に自動で実行するアプローチを紹介しました。

しかし、上記で書いたように、IAM Access Analyzerに頼りきるだけでは不完全ですので、AWS Configを利用した差分確認などと組み合わせて運用することをお勧めします。

# 参考文献
* [IAM - Boto3 Docs 1.21.5 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html)
* [IAM Access Analyzer- Boto3 Docs 1.21.5 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/accessanalyzer.html)
* [Access Analyzer ポリシーチェックリファレンス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access-analyzer-reference-policy-checks.html)
* [Automate resolution for IAM Access Analyzer cross-account access findings on IAM roles](https://aws.amazon.com/jp/blogs/security/automate-resolution-for-iam-access-analyzer-cross-account-access-findings-on-iam-roles/)
* [Continuously monitor unused IAM roles with AWS Config](https://aws.amazon.com/jp/blogs/security/continuously-monitor-unused-iam-roles-aws-config/)