🐷

IAMポリシー作成ssm:StartSessionのポートフォワーディングでポリシー制限を厳しく設定

2024/09/30に公開

困ったこと

AWS Systems Manager Session Managerを利用して、ローカルマシン(ユーザーの端末)-->>EC2インスタンス(bastionサーバー)に接続し、さらにそのサーバー経由でリモートデータベース(プライベートネットワーク内のホスト)にポートフォワーディングを行いたいと考えました。
ただし、ポリシーの制限を強くするためにSSMを許容するEC2インスタンスを特定のインスタンスのみに絞りたいです。

ポートフォワーディングセッションが失敗し、どのIAMポリシーに問題があるのかがわかりませんでした...

IAMポリシーの評価論理

AWS IAMポリシーの評価は次のようなルールに基づいています:

  • 暗黙的な拒否:IAMでは、デフォルトですべてのアクションが拒否されます。つまり、許可が明示されない限り、全てのリソースやアクションにアクセスすることはできません。
  • 明示的な許可: 明示的に許可されたアクションやリソースのみが実行可能です。ポリシーに「Effect: Allow」と記載されたものがこれに該当します。
  • 明示的な拒否: ポリシー内に「Effect: Deny」と明示されたアクションは、そのアクションに対して許可が存在していたとしても、強制的に拒否されます。

公式ドキュメント:
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html

StartPortForwardingSessionToRemoteHostとは

AWS-StartPortForwardingSessionToRemoteHost は、AWS Systems ManagerのSession Managerが提供するドキュメントの一つで、特定のEC2インスタンス経由でリモートホスト(DBやアプリケーションサーバー)へのポートフォワーディングを行うために使用されます。このドキュメントを利用することで、SSHやバスチョンホストを使わずに、セキュアなトンネルを介してリモートホストにアクセスすることが可能です

SSMコマンド例

下記のコマンドで、ローカルマシン(ユーザーの端末)-->>EC2インスタンス(bastionサーバー)-->>リモートデータベース(プライベートネットワーク内のホスト)にポートフォワーディングを行いたいと考えました。

aws ssm start-session \
    --target <instance-id> \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"host":[<dbのhost-cluster-url>],"portNumber":["3306"], "localPortNumber":["13306"]}' \
    --profile stg \
    --region ap-northeast-1

設定したポリシー

修正前

当初は、全てのリソースに対してssm:StartSessionを許可していました。
勿論、SSMは実行可能ですが、権限を最小限にしたいです。
特定のEC2インスタンス(bastionサーバー)からのみSSMを許可しようと思います。

    {
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession"
      ],
      "Resource": "*"
    },

失敗例

EC2インスタンスのタグに「Name: bastion-db-maintenance」が付いているEC2インスタンスのみに制限しようとしました。 しかし、SSMを実行すると権限が無いというエラーになるのです。
Conditionの使い方は問題無さそうでした...

    {
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ssm:ResourceTag/Name": [
            "bastion-db-maintenance"
          ]
        }
      }
    },

勘違いしていたこと

今回の接続では、下記の2つA・Bが存在しています。
この両方がssm:StartSession actionの権限が必要だったようです。
A: ローカルマシン(ユーザーの端末)-->>EC2インスタンス(bastionサーバー)
B: EC2インスタンス(bastionサーバー)-->>リモートデータベース(プライベートネットワーク内のホスト)

つまり、失敗例では
A: は許可できるが、B: の権限が無いということです。

修正後

こちらに修正することによって、

A: ローカルマシン(ユーザーの端末)-->>EC2インスタンス(bastionサーバー)

-->> bastion-db-maintenance のみに制限する

B: EC2インスタンス(bastionサーバー)-->>リモートデータベース(プライベートネットワーク内のホスト)

-->> AWS-StartPortForwardingSessionToRemoteHost を許可する

    {
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ssm:ResourceTag/Name": [
            "bastion-db-maintenance"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession"
      ],
      "Resource": "arn:aws:ssm:*:*:document/AWS-StartPortForwardingSessionToRemoteHost"
    },

参考

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html

https://dev.classmethod.jp/articles/devio-2021-iam-evaluation-logic/

Discussion