ぶろとよ

ネットワーク系クラウドエンジニア(AWS)の技術ブログ。自動化に興味があるためAWS CLIを勉強&アウトプット中。

AWS CLI:踏み台サーバ無しでプライベートなWindows ServerへRDPする。

f:id:toyokky:20211017162252p:plain


AWSのプライベートサブネットにあるWindows ServerへRDP接続する時、パブリックに踏み台サーバを置いてアクセスするパターンが一般的かと思います。
ただその方法だと、踏み台サーバのメンテナンスも必要ですしRDPライセンス数も消費してしまいます。
それと会社内からの接続の場合、Firewallやプロキシが有ってRDP(3389)を禁止している場合もあります。


SystemsManagerセッションマネージャのポートフォフォワーディング機能を利用することで、踏み台サーバ無しで直接Windows ServerへRDP接続出来るようにできます。
また、本手順だと実通信はHTTPS(443)なのでFirewall等で禁止されずに接続できる可能性があります(自分はこのパターンで接続できました)。


本記事では、一般的な方法(図の上)は行わず、SystemsManagerのポートフォワード機能(図の中)を使用した方法を記載しています。
実際の設定ポイントは図の下へ記載しました。設定は可能な限りAWS CLIで行います。
※初回は①~⑧の全てを実行しますが、2回目移行は⑦~⑧の実行のみでOKです。

f:id:toyokky:20211017142302p:plain


この記事を読むことで出来ること

  • 踏み台サーバ無しでプライベートサブネットのWindows ServerへRDP接続できる。
  • AWS CLIで設定できる。
    • (・・・GUI部分をもう少し減らしたい・・・)
  • 会社のFirewallでRDP(3389)が閉じられてても接続できるかもしれない。

目次

検証環境

  • 検証日: 2021/10/17
  • PC環境:
    • 操作PC
      • Windows10 Home Ver21H1
        • AWS CLI: aws-cli/2.1.34 Python/3.8.8 Windows/10 exe/AMD64 prompt/off
          • -- profile default : 読み取り専用
          • -- profile aws_RW : 書き込み権限あり
            • ※ 誤操作防止のため、変更操作時のみ-- profileオプションを付けてます。
          • -- profile aws_PF : SystemsManagerポートフォワーディング用
        • PowerShell: Ver7.1.5
        • Google Chrome: バージョン: 94.0.4606.81(Official Build) (64 ビット)
    • 接続先サーバ
      • Microsoft Windows Server 2019 Datacenter : 10.0.17763
        • SSMエージェント: バージョン 3.1.338.0
      • EC2
        • Nameタグ: blog-P1a-Ec2-Srv-TestUser
        • インスタンスタイプ: t2.micro
        • AMI: ami-0890484998c6a6e77
  • 本記事のコマンドはPowerShellで検証しています。
  • コマンド内の変数やパラメータやは検証で使用したものを記載しています。自身の環境に書き換えて使用してください。
  • 個人で検証しているため実行結果に責任は持てません。必ずご自身でも検証してから使用してください。

注意事項

  • 本記事の内容はWindows PowerShellで検証しています。
  • AWS CLIはバージョン2を使用しています。
    • 本記事のコマンド出力でYAMLを使用してますが、これはバージョン2の機能です。
  • コマンド内の変数やパラメータやは検証で使用したものを記載しています。ご自身の環境に合わせ、書き換えて使用してください。
  • 個人で検証しているため実行結果に責任は持てません。必ずご自身でも検証してから使用してください。


1. 操作PC: AWS CLIをインストール

AWS CLIのインストールは別に記事を作成してますので参考にしてください。

blog.toyokky.com


2. 操作PC: SSMプラグインをインストール

参考URL

docs.aws.amazon.com

  1. 以下のURLからSSMプラグインのインストーラーをダウンロードする

    https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe

  2. ダウンロードしたEXEファイルを実行する
    • Windows によって PC が保護されましたとMicrosoft Defender SmartScreenが表示される場合は、「詳細情報 → 実行」をクリックしてインストールを進めます。
  3. ウィザードに従ってインストールを進める
  4. SSMプラグインのインストール確認
    • 新規でPowerShellを開く(既に開いている場合は開き直す)
    • コマンドを実行する
      session-manager-plugin
      • 成功時の表示:
        The Session Manager plugin is installed successfully. Use the AWS CLI to start a session.
      • 失敗時の表示:
        session-manager-plugin は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。


3. 接続先サーバ OS側: SSMエージェントをインストール

EC2をAMIから起動した場合、基本的にSSMエージェントがインストールされているため本項は実施不要です。

ただし、SSMエージェントのバージョンが古い場合はアップデートが必要です。
SSMエージェントのリリースノートを見る限り、最低でも2.3.756.0は必要かと思いました。

amazon-ssm-agent RELEASENOTES.md

■ port forwarding 系の更新を抜き出し
2.3.756.0
Terminate port forwarding session on receiving TerminateSession flag
2.3.714.0
For port forwarding session, close server connection when client drops it's connection
2.3.707.0
Keep port forwarding session open until session is terminated
2.3.672.0
Add Port plugin for SSH/SCP


4. 接続先サーバ EC2側: IAMロール設定

EC2へAWS管理ポリシー: AmazonEC2RoleforSSM を含んだIAMロールの適用が必要です。

本項ではIAMロール自体の作成はせず、EC2へ適用しているIAMロールにAmazonEC2RoleforSSM を含んでいるか、含んでいなければ追加するAWS CLIコマンドを記載します。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# IAMロールにAWS管理ポリシー: `AmazonEC2RoleforSSM` が適用されているか確認
${profile} = "aws_RW"
${iam_role_name} = "blog-P1x-Irl-Mnt-Cw-And-Ssm"

aws iam list-attached-role-policies `
    --role-name ${iam_role_name} `
    --query "AttachedPolicies[?PolicyName=='AmazonEC2RoleforSSM'].PolicyArn"

> [適用されている場合の出力]
> - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM

> [適用されていない場合の出力]
> []


# 適用されていない場合のみ、IAMロールへAWS管理ポリシーをアタッチする
aws iam attach-role-policy `
    --profile ${profile} `
    --role-name ${iam_role_name} `
    --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM


5. SystemsManager: Windows Serverがオンラインになっていること

5.1. GUIでの確認

SystemsManagerのフリートマネージャー、東京リージョン へアクセスし、一覧から対象サーバの「SSM エージェントの ping の状態」が「オンライン」であればOKです。

5.2. AWS CLIでの確認

以下のコマンドを実行し、結果がOnlineならOKです。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

## EC2インスタンスID
${ec2_name} = "blog-P1a-Ec2-Srv-TestUser"

${ec2_id} = aws ec2 describe-instances `
    --filters "Name=tag:Name,Values=${ec2_name}" `
    --query "Reservations[].Instances[].InstanceId" `
    --output text `
    ; ${ec2_id}

aws ssm describe-instance-information `
    --filters "Key=InstanceIds,Values=${ec2_id}" `
    --query "InstanceInformationList[].PingStatus"

> [結果が`Online`ならばOKです。]
> - Online

5.3. オンラインにならない場合

AWSドキュメントでトラブルシュートを出しているため、こちらを参考に調査してみてください。

aws.amazon.com


自分が実際に遭遇した問題は「プロキシサーバが原因で、サーバ側のSSMエージェント~SystemsManagerのエンドポイントがHTTPS(443)通信できない。」ことでした。

  • SSMエージェント接続用のPrivateLink(エンドポイント)をVPC内に作成した。
  • 顧客ルールでHTTPS(443)はオンプレのプロキシサーバを経由する。
  • オンプレのプロキシサーバがVPC側の名前解決できなくてNG。
  • Windows Server側のNoProxy設定へPrivateLink(エンドポイント)を含めてプロキシを向かないようにした。
  • オンラインになった。


6. IAM: 専用のIAMユーザーとアクセスキーを発行する

6.1. カスタマー管理ポリシーを作成する(送信元IPで絞る)

SystemsManagerセッションマネージャのポートフォフォワーディング専用のIAMポリシーを作成します。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# 作業フォルダの作成
${date} = Get-Date -Format yyyy-MM-dd
${work_dir} = "C:\#temp\awscli\${date}"
New-Item "${work_dir}" -itemType Directory -Force

# (参考)作成した作業フォルダを開くコマンド
Invoke-Item "${work_dir}"


# IAMポリシーを記載したJSONファイルを出力
${policy_name} = "StartPortForwardingSession_SourceIP"
${global_ip} = curl.exe ifconfig.me ; ${global_ip}

${json_content} = @"
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/*",
                "arn:aws:ssm:*:*:document/AWS-StartPortForwardingSession"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "${global_ip}/32"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}
"@
Write-Output ${json_content} | Out-File -Encoding ascii ${work_dir}\${policy_name}.json


# IAMポリシー`StartPortForwardingSession_SourceIP`の作成
${profile} = "aws_RW"

aws iam create-policy `
    --profile ${profile} `
    --policy-name ${policy_name} `
    --description "This policy is for forwarding only and limits the source IP address." `
    --policy-document file://${work_dir}\${policy_name}.json `
    --tags "Key=Name,Value=${policy_name}"

6.2. 送信元IPで絞りたくない場合のカスタマー管理ポリシー作成

こちらは送信元IPアドレスでの制限を行わない場合のIAMポリシー作成です。
送信元IPで絞りたくない人だけ実行してください。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# 作業フォルダの作成
${date} = Get-Date -Format yyyy-MM-dd
${work_dir} = "C:\#temp\awscli\${date}"
New-Item "${work_dir}" -itemType Directory -Force

# (参考)作成した作業フォルダを開くコマンド
Invoke-Item "${work_dir}"


# IAMポリシーを記載したJSONファイルを出力
${policy_name} = "StartPortForwardingSession"

${json_content} = @"
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/*",
                "arn:aws:ssm:*:*:document/AWS-StartPortForwardingSession"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}
"@
Write-Output ${json_content} | Out-File -Encoding ascii ${work_dir}\${policy_name}.json


# IAMポリシー`StartPortForwardingSession`の作成
${profile} = "aws_RW"

aws iam create-policy `
    --profile ${profile} `
    --policy-name ${policy_name} `
    --description "This policy is for forwarding only and limits the source IP address." `
    --policy-document file://${work_dir}\${policy_name}.json `
    --tags "Key=Name,Value=${policy_name}"

6.3. IAMユーザーの作成

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# IAMユーザーの作成
${profile} = "aws_RW"
${iamuser_name} = "blog-Pxx-Iur-Ssm-Forwarding"

aws iam create-user `
    --profile ${profile} `
    --user-name ${iamuser_name} `
    --tags "Key=Name,Value=${iamuser_name}"

6.4. IAMユーザーへIAMポリシーの適用

  • IAMポリシー名
    • 送信元IPで絞る: StartPortForwardingSession_SourceIP
    • 絞らない: StartPortForwardingSession
# --- PowerShell ---  ※変数部分は自分用に修正してください。

# IAMポリシーのARNを変数に格納
${profile} = "aws_RW"
${policy_name} = "StartPortForwardingSession_SourceIP"

${policy_arn}=aws iam list-policies `
    --profile ${profile} `
    --scope Local `
    --max-items 1000 `
    --query "Policies[?PolicyName=='${policy_name}'].Arn" `
    --output text `
    ; ${policy_arn}


# IAMユーザーへIAMポリシーを適用(アタッチ)
${iamuser_name} = "blog-Pxx-Iur-Ssm-Forwarding"

aws iam attach-user-policy `
    --profile ${profile} `
    --user-name ${iamuser_name} `
    --policy-arn ${policy_arn}


# 確認
aws iam list-attached-user-policies `
    --profile ${profile} `
    --user-name ${iamuser_name}

6.5. アクセスキーの発行

アクセスキーを発行します。コマンドの結果にアクセスキーとシークレットアクセスキーが表示されるため、内容を控えておいてください。

【注意】シークレットアクセスキーは作成時のみ値が表示され、後から確認することはできません。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# アクセスキーの発行
${profile} = "aws_RW"
${iamuser_name} = "blog-Pxx-Iur-Ssm-Forwarding"

aws iam create-access-key `
    --profile ${profile} `
    --user-name ${iamuser_name}

6.6. AWS CLIへアクセスキーを登録する

作成したアクセスキーをAWS CLIへ登録します。
プロファイル名はPort Forwardingなので、--profile aws_PFとします。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# AWS CLIへアクセスキーを登録する
aws configure --profile aws_PF

# 対話式で進むため、以下のパラメータを入力。
> AWS Access Key ID [None]:    <6.5で作成したアクセスキー>
> AWS Secret Access Key [None]:    <6.5で作成したシークレットアクセスキー>
> Default region name [None]:    ap-northeast-1
> Default output format [None]:    yaml

# Pager設定(more)の無効化
aws configure set cli_pager " " --profile aws_PF

# 確認
aws configure list --profile aws_PF

# (任意)認証情報の格納場所を開く
Invoke-Item ~\.aws


7. 操作PC: AWS CLIコマンドでSystemsManagerへ接続する

接続に必要な情報は「接続先サーバのEC2インスタンス-ID」と「ローカルポート番号」が必要です。
ローカルポート番号ですが、構成図では13389を指定していますが必ずしもそれを使わなくて良いです。
IANAのサイトで検索して、使用予定の無い(Unassigned)ポートを使用する方が良いと思います。
検索ワードUnassignedなら空きポートの一覧を確認できます。

www.iana.org


ちなみにですが、13389Unassignedです。13225~13399は空き。

f:id:toyokky:20211017151745p:plain:w400

# --- PowerShell ---  ※変数部分は自分用に修正してください。

# EC2インスタンスID
${ec2_name} = "blog-P1a-Ec2-Srv-TestUser"

${ec2_id} = aws ec2 describe-instances `
    --filters "Name=tag:Name,Values=${ec2_name}" `
    --query "Reservations[].Instances[].InstanceId" `
    --output text `
    ; ${ec2_id}


# SystemsManagerへ接続する(ポートフォワーディングを開始する)
${local_port} = "13389"

aws ssm start-session `
    --profile aws_PF `
    --target ${ec2_id} `
    --document-name AWS-StartPortForwardingSession `
    --parameters "portNumber=3389, localPortNumber=${local_port}"

# 接続されると以下のように表示されます
> Starting session with SessionId: blog-Pxx-Iur-Ssm-Forwarding-1234567890abcdefg.
> Port 13389 opened for sessionId blog-Pxx-Iur-Ssm-Forwarding-1234567890abcdefg.
> Waiting for connections...

※注意※ RDP中、このPowerShellは開きっ放しにしてください。


8. 操作PC: リモートデスクトップを実行する。

サーバのIPアドレスはlocalhostで、ポート番号は前項で指定したものを使用します。本記事では13389です。

8.1. リモートデスクトップ接続を開く

「7. 操作PC: AWS CLIコマンドでSystemsManagerへ接続する」とは別のPowerShellを開く。

# --- PowerShell ---  ※変数部分は自分用に修正してください。

${local_port} = "13389"

# フル画面で開く
mstsc /v:localhost:${local_port} /F

# 画面サイズ 1400x900 で開く
mstsc /v:localhost:${local_port} /w:1440 /h:900

8.2. ログイン情報を入力しRDP接続する

f:id:toyokky:20211017155654p:plain:w350

f:id:toyokky:20211017155725p:plain:w350

f:id:toyokky:20211017161450p:plain:w600

8.3. RDP接続終了後の処理

  1. Windows Server側はオンプレや踏み台サーバ接続と同様に切断します(サインアウト or ×印)。
  2. 「7. 操作PC: AWS CLIコマンドでSystemsManagerへ接続する」で開きっぱなしにしていたPowerShellを×印で閉じます。