AWS Systems Manager Session Managerのポートフォワーディング機能を使ってプライベートサブネットのWebサーバーに接続してみた
以下のAWSのブログにある通りAWS Systems Manager Session Managerでポートフォワーディング機能を使ってリモートホストにアクセスすることができます。
Webサーバーでも同じように接続できるのかやってみました。
構成
- 構成図は今回メインとなる部分を抜粋して記載しています。
- EC2への接続はSession Managerを使用するのでインターフェイス型VPCエンドポイントを作成します。
- EC2ではAmazon Linux2を使用してWebサーバーにApacheを使用します。
- こちらのドキュメントに記載されている通りAmazon Linux2のリポジトリはS3にあるので構成図には記載していませんがゲートウェイ型VPCエンドポイントを作成します。
環境作成
EC2などの環境は全て以下のCloudFormationテンプレートで作成しました。
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: port forwarding Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for VPC Parameters: - VPCCIDR - Label: default: Parameters for Subnet Parameters: - PrivateSubnet01CIDR - Label: default: Parameters for EC2 Parameters: - EC2VolumeSize - EC2VolumeIOPS - EC2AMI - EC2InstanceType Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VPCCIDR: Default: 10.1.0.0/16 Type: String PrivateSubnet01CIDR: Default: 10.1.0.0/24 Type: String EC2VolumeSize: Default: 32 Type: Number EC2VolumeIOPS: Default: 3000 Type: Number EC2AMI: Default: ami-0ffac3e16de16665e Type: AWS::EC2::Image::Id EC2InstanceType: Default: t3.micro Type: String Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: test-vpc # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PrivateSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnet01CIDR Tags: - Key: Name Value: test-private-01 VpcId: !Ref VPC # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: test-private-rtb PrivateRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet01 # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# EC2SGSSM: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for EC2 GroupName: EC2-sg-ssm SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 Tags: - Key: Name Value: EC2-sg-ssm VpcId: !Ref VPC EC2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for EC2 GroupName: EC2-sg-web SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - SourceSecurityGroupId: !Ref EC2SGSSM FromPort: 80 IpProtocol: tcp ToPort: 80 Tags: - Key: Name Value: EC2-sg-web VpcId: !Ref VPC VPCEndpointSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for Systems Manager GroupName: SystemsManager-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - SourceSecurityGroupId: !Ref EC2SGSSM FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: SystemsManager-sg VpcId: !Ref VPC # ------------------------------------------------------------# # VPC Endpoint # ------------------------------------------------------------# S3Endpoint: Type: AWS::EC2::VPCEndpoint Properties: RouteTableIds: - !Ref PrivateRouteTable ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref VPC SystemsManagerEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface PrivateDnsEnabled: true ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm VpcId: !Ref VPC SubnetIds: - !Ref PrivateSubnet01 SecurityGroupIds: - !Ref VPCEndpointSG SystemsManagerMessageEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface PrivateDnsEnabled: true ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages VpcId: !Ref VPC SubnetIds: - !Ref PrivateSubnet01 SecurityGroupIds: - !Ref VPCEndpointSG # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# EC2IAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "s3:GetObject" Resource: - !Join - '' - - 'arn:aws:s3:::amazonlinux.' - !Sub ${AWS::Region} - '.amazonaws.com/*' - !Join - '' - - 'arn:aws:s3:::amazonlinux-2-repos-' - !Sub ${AWS::Region} - '/*' ManagedPolicyName: iam-policy-ec2 EC2IAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - !Ref EC2IAMPolicy RoleName: iam-role-ec2 Tags: - Key: Name Value: iam-role-ec2 EC2IAMInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: iam-instanceprofile-ec2 Roles: - !Ref EC2IAMRole # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 DisableApiTermination: false IamInstanceProfile: !Ref EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref EC2SG SubnetId: !Ref PrivateSubnet01 Tags: - Key: Name Value: test-ec2-web UserData: !Base64 | #!/bin/bash yum update -y yum install httpd -y systemctl start httpd systemctl enable httpd echo "Session Manager Port Forwarding Test" > /var/www/html/index.html EC2SSM: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 DisableApiTermination: false IamInstanceProfile: !Ref EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref EC2SGSSM SubnetId: !Ref PrivateSubnet01 Tags: - Key: Name Value: test-ec2-ssm
上記のCloudFormationテンプレートでEC2を2台作成していますが、Nameタグが「test-ec2-ssm」になっているものがSSMマネージドEC2、「test-ec2-web」になっているものがWebサーバーになります。
デプロイは以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
動作確認
動作確認はローカルPCから行うのでAWS CLIとSession Managerプラグインを準備してください。
インストールは以下のブログを参考にしてください。
AWS CLIとSession Managerプラグインが準備できたら以下のコマンドを実行します。
aws ssm start-session --target test-ec2-ssmのインスタンスID --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"portNumber":["80"],"localPortNumber":["8080"],"host":["test-ec2-webのプライベートIPアドレス"]}'
PowerShellで実行する場合は以下のようにダブルクォーテーションをエスケープしてください
aws ssm start-session --target test-ec2-ssmのインスタンスID --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{\"portNumber\":[\"80\"],\"localPortNumber\":[\"8080\"],\"host\":[\"test-ec2-webのプライベートIPアドレス\"]}'
実行したら「http://localhost:8080」にブラウザでアクセスします。
アクセスして以下のように「Session Manager Port Forwarding Test」と表示されれば成功です。
さいごに
今回のようにアクセスが制限された環境で接続確認を行いたい際にインターネットゲートウェイを無理やり置いたり、ALBに繋げたりする必要が無いのでかなり便利な機能だと思いました。