Private SubnetのEC2にSSM Port ForwardingでRDPアクセスするための通信要件

記事タイトルとURLをコピーする

CS課佐竹です。

今回は「AWS Systems Manager のポートフォワーディング機能」の通信要件について実際に構築を行いながら整理をします。左記のブログとかなり重複するところはありますが、このブログの主題は「Windows端末にリモートデスクトップ接続を行う場合のSystems Manager Port Forwardingに関する最小限の通信要件を整理すること」になります。

前提

今回、以下の前提で実装を行います。

  1. EC2(Windows Server) はPrivate Subnetに配置されていること
  2. Security GroupsはInbound/Outbound ともに厳密に制限すること
  3. Port Forwarding では特定のEC2のみにアクセスできるようIAMで制限すること

では行ってみましょう。

構成図

以下に全体図を記します。また、まとめは最後に記載していますため、まとめだけ確認をされたい方は最下部まで移動ください。

EC2(Windows)をPrivate Subnetに構築/起動する

まずはEC2 Instanceを用意します。既にEC2 Instanceを構築済の方は、Start(起動)してください。
「SSMがつながらない」というトラブルで、稀に原因が「EC2がそもそも起動していなかった」ということもありますので、是非EC2が起動状態であるのかは確認をお願いします。

今回は、 i-0c678d2c038786416 (SSM Port Forwarding Windows 2019)というEC2 InstanceをAmazon出荷のAMIより新規に構築しましたので、これを例にとって進めていきます。

EC2にIAM Roleを付与する

構成図の左上にあるEC2 Instanceの構築は完了しましたので、次にIAM Roleを付与します。
IAM Roleは、これまでは「AmazonEC2RoleforSSM」を利用することが推奨されておりましたが、現在は非推奨状態です。理由としては、S3バケット全てにアクセスが可能となっているなど、許可範囲が広すぎるためと思われます。

ということで、今回は新規にIAM Roleを作成したいと思います。新規に作成するRole名は「AmazonEC2RoleforSSMPF」としました。IAMの画面から、Roleを作成します。ポリシー名「AmazonSSMManagedInstanceCore」だけをまずは選択して作成します。

作成が完了したら、このRoleをEC2 Instanceにアタッチします。

無事にアタッチができました。
次はEC2 InstanceにインストールされているSSM AgentのVersionの確認になるのですが、これはSSMの機能で対応するため次に行きます。

SSMへのVPC EndpointとSecurity Groupを作成する

以下の公式ドキュメントを参考に、VPC Endpointを3つ作成します。

Systems Manager を使用してインターネットアクセスなしでプライベート EC2 インスタンスを管理できるように、VPC エンドポイントを作成するにはどうすればよいですか?
  1. com.amazonaws.region.ssm
  2. com.amazonaws.region.ec2messages
  3. com.amazonaws.region.ssmmessages

の3つを作成してください。なおVPC EndpointにはSecurity Groupが必要になります。今回はEC2からのアクセスのみに絞るため、「ssmvpcendpoint」というSecurity Groupを新設し、これら3つのEndpointに付与しました。

具体的なInboundの設定は以下の通りです。ドキュメントにも以下の通り記載がありますが、Outboundのルールは不要なため、記載しません。

セキュリティグループはポート 443 でインスタンスからのインバウンドトラフィックを許可する必要があります。

今回、EC2 Instanceは 192.168.64.0/24 の範囲にのみ存在するため、Inbound はその範囲に絞ったCIDRとしました。

補足1:S3のVPC Endpointも合わせて作成する

SSMの実行においてログ出力などでも利用するため、もしS3のVPC Endpointがない場合は合わせて作成ください。なお、S3のVPC Endpointはゲートウェイ側のEndpointとなっており、Endpointに付与するSecurity Groupでは制御せず(Sgは付与できません)、Route tableに記載します。なお、東京リージョンのS3 VPC Endpointは「pl-61a54008」がPrefixListIdとして付与されており、これをEC2側のSecurity Groupに記載することが可能です。具体的には以下のように記載します。

EC2にVPC Endpoint向けのSecurity Groupを付与する

通常、EC2 InstanceにアタッチするSecurity GroupのOutboundを制御することはあまり無いとは思います。しかし、厳格な制御を好まれるお客様もいらっしゃいますため、今回はOutboundも制限します。
作成した、3つの各VPC EndpointはENIを保持するため、それぞれのENIのLocal IPを指定したSecurity Groupを作成するのが厳格とは考えられますが、IPアドレスが変更されてしまう可能性を加味し、今回はVPC Endpointに付与している「ssmvpcendpoint」というSecurity Groupに対しての許可とします。
実際の設定の前に、まずは「ssmvpcendpointforEC2」という名前の空のSecurity Groupを1つ作成します。そして先ほど記載した通り、Outboundの443/TCPを「ssmvpcendpoint」にだけ許可する設定を施します。

最後にこの「ssmvpcendpointforEC2」をEC2 Instanceにアタッチし、作業は完了です。

SSMのコンソールよりSSM Agentのバージョンを確認する

ここまで作業しますと、上図の状態になります。ここまでで、既に以下の作業が完了しています。

  1. EC2 Instanceの構築と起動(SSM AgentはAmazon出荷のAMIには既に含まれている)
  2. IAM Roleの新規作成とEC2 Instanceへの付与
  3. VPC Endpoint 3つの作成とEC2 InstanceからVPC Endpointへのアクセスを許可するSecurity Groupの付与(Inbound)
  4. EC2 InstanceからVPC Endpointへのアクセスを許可するSecurity Groupの付与(Outbound)

これまでの作業で、Systems Managerのコンソールから、Managed Instancesとして確認が可能となります。実際に確認をしてみます。

上図の通り、対象のInstanceが表示されたでしょうか?もしこの一覧に表示されない場合は、EC2 Instanceの再起動なども行ってみてください。この画面から、Agentのバージョンが確認可能です。「2.3.634.0」となっていました。SSMのPort Forwardingには「SSM Agent (2.3.701.0)」以上が必須とのことですので、バージョンアップを行います。

SSMのコンソールよりSSM Agentのバージョンアップを行う

Run Command を使用して SSM エージェント を更新する https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/rc-console.html#rc-console-agentexample

上記ドキュメントに沿って、SSM Agentのバージョンアップ(AWS-UpdateSSMAgent のRunCommandでの実行)を行います。以下少し注意点を記載します。

ターゲットとなるEC2 Instanceには、今回バージョンアップがしたいEC2を1台のみ選択します。

「Output options」で、SSMの実行結果をS3バケットに出力するよう設定が可能ですが、現在EC2に付与されているIAM Roleにある「AmazonSSMManagedInstanceCore」にはS3バケットに出力する権限を保持していないためこれが失敗します。よって、一旦このOptionは今回利用しないこととします。

RunCommand を実行すると、上図のように「In Progress」のステータスになります。

Successとなれば完了です。バージョンを確認します。「2.3.722.0」にアップデートされました。

補足2:S3 BucketにSSMのログを出力する場合の権限設定

現在EC2 Instanceに付与されているIAM Roleは「AmazonEC2RoleforSSMPF」ですが、これにインラインポリシー等で以下のIAM Policyを追記することでS3 BucketにRunCommandの実行結果を出力することが可能です。※SSMのログはEC2のIAM Roleの権限でS3にPutされます

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::ssm-result-bucket-999999999999/*"
            ]
        }
    ]
}

[ssm-result-bucket-999999999999] の箇所は、結果をputされたいS3 bucket名に変更ください。

ここまでのおさらい

ここまでで上記の構成図まで進みました。EC2とSSMは既に通信が確立しており、またSSMからのRun Commandも実行が可能な状態となっています。

ローカルクライアントにCLIとPluginをインストールする

SSM Port Forwardingを実行するためには、ローカルクライアントの設定も必要になります。具体的には、以下の2つが満たされている必要があります(特に Windows Clientの場合)。

  1. AWS-CLI (1.16.234 以上)
  2. session-manager-plugin (1.1.31.0 以上)

CLIとPluginは以下のドキュメントを参考にインストールください。

AWS CLI のインストール https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-install.html
(オプション) AWS CLI 用の Session Manager Plugin をインストールする https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

どちらも最新バージョンをインストールして頂ければ問題ありません。バージョンが古い方はバージョンアップをお願いします。なお、それぞれのバージョン確認コマンドは以下の通りです。

aws --version
session-manager-plugin --version

 

IAM User に Policyを付与し、Credentialを払い出す

現時点で、ここまで設定が完了しました。この時点で既にアクセスできそうな気配ですが、まだPort Forwardingはできません。それはCLIを実行するIAM Userの作成と権限設定が不足しているためです。今からこれを設定します。

まず今回、仮のIAM Userとして「ssmpf」を作成しました。CLIでアクセスするため、Credentialを作成してCSVをダウンロードします。また、「ssmpf」へ付与するPolicyですが、今回最低限のIAM Policyを記載するためインラインポリシーに記載することにしました。以下がポートフォワードに必要な最低限のIAMポリシーとなります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ssm:ap-northeast-1::document/AWS-StartPortForwardingSession",
                "arn:aws:ec2:ap-northeast-1:999999999999:instance/i-0c678d2c038786416"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}

上記の通り、StartSessionには対象のドキュメントだけではなく、今回RDPアクセス対象のEC2 Instanceも記載します。このInstance ID部分でターゲットを絞ることが可能です。本ポリシーは「追加の Session Manager 用のサンプル IAM ポリシー」を参考に作成しました。

動作確認

以下のようにコマンドを実行してください。Instance idはご自身が接続されたいEC2のidにご変更ください。localPortNumberは使い慣れている33890としましたが、8389でも13389でも構いません。コマンド内の --profile ã«ã¤ã„てご存じない方は「名前付きプロファイル」をご参考ください。

aws ssm start-session --target i-0c678d2c038786416 --document-name AWS-StartPortForwardingSession --parameters portNumber=3389,localPortNumber=33890 --profile ssmpf

このコマンドが正常に実行されると、コマンドプロンプト上に以下のような表記が出ます。

この状態で、リモートデスクトップ接続のアプリケーションを実行します。ここで以下の通り「localhost:33890」とPort指定でローカルホストにアクセスします。

あとは通常通り、ユーザ&パスワードを入れると、

RDPで接続されます。お疲れ様でした。
なお、コマンドプロンプト上で「Ctrl+C」を実行することで、ポートフォワードをキャンセル可能です。以下のようなログになります。

Starting session with SessionId: ssmpf-0c43a55263e8d099b
Port 33890 opened for sessionId ssmpf-0c43a55263e8d099b.
Connection accepted for session ssmpf-0c43a55263e8d099b.
Terminate signal received, exiting.

Exiting session with sessionId: ssmpf-0c43a55263e8d099b.

 

接続要件 まとめ

上記に記載した内容をまとめますと以下の通りになります。

  1. EC2 Instanceの構築と起動
  2. AmazonSSMManagedInstanceCoreを付与したIAM Roleの新規作成とEC2 Instanceへの付与
  3. VPC Endpoint 3つの作成とEC2 InstanceからVPC Endpointへのアクセスを許可するSecurity Groupの付与(Inbound)
    1. com.amazonaws.region.ssm
    2. com.amazonaws.region.ec2messages
    3. com.amazonaws.region.ssmmessages
  4. EC2 InstanceからVPC Endpointへのアクセスを許可するSecurity Groupの付与(Outbound)
  5. EC2 InstanceのSSM Agentのバージョンアップを行う(2.3.701.0以上とする)
  6. 接続元のクライアントに AWS-CLI をインストールする(1.16.234以上とする)
  7. 接続元のクライアントに Session Manager Plugin をインストールする(1.1.31.0以上とする)
  8. IAM Userを作成しCredentialを取得する
  9. IAM Userにポートフォワーディングが可能となるようssm:StartSessionの権限を付与する

以上が接続に必要となる最低限の設定となるかと存じます。参考になりましたら幸いです。

佐竹 陽一 (Yoichi Satake) エンジニアブログの記事一覧はコチラ

マネージドサービス部所属。AWS資格全冠。2010年1月からAWSを利用してきています。2021-2022 AWS Ambassadors/2023-2024 Japan AWS Top Engineers/2020-2024 All Certifications Engineers。AWSのコスト削減、最適化を得意としています。

"; doc.innerHTML = entry_notice + doc.innerHTML; }
' } }) e.innerHTML = codeBlock; });