特定のインスタンスの操作のみを出来るように最小権限で開発者ユーザー用IAMポリシーを作成してみた

特定のインスタンスの操作のみを出来るように最小権限で開発者ユーザー用IAMポリシーを作成してみた

本ブログでは、特定のEC2インスタンスへのSSMセッションマネージャー、起動・停止操作、S3バケットアクセスを、最小権限ポリシーで安全に制御する方法をご紹介しています。開発者用ユーザーの権限管理に悩んでいる方におすすめの記事です。
Clock Icon2025.02.26

どうもさいちゃんです。皆さんは開発担当者には誤操作防止のために特定のインスタンスの必要な操作権限のみを与えたユーザーで操作を行って欲しいと考えたことはありませんか?

  • インフラ担当者とアプリケーション担当者が分かれており、アプリケーションの開発は別のベンダーに任せている場合
  • 複数のワークロードを AWS 上で実行していて開発者には特定のインスタンスに関わる操作のみを許可したい場合

など、上記のように様々なシーンで開発者用に IAM ユーザーの権限を絞りたいといった要件は出てくるかと思います。

今回は開発者用に権限を絞った IAM ユーザーの作成を行ったので紹介します。

やりたかったこと

今回作成した IAM ユーザーの要件は以下の通りです。

  • 特定の EC2 インスタンスへ SSM セッションマネージャーのポートフォワーディングでのアクセスを許可。
  • 上記と同様のインスタンスに対して、起動、停止、再起動の操作ができるようにしたい。
  • 特定の S3 バケットの読み取り権限を許可する。

この時点で IAM ユーザーにコンソールへのアクセスを許可するのか、CLI での操作を前提とするのかについては決めていませんでした。
後述しますが、上記の要件を最小権限で実現しようとするとコンソールからの操作は難しい部分があったので今回該当ユーザーは CLI から操作を行ってもらう事にしました。

IAM ポリシーの作成

今回作成した IAM ポリシーはこんな感じです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:RebootInstances"
      ],
      "Resource": "<アクセスを許可するEC2のARN>"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "<アクセスを許可するバケットのARN>",
        "<アクセスを許可するバケットのARN>/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession",
        "ssm:TerminateSession",
        "ssm:ResumeSession"
      ],
      "Resource": "<アクセスを許可するEC2のARN>"
    },
    {
      "Effect": "Allow",
      "Action": ["ssm:DescribeSessions", "ssm:GetConnectionStatus"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["ssm:StartSession"],
      "Resource": ["arn:aws:ssm:*:*:document/AWS-StartPortForwardingSession"]
    }
  ]
}

※EC2 の ARN は arn:aws:ec2:<リージョン>:<アカウント ID>:instance/<インスタンス ID>の形式です。

今回はこのポリシーを「my-poc-dev-policy」と名付け、アタッチしたグループを作成し、新規作成したユーザーをグループに所属させました。
グループにポリシーをアタッチすることで今後開発者用ユーザーが増えても対応できるようにしておきます。

今回作成したユーザーはコンソールアクセスをする必要がないので、コンソールアクセス用のパスワード等は発行しなくてももんだありません。

ではさっそく動きを確認していきましょう。

権限の確認

はじめはコンソールから停止や開始、再起動が出来るかもと思い試してみました。
ちなみに今回開発者用ユーザーはec2:DescribeInstancesを持っていないので EC2 の一覧が表示できません。

policy-1.png

ec2:DescribeInstancesでは一部の EC2 を表示する(もしくはしない)という設定が出来ないためすべてのインスタンス情報の表示が出来なくなります。
つまり、コンソールから個別にインスタンスの状態を操作することが出来ないという事です。

でも、EC2 の詳細画面用にインスタンスごとに用意されている URL を直接見に行けば操作できるんじゃない?と、淡い期待を抱き試してみることにしました。

policy-2.png

作成したユーザーでアカウントにログインした状態でインスタンスごとの詳細画面 URL へアクセスすると、一覧表示画面へとリダイレクトされてしまいました。

policy-3.png

そしてこんなエラーが出ています。
「Failed to fetch instance: You are not authorized to perform this operation. User: arn:aws:iam::アカウント ID:user/test-dev-user is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action」

ec2:DescribeInstancesがないからこの操作は行えないよと怒られてます。

ということで、コンソールからはインスタンスの状態を操作することが出来ないという事実が判明したので、ここからは CLI で操作を行います。
(この時点でこのユーザーはコンソールへのアクセスをさせる必要がなくなったので、先述の通りユーザーの作成時にコンソールへのアクセスの項目にチェックをする必要はありません。)

とにかく一覧表示はできないことは確認できたので CLI 経由で停止、起動、再起動をしてみます。

停止

$ aws ec2 stop-instances --instance-ids i-XXXXXXXXXXXX

#実行結果
{
    "StoppingInstances": [
        {
            "InstanceId": "i-XXXXXXXXXXXX",
            "CurrentState": {
                "Code": 64,
                "Name": "stopping"
            },
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}

起動

$ aws ec2 start-instances --instance-ids i-XXXXXXXXXXXX

#実行結果
{
    "StartingInstances": [
        {
            "InstanceId": "i-XXXXXXXXXXXX",
            "CurrentState": {
                "Code": 0,
                "Name": "pending"
            },
            "PreviousState": {
                "Code": 80,
                "Name": "stopped"
            }
        }
    ]
}

再起動

$ aws ec2 reboot-instances --instance-ids i-XXXXXXXXXXXX

再起動に関しては CLI から再起動したことの確認はできませんが、CloudTrail のログに記録がされています。

私は再起動を検知して自分のメールアドレスへとメールを送信してくれるシステムをたまたま構築していたので、メールが飛んできて再起動がされたことが分かりました。

ちなみに上記を別のインスタンスの ID を指定して実行してみるとどうなるでしょうか?


$ aws ec2 reboot-instances --instance-ids i-▼▼▼▼▼▼▼▼▼▼▼▼

#実行結果
An error occurred (UnauthorizedOperation) when calling the RebootInstances operation: You are not authorized to perform this operation. User: arn:aws:iam::アカウントID:user/test-dev-user is not authorized to perform: ec2:RebootInstances on resource: arn:aws:ec2:ap-northeast-1:アカウントID:instance/i-▼▼▼▼▼▼▼▼▼▼▼▼ because no identity-based policy allows the ec2:RebootInstances action. Encoded authorization failure message: nTvli9MIGf9jJIUlYg-05oAf30KnU7OEo79ESSAZT3HWIMR1pCJ0G_TBVlNMBHCSGtBNWmKC0S8Kdjexo8E8HRPvgbRgN4mktL26W9sY2-cA8SfI8u0Xr9FoH5tZ23nJAWXdj6kQsdyxvs0BhG2D9xI3jcvy1utCi8lv3aftNLhbWY8_QBdIyqDmduxgTemH09EbIB2VPq3riuCPptZOGNNXNzkZzFygFZ_FS5gxc06-ZU8Ls9NDvmxacmhbwa6h2bfg-IJRRSBudVwMIc6TJNZ5N0aAaMdVu01BB6Odsc-oRWeltlvEt3ImTKHl8pQGEpaSWw7l64H48Sdipux-5qI5vfCoAISlxU0JnvY0JB_rHFoaQKL8tTMibCpYLTf4piYjxLJJyFsPwLJ3ja29J0lY_75dRBFmTb033GS88pQlsLnpTv0rDgr7-ewnVtbSfaAhzNix7n3OYdJUndDxTuX8Hkg7EQYMybYZPSlYUgI46Q978nwnfkHXqwEWwEQOENO5UtcAwmoR229P24ZRbjJiO3JAn749Tik2wWvbXP66jU_XowIjbKXbhK4cWpZG36jMIyL4ZJ-sXX4njBeqz1GzFwBAoCTVGqOM1L3RuhOxWXgEvOImKxsco-hg8gKZhGoKExLWkqRwWzzI_aGKXN7PZrZTu9EYXPjMemVmNPrY1uLLI3DKTO06KykdPynZSuBzMQOL4Au1muqGLGwPXHjdNOSWQyUiK0oHM7KHkfvAHrM2QAjkJ7QLROWDWQArYIoVa_80tChbaSk4gDJuSEqi2gy5pZfTmiGmcvQ42K39m3_TMqWWAtArYvgfDaYkLdSU_IOi4GM-Ya1Yj1JHr1PfHYeUy52Ll7BHEit3zFiJjuNa2-rSTCSEoTXb4n6No9NdIwxdT34EvA

こんな感じで権限不足だと怒られます。

S3 へのアクセス

まずはアカウント内の S3 バケット一覧の表示が出来るか試してみます。

$  aws s3 ls

#実行結果
An error occurred (AccessDenied) when calling the ListBuckets operation: User: arn:aws:iam::アカウントID:user/test-dev-user is not authorized to perform: s3:ListAllMyBuckets because no identity-based policy allows the s3:ListAllMyBuckets action

S3 バケット一覧にはアクセスできません。想定通りの動きをしてくれています。
では特定のバケット(今回は「permission-test-s3-bucket」とします。)にテスト用のファイルを置いて、確認出来るかどうか試します。

$ aws s3 ls s3://permission-test-s3-bucket

#実行結果
2025-02-13 20:05:24       6630 test.yaml

テスト用に置いた yaml ファイルを確認できました。

ポートフォワーディングを使用したインスタンスへのアクセス

では最後に特定のインスタンスにポートフォワーディングができるかどうか確認してみましょう。

$ aws ssm start-session --target i-XXXXXXXXXXXX --document-name AWS-StartPortForwardingSession --parameters '{"portNumber":["22"],"localPortNumber":["11022"]}'

#実行結果
Starting session with SessionId: ndenki-test-dev-user-lhxctfrnrntgj6cg2rtr5u8zde
Port 10022 opened for sessionId ndenki-test-dev-user-lhxctfrnrntgj6cg2rtr5u8zde.
Waiting for connections...

この時点でセッションは確立できているので IAM ポリシーは意図した挙動をしてくれていることが分かりますが、実際に接続してみましょう。

$ ssh ec2-user@localhost -p 11022 -i Test-key.pem

#実行結果
The authenticity of host '[localhost]:11022 ([127.0.0.1]:11022)' can't be established.
ED25519 key fingerprint is SHA256:4RTpilt7d85yryY7aOx3LWoQ/ml2xgu8KDeARGjlels.
This host key is known by the following other names/addresses:
    C:\Users\e.said/.ssh/known_hosts:three: [localhost]:10022
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:11022' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Thu Feb 13 07:39:48 2025 from 127.0.0.1

うまくいきましたね。
指定したインスタンス以外のインスタンスに対してポートフォワーディング使用とすると以下の様にエラーが出ます。


aws ssm start-session --target i-▼▼▼▼▼▼▼▼▼▼▼▼ --document-name AWS-StartPortForwardingSession --parameters '{"portNumber":["22"],"localPortNumber":["11022"]}'

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::アカウントID:user/test-dev-user is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:アカウントID:instance/i-▼▼▼▼▼▼▼▼▼▼▼▼ because no identity-based policy allows the ssm:StartSession action

最後に

AWS 環境において、開発者の権限を最小限に抑えつつ、必要な操作を可能にする IAM ポリシーの設計は、セキュリティとアクセス制御の観点から非常に重要です。今回の実装では、特定の EC2 インスタンスへの SSM セッションマネージャーによるポートフォワーディング、インスタンスの起動・停止・再起動、および特定 S3 バケットへのアクセスを、CLI ベースで安全に実現する為の IAM ポリシーを作成しました。
開発者用のセキュアな IAM ポリシーを作成したい方は上記ポリシーをベースに権限について考えてみるの良いかもしれません。本ブログが誰かのお役に立てれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.