MENU

Linux 踏み台ホスト経由で RDS 接続する環境を AWS CloudFormation で構築する

Linux踏み台ホスト経由でRDS接続する環境をAWSCloudFormationで構築する

お世話になります、株式会社エイトハンドレッド・テクノロジー本部の細江と申します。本記事では、Linux 踏み台ホスト経由で RDS 接続する環境を構築する、CloudFormation YAML テンプレートをご紹介します。

構成図

東京リージョンのアベイラビリティゾーン 1a と 1c それぞれにパブリックとプライベートサブネットを作成。

踏み台ホストとなる EC2 インスタンスは、Auto Scaling によって可用性を高めた上で、何らかの理由でアベイラビリティゾーンが変わってしまった場合でも、踏み台ホストの接続情報が変わらないように NLB を経由しています1

さらに、プライベートサブネットにある RDS から直接 S3 バケットへアクセスできるように VPC エンドポイントも用意します。

構成図

YAML テンプレート

今回ご説明するのは以下の YAML テンプレートになります。

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  SharedName:
    Type: String
    Default: hatena-handson
  MyIP:
    Type: String
    Description: Allowed IP address for security groups
  Ec2ImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-default-x86_64
  MasterUser:
    Type: String
    Default: masteruser
    Description: Master user name for RDS

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 172.16.0.0/26
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-vpc

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment 
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.0.0/28
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-public-1a-subnet

  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.0.16/28
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-public-1c-subnet

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-public-rtb

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation1a:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1a
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetRouteTableAssociation1c:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1c
      RouteTableId: !Ref PublicRouteTable

  PrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.0.32/28
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-private-1a-subnet

  PrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.0.48/28
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-private-1c-subnet

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${SharedName}-private-rtb

  PrivateSubnetRouteTableAssociation1a:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1a
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetRouteTableAssociation1c:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1c
      RouteTableId: !Ref PrivateRouteTable

  NetworkLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    DependsOn: InternetGatewayAttachment
    Properties:
      Name: !Sub ${SharedName}-nlb
      Subnets:
      - !Ref PublicSubnet1a
      - !Ref PublicSubnet1c
      Type: network

  NLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref NLBTargetGroup
      LoadBalancerArn: !Ref NetworkLoadBalancer
      Port: 22
      Protocol: TCP

  NLBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${SharedName}-target-grp
      Port: 22
      Protocol: TCP
      VpcId: !Ref VPC

  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
      - !Ref PublicSubnet1a
      - !Ref PublicSubnet1c
      MinSize: 1
      MaxSize: 1
      AutoScalingGroupName: !Sub ${SharedName}-auto-scaling-grp
      LaunchTemplate:
        LaunchTemplateId: !Ref EC2LaunchTemplate
        Version: !GetAtt EC2LaunchTemplate.LatestVersionNumber
      TargetGroupARNs:
      - !Ref NLBTargetGroup

  EC2LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub ${SharedName}-launch-template
      LaunchTemplateData:
        ImageId: !Ref Ec2ImageId
        InstanceType: t2.micro
        NetworkInterfaces:
        - DeviceIndex: 0
          Groups:
          - !Ref EC2SecurityGroup
        KeyName: !Ref EC2KeyPair
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Name
            Value: !Sub ${SharedName}-bastion

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${SharedName}-bastion-sg
      GroupName: !Sub ${SharedName}-bastion-sg
      SecurityGroupIngress:
      - CidrIp: !Sub ${MyIP}/32
        Description: MyIP
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      - CidrIp: !GetAtt PublicSubnet1a.CidrBlock
        Description: !Sub ${SharedName}-public-1a-subnet
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      - CidrIp: !GetAtt PublicSubnet1c.CidrBlock
        Description: !Sub ${SharedName}-public-1c-subnet
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      VpcId: !Ref VPC

  EC2KeyPair:
    Type: AWS::EC2::KeyPair
    Properties: 
      KeyName: !Sub ${SharedName}-bastion-key

  RDS:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: !Sub ${SharedName}-rds
      Engine: postgres
      AllocatedStorage: 5
      DBInstanceClass: db.t3.micro
      DBSubnetGroupName: !Ref RDSSubnetGroup
      VPCSecurityGroups:
      - !Ref RDSSecurityGroup
      AssociatedRoles:
      - FeatureName: s3Import
        RoleArn: !GetAtt RDSIAMRole.Arn
      MasterUsername: !Ref MasterUser
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}'

  RDSSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: !Sub ${SharedName}-subnet-grp
      DBSubnetGroupName: !Sub ${SharedName}-subnet-grp
      SubnetIds:
      - !Ref PrivateSubnet1a
      - !Ref PrivateSubnet1c

  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${SharedName}-rds-sg
      GroupName: !Sub ${SharedName}-rds-sg
      SecurityGroupIngress:
      - SourceSecurityGroupId: !Ref EC2SecurityGroup
        Description: !Sub ${SharedName}-bastion-sg
        FromPort: 5432
        ToPort: 5432
        IpProtocol: TCP
      VpcId: !Ref VPC

  RDSIAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SharedName}-rds-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - rds.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies:
      - PolicyName: !Sub ${SharedName}-rds-policy
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Sid: s3import
            Effect: Allow
            Action:
            - s3:GetObject
            - s3:ListBucket
            Resource:
            - !GetAtt S3Bucket.Arn
            - !Join
              - '/'
              - - !GetAtt S3Bucket.Arn
                - '*'

  RDSSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub ${SharedName}-secret
      GenerateSecretString:
        SecretStringTemplate: !Sub '{"username": "${MasterUser}"}'
        GenerateStringKey: password
        ExcludeCharacters: '"@/\'

  S3VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: com.amazonaws.ap-northeast-1.s3
      VpcId: !Ref VPC
      RouteTableIds:
      - !Ref PrivateRouteTable
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action: '*'
            Resource:
            - !GetAtt S3Bucket.Arn
            - !Join
              - '/'
              - - !GetAtt S3Bucket.Arn
                - '*'
            Condition:
              ArnEquals:
                aws:PrincipalArn: !GetAtt RDSIAMRole.Arn

Parameters

【参考】 AWS CloudFormation パラメータ

Parameters:
  SharedName:
    Type: String
    Default: hatena-handson

各リソースの Name タグ等に使用する共通の名称として SharedName というパラメータを宣言。デフォルトは hatena-handson としています。

  MyIP:
    Type: String
    Description: Allowed IP address for security groups

踏み台ホストとなる EC2 のセキュリティグループで許可する IP アドレスを入力します。

本テンプレートではサブネットマスクを /32 で記述しているので、IP アドレスを範囲で許可したい場合は、Resources > EC2SecurityGroup > Properties > SecurityGroupIngressCidrIp: !Sub ${MyIP}/32 を書き換える必要があります。

  Ec2ImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-default-x86_64

Amazon Linux 2023 AMI の最新バージョンを Systems Manager パラメータストア内の既存パラメータから取得します。

【参考】
AWS CloudFormation > パラメータ > SSM パラメータタイプ
AWS Systems Manager Parameter Store を使用して最新の Amazon Linux AMI IDを取得する
Launching Amazon Linux 2023 using the SSM parameter and AWS CLI

  MasterUser:
    Type: String
    Default: masteruser
    Description: Master user name for RDS

作成する RDS のマスタユーザとパスワード設定用のパラメータです。マスタユーザ masteruser をデフォルトにしています。

Resources

VPC から PrivateSubnetRouteTableAssociation1c までは AWS CloudFormation で VPC とサブネットを作成する にて紹介しているので、本記事では NetworkLoadBalancer 以降をご紹介します。

NetworkLoadBalancer

  NetworkLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    DependsOn: InternetGatewayAttachment
    Properties:
      Name: !Sub ${SharedName}-nlb
      Subnets:
      - !Ref PublicSubnet1a
      - !Ref PublicSubnet1c
      Type: network
DependsOn

InternetGatewayAttachment リソース作成の後に当該リソースを作成する依存関係を明示しています。

【参考】 AWS CloudFormation > DependsOn 属性

Properties > Type

今回は HTTP プロトコルを使用しないため、ロードバランサは NLB (network) にします。

【参考】
Elastic Load Balancing > Network Load Balancer のリスナー
Elastic Load Balancing > Application Load Balancer のリスナー

NLBListener, NLBTargetGroup

  NLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref NLBTargetGroup
      LoadBalancerArn: !Ref NetworkLoadBalancer
      Port: 22
      Protocol: TCP

  NLBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${SharedName}-target-grp
      Port: 22
      Protocol: TCP
      VpcId: !Ref VPC

NetworkLoadBalancer のリスナーとターゲットグループを作成。

NLBListener > Properties > DefaultActions で !Ref によって、本テンプレートで作成している NLBTargetGroup へ転送し、ポート番号は SSH のウェルノウンポートである 22 を指定しています。

【参考】 AWS CloudFormation > Ref

AutoScalingGroup

  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
      - !Ref PublicSubnet1a
      - !Ref PublicSubnet1c
      MinSize: 1
      MaxSize: 1
      AutoScalingGroupName: !Sub ${SharedName}-auto-scaling-grp
      LaunchTemplate:
        LaunchTemplateId: !Ref EC2LaunchTemplate
        Version: !GetAtt EC2LaunchTemplate.LatestVersionNumber
      TargetGroupARNs:
      - !Ref NLBTargetGroup
VPCZoneIdentifier

EC2 インスタンスを作成するサブネットを設定します。

Min/MaxSize

Auto Scaling グループによって起動&保持する EC2 インスタンス数の最小・最大値。今回はアベイラビリティゾーン 1a と 1c のいずれかにひとつ起動していれば良いので、最小も最大も 1 にしています。

LaunchTemplate

Auto Scaling グループによって起動する EC2 インスタンスの起動テンプレート EC2LaunchTemplate を指定。Version!GetAtt によって、起動テンプレートの最新バージョンを選択しています。

【参考】
AWS CloudFormation > AWS::EC2::LaunchTemplate > Return values > Fn::GetAtt

TargetGroupARNs

Auto Scaling グループによって起動した EC2 インスタンスを登録するターゲットグループを指定します。

EC2LaunchTemplate

  EC2LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub ${SharedName}-launch-template
      LaunchTemplateData:
        ImageId: !Ref Ec2ImageId
        InstanceType: t2.micro
        NetworkInterfaces:
        - DeviceIndex: 0
          Groups:
          - !Ref EC2SecurityGroup
        KeyName: !Ref EC2KeyPair
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Name
            Value: !Sub ${SharedName}-bastion

LaunchTemplateData に起動テンプレート情報を記述します。

ImageId

起動する EC2 インスタンスの AMI ID をパラメータから取得。デフォルトは Amazon Linux 2023 の最新バージョンです。

NetworkInterfaces

起動する EC2 インスタンスにアタッチする Elastic Network Interface (ENI) を作成。

  • DeviceIndex: 0 ENI のアタッチ順(プライマリ ENI は0)
  • Groups ENI に関連付けるセキュリティグループ
TagSpecifications

起動する EC2 インスタンスに付与するタグを記述しています。

EC2SecurityGroup

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${SharedName}-bastion-sg
      GroupName: !Sub ${SharedName}-bastion-sg
      SecurityGroupIngress:
      - CidrIp: !Sub ${MyIP}/32
        Description: MyIP
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      - CidrIp: !GetAtt PublicSubnet1a.CidrBlock
        Description: !Sub ${SharedName}-public-1a-subnet
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      - CidrIp: !GetAtt PublicSubnet1c.CidrBlock
        Description: !Sub ${SharedName}-public-1c-subnet
        FromPort: 22
        ToPort: 22
        IpProtocol: TCP
      VpcId: !Ref VPC

EC2LaunchTemplate によって起動する EC2 インスタンスへ適用するセキュリティグループを作成します。

SecurityGroupIngress

インバウンドルールの設定。今回は、SSH 接続するユーザ自身の IP アドレス MyIPNLBTargetGroup のヘルスチェック用に PublicSubnet1a/1cCidrBlock をポート 22 で許可しています。

EC2KeyPair

  EC2KeyPair:
    Type: AWS::EC2::KeyPair
    Properties: 
      KeyName: !Sub ${SharedName}-bastion-key

EC2LaunchTemplate によって起動する EC2 インスタンスのキーペアを作成。作成された秘密鍵は、マネージメントコンソールの「AWS Systems Manager > パラメータストア > マイパラメータ」で確認できます。

RDS

  RDS:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: !Sub ${SharedName}-rds
      Engine: postgres
      AllocatedStorage: 5
      DBInstanceClass: db.t3.micro
      DBSubnetGroupName: !Ref RDSSubnetGroup
      VPCSecurityGroups:
      - !Ref RDSSecurityGroup
      AssociatedRoles:
      - FeatureName: s3Import
        RoleArn: !GetAtt RDSIAMRole.Arn
      MasterUsername: !Ref MasterUser
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}'
Engine

データベースエンジン(今回は postgres)を指定。PostgreSQL データベースでエンジンバージョンを指定しない場合、通常は最新バージョンがデフォルト設定されます。

【参考】 利用可能な PostgreSQL データベースのバージョン

AllocatedStorage

データベースに割り当てるストレージサイズを GiB 単位で指定します。

DBSubnetGroupName

データベースに関連付ける DB サブネットグループを指定します。

VPCSecurityGroups

データベースへのアクセスを許可する VPC セキュリティグループを指定します。

AssociatedRoles

データベースに関連付ける IAM ロールを指定。S3 バケットへのアクセスを許可するため、今回は本テンプレートで作成している RDSIAMRole を記述しています。

MasterUsername

データベースのマスタユーザ ID をパラメータから取得しています。

MasterUserPassword

データベースのマスタユーザ PW を本テンプレートで作成している RDSSecret から取得しています。

RDSSubnetGroup

  RDSSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: !Sub ${SharedName}-subnet-grp
      DBSubnetGroupName: !Sub ${SharedName}-subnet-grp
      SubnetIds:
      - !Ref PrivateSubnet1a
      - !Ref PrivateSubnet1c

RDS > Properties > DBSubnetGroupName で指定しているサブネットグループを作成。データベースが PrivateSubnet1a/1c いずれかに配置されるようにします。

RDSSecurityGroup

  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${SharedName}-rds-sg
      GroupName: !Sub ${SharedName}-rds-sg
      SecurityGroupIngress:
      - SourceSecurityGroupId: !Ref EC2SecurityGroup
        Description: !Sub ${SharedName}-bastion-sg
        FromPort: 5432
        ToPort: 5432
        IpProtocol: TCP
      VpcId: !Ref VPC

データベースへ適用する VPC セキュリティグループを作成します。

SecurityGroupIngress

インバウンドルールの設定。踏み台ホストとなる EC2 インスタンスのセキュリティグループ EC2SecurityGroup を、PostgreSQL のウェルノウンポートである 5432 で許可しています。

RDSIAMRole

  RDSIAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SharedName}-rds-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - rds.amazonaws.com
          Action:
          - sts:AssumeRole
      Policies:
      - PolicyName: !Sub ${SharedName}-rds-policy
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Sid: s3import
            Effect: Allow
            Action:
            - s3:GetObject
            - s3:ListBucket
            Resource:
            - !GetAtt S3Bucket.Arn
            - !Join
              - '/'
              - - !GetAtt S3Bucket.Arn
                - '*'

データベースに関連付ける IAM ロールを作成します。

AssumeRolePolicyDocument

信頼関係(信頼されたエンティティ)の編集。
RDS サービス rds.amazonaws.com から本 IAM ロールを引き受けられるアクション sts:AssumeRole を記述しています。

Policies

インラインポリシーの作成。本テンプレートで作成しているリソース S3Bucket に対して s3:GetObjects3:ListBucket を許可しています。

【参考】 AWS CloudFormation > Join

RDSSecret

  RDSSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub ${SharedName}-secret
      GenerateSecretString:
        SecretStringTemplate: !Sub '{"username": "${MasterUser}"}'
        GenerateStringKey: password
        ExcludeCharacters: '"@/\'

データベースのマスタユーザ PW を作成。作成された値は、マネージメントコンソールの「AWS Secrets Manager > シークレット」で確認できます。

SecretStringTemplate

シークレットは JSON 構造で作成する必要があるため、そのオブジェクトを定義。username key に対する value として、データベースのマスタユーザ ID をパラメータから取得しています。

GenerateStringKey

ランダムに生成した文字列を value とする key の名称。SecretStringTemplate と合わせて、以下のような JSON を生成しています。

{
  "username": "パラメータ MasterUser の入力値",
  "password": "データベースのマスタユーザ PW となる文字列"
}

S3Bucket

  S3Bucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Delete
    Properties:
      BucketName: !Sub ${SharedName}-${AWS::AccountId}
      AccessControl: Private 
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

データベースへのデータ取込に利用する S3 バケットを作成します。

DeletionPolicy

CloudFormation スタックの削除時に本 S3 バケットも削除するよう Delete を指定2。保持する場合は Retain を記述します。

【参考】 AWS CloudFormation > DeletionPolicy 属性

Properties > BucketName

バケット名を一意にするため、今回は SharedName-AWSアカウントID という名称にしています。

【参考】 AWS CloudFormation > 擬似パラメータ参照 > AWS::AccountId

Properties > AccessControl

バケットとオブジェクトへのアクセスを管理する ACL を設定。所有者以外のユーザにはアクセスを許可しないため、Private を記述しています。

【参考】
Amazon Simple Storage Service (S3) > アクセスコントロールリスト (ACL) の概要 > 既定 ACL

Properties > PublicAccessBlockConfiguration

配下4つの構成要素をすべて true にすることで、パブリックアクセスをすべてブロックします。

S3VPCEndpoint

  S3VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: com.amazonaws.ap-northeast-1.s3
      VpcId: !Ref VPC
      RouteTableIds:
      - !Ref PrivateRouteTable
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action: '*'
            Resource:
            - !GetAtt S3Bucket.Arn
            - !Join
              - '/'
              - - !GetAtt S3Bucket.Arn
                - '*'
            Condition:
              ArnEquals:
                aws:PrincipalArn: !GetAtt RDSIAMRole.Arn

プライベートサブネットのルートテーブル PrivateRouteTable から
S3 (com.amazonaws.ap-northeast-1.s3) へのアクセスを可能にする VPC エンドポイントを作成。

【参考】 Amazon VPC > AWS PrivateLink と統合する AWS のサービス

ポリシーは、対象バケット(Resource)とIAMロール(Condition)を限定した上で S3Bucket に対するすべてのアクションを許可しています。

最後に

とあるプロジェクトの検証用として本環境を CloudFormation で構築したので、そのテンプレートを抽象化して公開したら、どなたかのお役に立つのではないかと思いました。

ややこしい設定項目を可能な限り減らし、必要最低限のプロパティしか記載しないようにしたので、AWS 初中級者の方々にご活用いただけましたら嬉しいです!


  1. 作成した環境への接続方法は、SSH ポートフォワーディングで RDS 接続する でご紹介しています。
  2. スタック削除前にバケットを「空にする」必要があります。