[アップデード]IAM Access Analyzerのカスタムポリシーチェックでパブリックアクセスと重要リソースアクセスのチェックが追加されました! #AWSreInforce

IAM Access Analyzerの自動推論を利用したチェックが強化されました!単純なdiffが難しいポリシーの設定で間違いないかという担保を得るために活用しましょう!
2024.06.16

こんにちは、臼田です。

みなさん、アクセス権限のチェックしてますか?(挨拶

今回はAWS re:Infore 2024にてIAM Access Analyzerのカスタムポリシーチェックが拡張されたので解説します。

AWS IAM Access Analyzer now offers policy checks for public and critical resource access - AWS

IAM Access Analyzer Update: Extending custom policy checks & guided revocation | AWS News Blog

カスタムポリシーチェックとは?

IAM Access Analyzerのカスタムポリシーチェックは前回のre:Invent 2023にて発表された機能です。

AWSを利用している際にはアクセス制御のために様々なポリシーを記述して利用します。例えばIAM UserやRoleに対して権限をアタッチするアイデンティティベースポリシー(IAM Policyやインラインポリシーなどのアクセス許可ポリシー)や、S3やIAM Roleに誰が(何が)アクセスすることを許可するか設定するためのリソースベースポリシー(S3: バケットポリシー、IAM Role: 信頼ポリシー)があります。

通常、新たに権限を追加したり、それを変更する作業は特権に近い作業ですから、誰でも簡単にできると困りますよね。然るべき人のチェックを受けながら、新しいユーザーのために権限を払い出したり、サービスにアタッチされた権限を慎重に変更していきます。

ではそのレビューは本当に正しく実行できるでしょうか?

ポリシードキュメントはjsonで記述されていて複雑です。人の目でチェックしていると、ヌケモレがあるかもしれません。行数が減っている代わりに新しい権限が追加されていることが見逃されているかもしれません。diffを取ったらどうでしょうか?表記ブレや順番の変更など単純なdiffだと見なくていい場所が増えたりして、だんだんレビューが煩雑になるかもしれません。Allowだけ記述されていたポリシーから、AllowとDenyを組み合わせた別の表現方法にしたとき、それは本当に以前より権限が絞られているでしょうか?

そんなときに利用できる機能がカスタムポリシーチェックです!

最初のリリースの際には下記2つのチェックが可能でした。

  • CheckNoNewAccess
    • 新旧のポリシーを比較して、新しいアクセスが許可されているか確認する
    • 権限を変更する際に権限が増えていないかという観点のチェックができる
    • 権限が増えていないことを担保したり、増えていた場合にレビューのレベルを上げるなどの運用に利用できる
    • アイデンティティベースポリシーとリソースベースポリシー両方に適用可能
  • CheckAccessNotGranted
    • 単体のポリシーに対して指定したアクション(権限)が含まれているか確認する
    • 与えたくない権限を与えていないか確認できる
    • 特に変更系や削除系の権限などが含まれていないことを担保できる
    • アイデンティティベースポリシーとリソースベースポリシー両方に適用可能

これらは自動推論を利用して、数学的に裏付けされたチェックがされるため、単純なdiffや目視での確認では担保しづらい確認を行えます。それぞれポリシーを実際のAWSリソースに展開する前に、ポリシードキュメントなどだけでチェックが可能なため、CI/CDのフローなどに組み込み、事前のチェックに利用可能です。

今回のアップデートでは、新しくCheckNoPublicAccessというチェックが加わり、CheckAccessNotGrantedに指定したリソースへのアクセスが含まれていないか確認できるようになりました。順番に見ていきましょう。

CheckNoPublicAccess

名前の通り、パブリックアクセスが含まれていないかチェックできます。

check-no-public-access — AWS CLI 2.16.9 Command Reference

ざっくり説明は以下の通りです。

  • 単体のポリシーに対してパブリックアクセスが許可されていないか確認する
  • 外部に公開すべきでないリソースが確実に公開されていないことを担保できる
  • リソースベースポリシーに適用可能
  • 現状対応しているリソースは以下の通り
    • AWS::DynamoDB::Table
    • AWS::DynamoDB::Stream
    • AWS::EFS::FileSystem
    • AWS::OpenSearchService::Domain
    • AWS::Kinesis::Stream
    • AWS::Kinesis::StreamConsumer
    • AWS::KMS::Key
    • AWS::Lambda::Function
    • AWS::S3::Bucket
    • AWS::S3::AccessPoint
    • AWS::S3Express::DirectoryBucket
    • AWS::S3::Glacier
    • AWS::S3Outposts::Bucket
    • AWS::S3Outposts::AccessPoint
    • AWS::SecretsManager::Secret
    • AWS::SNS::Topic
    • AWS::SQS::Queue
    • AWS::IAM::AssumeRolePolicyDocument

かなり幅広く対応していますね。おそらくリソースベースポリシーに対応していて外部公開が可能なリソースはすべて含んでいるのではないでしょうか?(たぶん、直感的にそんな気がします、調べてないけど)

利用していきたい主要なリソースはS3バケット、Lambdaあたりでしょうか。知らない間に公開されていると非常に困りますから、これを自動推論でチェックして担保できるのは非常に心強いです。

実際に使ってみましょう。

今回はS3バケットが公開されていないか確認してみましょう。 その前に、CloudShellを立ち上げてAWSCLIのバージョンをチェックします。

[cloudshell-user@ip-10-134-4-171 ~]$ aws --version
aws-cli/2.16.5 Python/3.11.8 Linux/6.1.92-99.174.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023

バージョンが古いですね。どうやらAWSCLIは2.16.6から本機能に対応していそうなので、バージョンを上げておきます。

AWS CLIの最新バージョンのインストールまたは更新 - AWS Command Line Interface

上げたあとのバージョンはこちら。

[cloudshell-user@ip-10-134-4-171 ~]$ aws --version
aws-cli/2.16.8 Python/3.11.8 Linux/6.1.92-99.174.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023

今回利用するポリシーはこのユーザーガイドに掲載されている公開用のポリシーです。現在では古の技術ですから、絶対に使わないでくださいね。(通常外部公開は直接行わず、CloudFront経由で公開しOACを利用してS3へアクセスさせる)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::Bucket-Name/*"
            ]
        }
    ]
}

それでは実行してみましょう。

[cloudshell-user@ip-10-134-4-171 ~]$ aws accessanalyzer check-no-public-access --policy-document file://s3-pub-policy.json --resource-type AWS::S3::Bucket
{
    "result": "FAIL",
    "message": "The resource policy grants public access for the given resource type.",
    "reasons": [
        {
            "description": "Public access granted in the following statement with sid: PublicReadGetObject.",
            "statementIndex": 0,
            "statementId": "PublicReadGetObject"
        }
    ]
}

無事パブリックアクセスが検出され、resultFAILになりました。

PublicReadGetObjectというステートメントに権限が含まれているとメッセージがでますね。今回のようにステートメントが1つで単純であれば見つけるのは簡単ですが、複雑なポリシーでも見つけやすいですね。

では次は権限を絞ってみましょう。今回のポリシーからあまり変えず、Principalのみ特定ユーザーに絞ってみます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": {"AWS":"arn:aws:iam::111122223333:role/JohnDoe"},
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::Bucket-Name/*"
            ]
        }
    ]
}

実行すると以下のようになります。

[cloudshell-user@ip-10-134-4-171 ~]$ aws accessanalyzer check-no-public-access --policy-document file://s3-user-policy.json --resource-type AWS::S3::Bucket
{
    "result": "PASS",
    "message": "The resource policy does not grant public access for the given resource type."
}

今度はPASSしました。絶対に外部公開していないよね?という担保に活用していけますね!

CheckAccessNotGrantedのリソースアクセスの確認

今までCheckAccessNotGrantedは指定のアクションが含まれているかだけ確認できました。例えば「このユーザーにS3削除の権限がついていないよね?」とかです。

今回のアップデートで「このユーザーはこのS3にアクセスできないよね?」という特定リソースに対するアクセス権限の確認ができるようになりました。

check-access-not-granted — AWS CLI 2.16.9 Command Reference

例えばそのAWS環境上に存在するログなどのS3バケットにはアクセスを許可するけど、重要な顧客データや機密情報などにはアクセスさせたくないときに、適切に権限を絞り込めているか確認できます。

今回はS3バケットへの権限で試してみます。

まず関係のない別バケットへのアクセスが許可されていないか確認してみます。

[cloudshell-user@ip-10-134-4-171 ~]$ cat iam-s3-policy.json 
{
        "Version": "2012-10-17",
        "Statement": [
                {
                        "Sid": "VisualEditor0",
                        "Effect": "Allow",
                        "Action": "s3:GetObject",
                        "Resource": "arn:aws:s3:::Bucket-Name/*"
                }
        ]
}
[cloudshell-user@ip-10-134-4-171 ~]$ aws accessanalyzer check-access-not-granted --policy-document file://iam-s3-policy.json --policy-type IDENTITY_POLICY --access resources="arn:aws:s3:::DOC-EXAMPLE-BUCKET"
{
    "result": "PASS",
    "message": "The policy document does not grant access to perform the listed actions or resources."
}

カスタムポリシーチェックで指定されたバケットと、ポリシーで許可しているバケットが別のため問題なくPASSしました。

それでは今度は、バケットの中の重要なデータにアクセスが許可されている場合を確認してみます。

[cloudshell-user@ip-10-134-4-171 ~]$ cat iam-s3-policy.json 
{
        "Version": "2012-10-17",
        "Statement": [
                {
                        "Sid": "VisualEditor0",
                        "Effect": "Allow",
                        "Action": "s3:GetObject",
                        "Resource": "arn:aws:s3:::Bucket-Name/*"
                }
        ]
}
[cloudshell-user@ip-10-134-4-171 ~]$ aws accessanalyzer check-access-not-granted --policy-document file://iam-s3-policy.json --policy-type IDENTITY_POLICY --access resources="arn:aws:s3:::Bucket-Name/private"
{
    "result": "FAIL",
    "message": "The policy document grants access to perform one or more of the listed actions or resources.",
    "reasons": [
        {
            "description": "One or more of the listed actions or resources in the statement with sid: VisualEditor0.",
            "statementIndex": 0,
            "statementId": "VisualEditor0"
        }
    ]
}

許可ポリシーの指定としてはバケット全体に対する許可ですが、バケット内の特定のprefix(今回は/private)へのアクセスについても確認できました。細かいところまで確認できて嬉しいですね。

まとめ

今回はIAM Access Analyzerのカスタムポリシーチェックの2つのアップデートを確認しました。

システム開発のプロセスにおいて、権限周りの確認は煩雑になりやすいですから、開発のポリシーとして、あるいは組織全体のガバナンスとして、環境構築前や変更前のチェックに組み込んでいきたいですね!