この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。
今回は AWS Systems Manager (SSM) の「セッションマネージャー」機能について実験してみました。
同じような内容で「Linux編」と「Windows編」を執筆しています。
こちらのブログエントリは「Windows編」です。
Linux編は下記リンク先を参照してください。
Linux編とWindows編で内容が重複している箇所が多々ありますが、ご了承ください。
はじめに
セッションマネージャーには「プライベートサブネットのEC2に直接接続できる」「IAMでアクセス制御を行うためパスワードや秘密鍵の管理が不要」などのメリットがありますが、「マネジメントコンソールからWebブラウザだけでEC2へシェルログインして操作できる」という隠れた(?)利点もあります。
特に、インターネットへのアクセスがHTTPプロキシ経由となっている企業・学校などに所属されている方は、助かっている方も多いのではないでしょうか。
ところが、セッションマネージャーの便利な機能「ポートフォワーディング」を利用しようとすると、一転して話が変わります。
ポートフォワーディング機能はマネジメントコンソールから利用することができず、AWS CLIを使う必要があるためです。
しかし、AWS CLIも内部ではAWSのWeb APIを呼び出すことで処理を実行しており、プロキシ経由でのHTTP(S)の通信が行えればAWS CLIを利用できるはずです。
そこで、実際にプロキシ経由で利用できるか、試してみました。
SSMセッションマネージャーの「ポートフォワーディング」について
機能や手順については、こちらのブログが分かり易いかと思います。
SSMセッションマネージャーの「ポートフォワーディング」を利用する際、クライアントから接続先EC2インスタンスへの通信経路はこのようになります。
クライアントからSSMまでHTTPSで通信できればよい訳ですから、今回はHTTPプロキシ経由でこのように接続してみようと思います。
検証環境
「さあ、試してみよう」と思ったところ、困ったことに気付きました。
弊社ではHTTPプロキシではなく「SOCKSプロキシ」を利用しており、そもそも、現在は絶賛「テレワーク中」であるため、HTTPプロキシを使って実際に試すことができないのです。
そこで、AWS上に疑似的に「HTTPプロキシを設置しているオンプレミス環境」を構築して、試すことにしました。
図の左側が、SSMセッションマネージャーで接続する対象となるAWS環境です。
図の右側のように、別のAWSアカウントで疑似オンプレミス環境を構築します。
(別のAWSアカウントを用意するのが難しければ、同一AWSアカウントの別のVPCでも構いません)
パブリックサブネットにEC2インスタンスを配置してHTTPプロキシをインストールします。
プライベートサブネットには検証用クライアントとなるEC2インスタンスを配置し、プロキシを参照する設定を行います。
ポイントとしては、パブリックサブネットにNATゲートウェイ/NATインスタンスを設置しないことです。
そうすることで、HTTPプロキシ経由でなければインターネットへアクセスできないオンプレミス環境を再現することができます。
なお、検証用クライアントへのログインのために踏み台サーバー (Bastion) を利用します。
(ここもSSMセッションマネージャーを使うという方法もありますが、あちこちでSSMによる接続が登場すると分かり辛くなると思いますので、今回は踏み台サーバーを採用しました)
検証環境を構築する
AWS環境のプロビジョニング
接続対象のAWS環境、疑似オンプレミス環境を構築するCloudFormationテンプレートを用意しました。
それぞれ、環境を作成したいAWSアカウント・リージョンで実行してください。
(事前にキーペアを準備しておいてください。また、CloudFormationスタック作成時に自分のIPアドレスを聞かれますので、入力してください)
接続対象AWS環境作成CloudFormationテンプレート (クリックすると展開します)
cfn-ssmtest-win.yaml
---
AWSTemplateFormatVersion: "2010-09-09"
Description: "SSM test environment (Windows)"
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "General Information"
Parameters:
- SystemName
- Label:
default: "Network Configuration"
Parameters:
- CidrBlockVPC
- CidrBlockSubnetPublic
- CidrBlockSubnetPrivate
- Label:
default: "EC2 Instance Configuration"
Parameters:
- EC2ImageID
- EC2InstanceType
- EC2KeyName
- EC2VolumeType
- EC2VolumeSize
Parameters:
SystemName:
Type: String
Default: ssmtestwin
CidrBlockVPC:
Type: String
Default: 192.168.0.0/16
CidrBlockSubnetPublic:
Type: String
Default: 192.168.0.0/24
CidrBlockSubnetPrivate:
Type: String
Default: 192.168.128.0/24
EC2ImageID:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base
EC2InstanceType:
Type: String
Default: t3.micro
EC2KeyName:
Type: AWS::EC2::KeyPair::KeyName
EC2VolumeType:
Type: String
Default: gp2
EC2VolumeSize:
Type: String
Default: 30
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CidrBlockVPC
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${SystemName}-vpc"
- Key: System
Value: !Ref SystemName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${SystemName}-igw"
- Key: System
Value: !Ref SystemName
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
SubnetPublic:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref CidrBlockSubnetPublic
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-subnet"
- Key: System
Value: !Ref SystemName
SubnetPrivate:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref CidrBlockSubnetPrivate
Tags:
- Key: Name
Value: !Sub "${SystemName}-private-subnet"
- Key: System
Value: !Ref SystemName
EIPNatGateway:
DependsOn:
- VPCGatewayAttachment
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
DependsOn:
- EIPNatGateway
- SubnetPublic
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPNatGateway.AllocationId
SubnetId: !Ref SubnetPublic
Tags:
- Key: Name
Value: !Sub "${SystemName}-natgateway"
- Key: System
Value: !Ref SystemName
RouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-rtb"
- Key: System
Value: !Ref SystemName
RouteTablePrivate:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-private-rtb"
- Key: System
Value: !Ref SystemName
RouteIGW:
DependsOn:
- VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTablePublic
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteNatGateway:
DependsOn:
- VPCGatewayAttachment
- NatGateway
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTablePrivate
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
RouteTableAssociationPublic:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublic
RouteTableId: !Ref RouteTablePublic
RouteTableAssociationPrivate:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate
RouteTableId: !Ref RouteTablePrivate
SecurityGroupServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${SystemName}-server-sg"
GroupDescription: "Security group for server"
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-server-sg"
- Key: System
Value: !Ref SystemName
IAMRoleServer:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${SystemName}-server-role"
AssumeRolePolicyDocument: |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Path: /
IAMInstanceProfileServer:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub "${SystemName}-server-role"
Roles:
- !Ref IAMRoleServer
Path: /
EC2InstanceServer:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2ImageID
InstanceType: !Ref EC2InstanceType
KeyName: !Ref EC2KeyName
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: !Ref EC2VolumeType
VolumeSize: !Ref EC2VolumeSize
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref SubnetPrivate
GroupSet:
- !Ref SecurityGroupServer
IamInstanceProfile: !Ref IAMInstanceProfileServer
Tags:
- Key: Name
Value: !Sub "${SystemName}-server"
- Key: System
Value: !Ref SystemName
Outputs:
VPC:
Value: !Ref VPC
Export:
Name: !Sub "${AWS::StackName}::VPC"
SubnetPublic:
Value: !Ref SubnetPublic
Export:
Name: !Sub "${AWS::StackName}::SubnetPublic"
SubnetPrivate:
Value: !Ref SubnetPrivate
Export:
Name: !Sub "${AWS::StackName}::SubnetPrivate"
SecurityGroupServer:
Value: !Ref SecurityGroupServer
Export:
Name: !Sub "${AWS::StackName}::SecurityGroupServer"
IAMRoleServer:
Value: !Ref IAMRoleServer
Export:
Name: !Sub "${AWS::StackName}::IAMRoleServer"
IAMInstanceProfileServer:
Value: !Ref IAMInstanceProfileServer
Export:
Name: !Sub "${AWS::StackName}::IAMInstanceProfileServer"
EC2InstanceServer:
Value: !Ref EC2InstanceServer
Export:
Name: !Sub "${AWS::StackName}::EC2InstanceServer"
疑似オンプレミス環境作成CloudFormationテンプレート (クリックすると展開します)
cfn-onpremises-win.yaml
---
AWSTemplateFormatVersion: "2010-09-09"
Description: "SSM test environment (Windows) (virtual 'on-premises' environment)"
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "General Information"
Parameters:
- SystemName
- Label:
default: "Network Configuration"
Parameters:
- CidrBlockVPC
- CidrBlockSubnetPublic
- CidrBlockSubnetPrivate
- MyIP
- Label:
default: "EC2 Instance Configuration (Common)"
Parameters:
- EC2KeyName
- Label:
default: "EC2 Instance Configuration (Proxy)"
Parameters:
- EC2ProxyImageID
- EC2ProxyInstanceType
- EC2ProxyVolumeType
- EC2ProxyVolumeSize
- Label:
default: "EC2 Instance Configuration (Bastion)"
Parameters:
- EC2BastionImageID
- EC2BastionInstanceType
- EC2BastionVolumeType
- EC2BastionVolumeSize
- Label:
default: "EC2 Instance Configuration (Client)"
Parameters:
- EC2ClientImageID
- EC2ClientInstanceType
- EC2ClientVolumeType
- EC2ClientVolumeSize
Parameters:
SystemName:
Type: String
Default: onpremiseswin
CidrBlockVPC:
Type: String
Default: 10.0.0.0/16
CidrBlockSubnetPublic:
Type: String
Default: 10.0.0.0/24
CidrBlockSubnetPrivate:
Type: String
Default: 10.0.128.0/24
MyIP:
Type: String
EC2KeyName:
Type: AWS::EC2::KeyPair::KeyName
EC2ProxyImageID:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
EC2ProxyInstanceType:
Type: String
Default: t3.micro
EC2ProxyVolumeType:
Type: String
Default: gp2
EC2ProxyVolumeSize:
Type: String
Default: 8
EC2BastionImageID:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base
EC2BastionInstanceType:
Type: String
Default: t3.micro
EC2BastionVolumeType:
Type: String
Default: gp2
EC2BastionVolumeSize:
Type: String
Default: 30
EC2ClientImageID:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base
EC2ClientInstanceType:
Type: String
Default: t3.micro
EC2ClientVolumeType:
Type: String
Default: gp2
EC2ClientVolumeSize:
Type: String
Default: 30
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CidrBlockVPC
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${SystemName}-vpc"
- Key: System
Value: !Ref SystemName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${SystemName}-igw"
- Key: System
Value: !Ref SystemName
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
SubnetPublic:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref CidrBlockSubnetPublic
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-subnet"
- Key: System
Value: !Ref SystemName
SubnetPrivate:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
CidrBlock: !Ref CidrBlockSubnetPrivate
Tags:
- Key: Name
Value: !Sub "${SystemName}-private-subnet"
- Key: System
Value: !Ref SystemName
RouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-rtb"
- Key: System
Value: !Ref SystemName
RouteTablePrivate:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-private-rtb"
- Key: System
Value: !Ref SystemName
RouteIGW:
DependsOn:
- VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTablePublic
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableAssociationPublic:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPublic
RouteTableId: !Ref RouteTablePublic
RouteTableAssociationPrivate:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetPrivate
RouteTableId: !Ref RouteTablePrivate
SecurityGroupProxy:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${SystemName}-proxy-sg"
GroupDescription: "Security group for Proxy"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref MyIP
Description: "SSH access from MyIP"
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
CidrIp: !Ref CidrBlockVPC
Description: "Access from this VPC to Proxy"
Tags:
- Key: Name
Value: !Sub "${SystemName}-proxy-sg"
- Key: System
Value: !Ref SystemName
SecurityGroupBastion:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${SystemName}-bastion-sg"
GroupDescription: "Security group for Bastion"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
CidrIp: !Ref MyIP
Description: "RDP access from MyIP"
Tags:
- Key: Name
Value: !Sub "${SystemName}-bastion-sg"
- Key: System
Value: !Ref SystemName
SecurityGroupClient:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${SystemName}-client-sg"
GroupDescription: "Security group for Client"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
SourceSecurityGroupId: !Ref SecurityGroupBastion
Description: "RDP access from Bastion"
Tags:
- Key: Name
Value: !Sub "${SystemName}-client-sg"
- Key: System
Value: !Ref SystemName
EC2InstanceProxy:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2ProxyImageID
InstanceType: !Ref EC2ProxyInstanceType
KeyName: !Ref EC2KeyName
SourceDestCheck: false
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: !Ref EC2ProxyVolumeType
VolumeSize: !Ref EC2ProxyVolumeSize
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref SubnetPublic
GroupSet:
- !Ref SecurityGroupProxy
Tags:
- Key: Name
Value: !Sub "${SystemName}-proxy"
- Key: System
Value: !Ref SystemName
EC2InstanceBastion:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2BastionImageID
InstanceType: !Ref EC2BastionInstanceType
KeyName: !Ref EC2KeyName
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: !Ref EC2BastionVolumeType
VolumeSize: !Ref EC2BastionVolumeSize
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref SubnetPublic
GroupSet:
- !Ref SecurityGroupBastion
Tags:
- Key: Name
Value: !Sub "${SystemName}-bastion"
- Key: System
Value: !Ref SystemName
EC2InstanceClient:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2ClientImageID
InstanceType: !Ref EC2ClientInstanceType
KeyName: !Ref EC2KeyName
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: !Ref EC2ClientVolumeType
VolumeSize: !Ref EC2ClientVolumeSize
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref SubnetPrivate
GroupSet:
- !Ref SecurityGroupClient
Tags:
- Key: Name
Value: !Sub "${SystemName}-client"
- Key: System
Value: !Ref SystemName
Outputs:
VPC:
Value: !Ref VPC
Export:
Name: !Sub "${AWS::StackName}::VPC"
SubnetPublic:
Value: !Ref SubnetPublic
Export:
Name: !Sub "${AWS::StackName}::SubnetPublic"
SubnetPrivate:
Value: !Ref SubnetPrivate
Export:
Name: !Sub "${AWS::StackName}::SubnetPrivate"
SecurityGroupProxy:
Value: !Ref SecurityGroupProxy
Export:
Name: !Sub "${AWS::StackName}::SecurityGroupProxy"
SecurityGroupBastion:
Value: !Ref SecurityGroupBastion
Export:
Name: !Sub "${AWS::StackName}::SecurityGroupBastion"
SecurityGroupClient:
Value: !Ref SecurityGroupClient
Export:
Name: !Sub "${AWS::StackName}::SecurityGroupClient"
EC2InstanceProxy:
Value: !Ref EC2InstanceProxy
Export:
Name: !Sub "${AWS::StackName}::EC2InstanceProxy"
EC2InstanceBastion:
Value: !Ref EC2InstanceBastion
Export:
Name: !Sub "${AWS::StackName}::EC2InstanceBastion"
EC2InstanceClient:
Value: !Ref EC2InstanceClient
Export:
Name: !Sub "${AWS::StackName}::EC2InstanceClient"
SSMセッションマネージャーで接続できることの確認
AWS環境を作成しましたら、接続対象EC2インスタンスへSSMセッションマネージャーで接続できることを確認しましょう。
対象EC2インスタンスを右クリックして「接続」を選択し、「セッションマネージャー」を選択して「接続」をクリックします。
接続できることを確認できましたら、次へ進みましょう。
HTTPプロキシを構築する
今度は疑似オンプレミス環境の方に移って、まずはHTTPプロキシを構築します。
HTTPプロキシのソフトウェアとしてSquidを使用します。
EC2インスタンス「proxy」へSSHで接続します:
$ ssh -i ~/.ssh/<pemfile> ec2-user@<proxy_public_ip>
yumを使ってSquidをインストールします:
$ sudo yum install -y squid
Squidの設定ファイルを編集します:
$ sudo vi /etc/squid/squid.conf
プロキシの待ち受けポートを「8080」に変更します:
:
# Squid normally listens to port 3128
http_port 3128 → 8080 へ変更する
:
設定ファイルを保存して、Squidを起動します:
$ sudo systemctl enable squid.service
$ sudo systemctl start squid.service
Squidが正常に起動すれば準備はOKです。
クライアントでプロキシ参照の設定を行う
EC2インスタンス「client」へリモートデスクトップ接続で接続します。
踏み台サーバー「bastion」へリモートデスクトップ接続でいったん接続し、「bastion」上で更にリモートデスクトップ接続を起動して「client」へ接続します。
HTTPプロキシ参照の設定を行います。
設定方法は、AWSドキュメントの「AWS CLI」の下記ページの記述に従います。
HTTP プロキシを使用する - AWS Command Line Interface
コマンドプロンプトを起動して、以下のコマンドを実行します。
C:\> setx HTTP_PROXY http://<proxy_private_ip>:8080
C:\> setx HTTPS_PROXY http://<proxy_private_ip>:8080
なお、今回は疑似的なクライアントとしてEC2インスタンスを利用していますので、以下のコマンドも実行する必要があります。
(これは、EC2インスタンスメタデータへアクセスする際にプロキシを使用しないようにする設定です。物理のクライアントPCを利用している際は設定不要です)
C:\> setx NO_PROXY 169.254.169.254
プロキシ参照の設定を行いましたので、実際にプロキシを使ってインターネットへアクセスできることを確認しましょう。
AWSが提供しているアクセス元IPアドレス確認サイトcheckip.amazonaws.com
を利用します。
PowerShellコンソールを起動して、以下のコマンドレットを実行します。
PS C:\> Invoke-WebRequest -Uri https://checkip.amazonaws.com/
ただし、PowerShellのバージョンが7.0未満の場合は、環境変数HTTP_PROXY
、HTTPS_PROXY
の設定を残念ながら参照してくれないようです。
その場合は、以下のようにプロキシサーバーのアドレスを直接指定して実行します。
PS C:\> Invoke-WebRequest -Uri https://checkip.amazonaws.com/ -Proxy http://<proxy_private_ip>:8080
実行結果は以下のようになりました。
StatusCode : 200
StatusDescription : OK
Content : {49, 56, 46, 50...}
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 15
Date: Wed, 24 Jun 2020 11:30:20 GMT
Server: lighttpd/1.4.53
XX.XX.XX.XX
Headers : {[Connection, keep-alive], [Content-Length, 15], [Date, Wed, 24 Jun 2020 11:30:20 GMT], [Server, lighttpd/1.4.53]}
RawContentLength : 15
ここまで上手くいっていれば、アクセス元のIPアドレスが表示されるはずです。
表示されているIPアドレスが「EC2インスタンス『proxy』のパブリックIPアドレス」であることを確認してください。
SSMセッションマネージャーを利用するためのIAMユーザーの作成
冒頭でチラッと書きましたように、SSMセッションマネージャーはIAMユーザーによるアクセス制御が可能です。
クライアントでSSMセッションマネージャーを使うためのIAMユーザーを作成しましょう。
まず、アクセス権限を定義するIAMポリシーを作成します。
マネジメントコンソールで「IAM」から「ポリシー」を選択して、「ポリシーの作成」をクリックします。
アクセス権限を編集する画面になりますので、「JSON」タブを選択します。
テキストボックスに以下の内容を貼り付けて、「ポリシーの確認」をクリックします。
{
"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}-*"
]
}
]
}
ポリシーの名前を入力して、「ポリシーの作成」をクリックします。
(名前は任意ですが、ここではssm-session-manager-policy
としました)
これでIAMポリシーが作成されました。
続けて、IAMユーザーを作成します。
マネジメントコンソールで「IAM」から「ユーザー」を選択して、「ユーザーを追加」をクリックします。
ユーザーの名前を入力します。
(名前は任意ですが、ここではremote-access-user
としました)
アクセスの種類のうち「プログラムによるアクセス」にのみチェックを入れます。
「次のステップ: アクセス権限」をクリックします。
「既存のポリシーを直接アタッチ」を選択します。
ポリシーの一覧から、さきほど作成したIAMポリシーを探して、ポリシー名の左側のチェックボックスにチェックを入れます。
(検索窓にポリシー名を入力すると探すのが楽です)
「次のステップ: タグ」をクリックします。
次の画面「タグの追加 (オプション)」は何も入力せずスキップします。
確認画面になりますので、内容を確認して「ユーザーの作成」をクリックします。
AWS CLIを使ってSSMセッションマネージャーを使うために必要となる「アクセスキーID」「シークレットアクセスキー」が表示されます。
この画面を閉じてしまうと「シークレットアクセスキー」は二度と確認することができなくなりますので、「.csvのダウンロード」をクリックして保存しましょう。
(保存したcsvファイルやメモしたシークレットアクセスキーは、安全な場所に保管してください)
これでIAMユーザーも作成されました。
次の手順へ進みます。
クライアントでAWS CLIのインストール・設定を行う
以下のページを参照して、AWS CLIをインストールします。
Windows への AWS CLI バージョン 2 のインストール - AWS Command Line Interface
インストーラーを下記リンク先からダウンロードします。
https://awscli.amazonaws.com/AWSCLIV2.msi
ダウンロードしたインストーラー (MSIファイル) をダブルクリックして実行します。
設定は全てデフォルト値で構いません。
インストールが終わりましたら、コマンドが実行できることを確認しましょう。
C:\> aws --version
aws-cli/2.0.25 Python/3.7.7 Windows/10 botocore/2.0.0dev29
AWS CLIをインストールしましたら、AWS CLIを実行するユーザーの認証情報設定を行います。
設定ファイルと認証情報ファイルの設定 - AWS Command Line Interface
aws configure
コマンドを実行して、さきほどIAMユーザーを作成した際に取得した「アクセスキーID」「シークレットアクセスキー」を入力します。
C:\> aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]:
これで認証情報が保存されました。
指定したIAMユーザーによってAWS CLIのコマンドが実行できることを確認します。
C:\> aws sts get-caller-identity
{
"UserId": "XXXXXXXXXXXXXXXXXXXXX",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/remote-access-user"
}
このようにArn
欄にIAMユーザー名remote-access-user
が表示されていればOKです。
(エラーとなった場合は、ここまでの手順を確認してください)
※ 補足
「EC2インスタンスに対してIAMによるアクセス権限を与える場合は『IAMロール』と『インスタンスプロファイル』を使うのがベストプラクティスじゃないの?」と思われる方もいらっしゃると思います。
その通りなのですが、今回はあくまで「オンプレミス環境のクライアント」を疑似的に再現しているため、アクセスキーIDとシークレットアクセスキーによる認証情報の設定手順を採用しています。
Session Manager Pluginのインストール
これでAWS CLIを実行することができるようになりましたが、AWS CLIでSSMセッションマネージャーを使うためには「Session Manager Plugin」を追加でインストールする必要があります。
(オプション) AWS CLI 用の Session Manager Plugin をインストールする - AWS Systems Manager
インストーラーを下記リンク先からダウンロードします。
https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe
ダウンロードしたインストーラー (EXEファイル) をダブルクリックして実行します。
設定は全てデフォルト値で構いません。
インストールが終わりましたら、以下のコマンドを実行してインストール結果を確認しましょう。
C:\> session-manager-plugin
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
接続を試してみる
SSMセッションマネージャーを使ってEC2インスタンスへ接続してみる
さて、全ての準備が整いましたので、いよいよセッションマネージャーを使ってEC2インスタンスへ接続してみたいと思います。
セッションを開始する - AWS Systems Manager
以下のようにコマンドを実行します。
C:\> aws ssm start-session --target <instance-id>
実際に接続してみると、以下のようになりました。
セッションIDを見るとIAMユーザーremote-access-user
でセッションが開始されたことが分かります。
セッションが開始されると、マネジメントコンソールでSSMセッションマネージャーを利用する時と同様にプロンプトPS C:\Windows\system32>
が表示されます。
C:\> aws ssm start-session --target i-0c8ab910de5d74cd8
Starting session with SessionId: remote-access-user-090615001a0b9ce6c
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Windows\system32>
接続先でコマンドを実行してみましょう。
PS C:\Windows\system32> Get-ComputerInfo
WindowsBuildLabEx : 17763.1.amd64fre.rs5_release.180914-1434
WindowsCurrentVersion : 6.3
WindowsEditionId : ServerDatacenter
WindowsInstallationType : Server
WindowsInstallDateFromRegistry : 2020/06/23 6:55:14
WindowsProductId : XXXXX-XXXXX-XXXXX-XXXXX
WindowsProductName : Windows Server 2019 Datacenter
WindowsRegisteredOrganization : Amazon.com
WindowsRegisteredOwner : EC2
WindowsSystemRoot : C:\Windows
WindowsVersion : 1809
(以下省略)
PS C:\Windows\system32>
ちゃんと対象のEC2インスタンスへ接続できていることが確認できました。
確認が終わりましたら、exit
コマンドで接続を終了してください。
SSMセッションマネージャーの「ポートフォワーディング」を使ってみる
最後に、今回の最終目標である「HTTPプロキシ経由でSSMセッションマネージャーの『ポートフォワーディング』を利用する」を試してみたいと思います。
今回は、ポートフォワーディングを使って接続する対象として「リモートデスクトップ接続 (RDP)」を使いたいと思います。
以下のようにコマンドを実行します。
C:\> aws ssm start-session ^
--target <instance-id> ^
--document-name AWS-StartPortForwardingSession ^
--parameters portNumber="3389",localPortNumber="13389"
実際に接続してみると、以下のようになります。
IAMユーザーremote-access-user
でセッションが開始された後、セッション内でポート13389
がオープンされたことが分かります。
C:\> aws ssm start-session ^
More? --target i-0c8ab910de5d74cd8 ^
More? --document-name AWS-StartPortForwardingSession ^
More? --parameters portNumber="3389",localPortNumber="13389"
Starting session with SessionId: remote-access-user-0e20f92a19c70c08d
Port 13389 opened for sessionId remote-access-user-0e20f92a19c70c08d.
ポートフォワーディングによる接続が確立しましたので、リモートデスクトップ接続で対象EC2インスタンスへ接続したいと思います。
「リモートデスクトップ接続」を起動して、接続先アドレスにlocalhost:13389
を指定して接続します。
資格情報はAdministrator
およびパスワードを入力してください。
(Administratorのパスワードは、マネジメントコンソールから「Windowsパスワードの取得」で確認してください)
対象のEC2インスタンスへリモートデスクトップ接続できれば成功です!
いかがでしょうか?
各サーバーを行ったり来たりしながら設定を行うので分かり辛いところがあるかもしれませんが、ここまでの手順を1ステップずつ実施すれば、無事に接続できたのではないかと思います。
おわりに
HTTPプロキシ経由でしかインターネットへアクセスできない環境で、SSMセッションマネージャーの「ポートフォワーディング」機能が利用できることが確認できました。
今回は疑似的にオンプレミス環境をAWS上に構築して検証を行いましたが、実際のオンプレミス環境の場合でも今回の手順に沿って実施して頂ければ (一部読み替えが必要ですが) 利用できるのではないかと思います。
なお、今回はHTTPプロキシとしてSquidを利用しましたが、Squidの設定によっては、あるいは他のプロキシソフトウェアを利用している場合は、今回のように上手く接続できるとは限らない可能性があります。
その場合はご容赦ください。