AWS VPC PrivateLink ～ demo


AWS PrivateLinkを利用して、他のAWS VPCに対して安全にサービスを提供する。




#1.環境#

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'For making 3 ec2 on 2 VPC. One VPC is a Client VPC which has 1 ec2.
  The other is a Provider VPC which has 2 ec2. '
Mappings:
  AWSInstanceType2Arch:
    t3.medium:
      Arch: HVM64
    t3.micro:
      Arch: HVM64
    t3.small:
      Arch: HVM64
    t3a.micro:
      Arch: HVM64
  AWSRegionArch2AMI:
    us-east-1:
      HVM64: ami-0a887e401f7654935
    us-west-2:
      HVM64: ami-0e8c04af2729ff1bb
Outputs:
  AZ:
    Description: Availability Zone of the Client EC2 instance
    Value: !Join
      - ''
      - - !GetAtt 'ClientInstance.AvailabilityZone'
  ClientIP:
    Description: Client EC2 IPaddress
    Value: !Join
      - ''
      - - !GetAtt 'ClientInstance.PublicIp'
  ProviderEC2One:
    Description: 'Web server #1 on ProviderVPC'
    Value: !Join
      - ''
      - - http://
        - !GetAtt 'ProviderInstanceOne.PublicIp'
  ProviderEC2Two:
    Description: 'Web server #2 on ProviderVPC'
    Value: !Join
      - ''
      - - http://
        - !GetAtt 'ProviderInstanceTwo.PublicIp'
Parameters:
  InstanceType:
    AllowedValues:
      - t3a.micro
      - t3.micro
      - t3.small
      - t3.medium
    ConstraintDescription: must be a valid EC2 instance type.
    Default: t3.micro
    Description: WebServer EC2 instance type
    Type: String
  SSHLocation:
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
    Default: '0.0.0.0/0'
    Description: ' The IP address range that can be used to SSH to the EC2 instances'
    MaxLength: '18'
    MinLength: '9'
    Type: String
Resources:
  AttachGateway:
    Properties:
      InternetGatewayId: !Ref 'InternetGateway'
      VpcId: !Ref 'ClientVPC'
    Type: AWS::EC2::VPCGatewayAttachment
  ClientInstance:
    Properties:
      ImageId: !FindInMap
        - AWSRegionArch2AMI
        - !Ref 'AWS::Region'
        - !FindInMap
          - AWSInstanceType2Arch
          - !Ref 'InstanceType'
          - Arch
      InstanceType: !Ref 'InstanceType'
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref 'InstanceSecurityGroup'
          SubnetId: !Ref 'ClientSubnet'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: ClientEC2
      UserData: !Base64
        Fn::Join:
          - ''
          - - "#!/bin/bash -xe\n"
            - "yum update -y\n"
            - "\n"
    Type: AWS::EC2::Instance
  ClientSubnet:
    Properties:
      AvailabilityZone: us-west-2c
      CidrBlock: 10.100.0.0/24
      MapPublicIpOnLaunch: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: ClientPublic
      VpcId: !Ref 'ClientVPC'
    Type: AWS::EC2::Subnet
  ClientVPC:
    Properties:
      CidrBlock: 10.100.0.0/16
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: ClientVPC
    Type: AWS::EC2::VPC
  InboundResponsePortsNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'false'
      NetworkAclId: !Ref 'NetworkAcl'
      PortRange:
        From: '1024'
        To: '65535'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '102'
    Type: AWS::EC2::NetworkAclEntry
  InboundSSHNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'false'
      NetworkAclId: !Ref 'NetworkAcl'
      PortRange:
        From: '22'
        To: '22'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '101'
    Type: AWS::EC2::NetworkAclEntry
  InstanceSecurityGroup:
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
        - CidrIp: !Ref 'SSHLocation'
          FromPort: '22'
          IpProtocol: tcp
          ToPort: '22'
      VpcId: !Ref 'ClientVPC'
    Type: AWS::EC2::SecurityGroup
  InternetGateway:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
    Type: AWS::EC2::InternetGateway
  NetworkAcl:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
      VpcId: !Ref 'ClientVPC'
    Type: AWS::EC2::NetworkAcl
  OutBoundHTTPNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'true'
      NetworkAclId: !Ref 'NetworkAcl'
      PortRange:
        From: '80'
        To: '80'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '100'
    Type: AWS::EC2::NetworkAclEntry
  OutBoundResponsePortsNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'true'
      NetworkAclId: !Ref 'NetworkAcl'
      PortRange:
        From: '1024'
        To: '65535'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '102'
    Type: AWS::EC2::NetworkAclEntry
  ProviderAttachGateway:
    Properties:
      InternetGatewayId: !Ref 'ProviderInternetGateway'
      VpcId: !Ref 'ProviderVPC'
    Type: AWS::EC2::VPCGatewayAttachment
  ProviderInboundHTTPNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'false'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '80'
        To: '80'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '100'
    Type: AWS::EC2::NetworkAclEntry
  ProviderInboundResponsePortsNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'false'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '1024'
        To: '65535'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '102'
    Type: AWS::EC2::NetworkAclEntry
  ProviderInboundSSHNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'false'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '22'
        To: '22'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '101'
    Type: AWS::EC2::NetworkAclEntry
  ProviderInstanceOne:
    Metadata:
      AWS::CloudFormation::Init:
        config:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Join
                - ''
                - - "[main]\n"
                  - stack=
                  - !Ref 'AWS::StackId'
                  - "\n"
                  - region=
                  - !Ref 'AWS::Region'
                  - "\n"
              group: root
              mode: '000400'
              owner: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join
                - ''
                - - "[cfn-auto-reloader-hook]\n"
                  - "triggers=post.update\n"
                  - "path=Resources.ProviderInstanceOne.Metadata.AWS::CloudFormation::Init\n"
                  - 'action=/opt/aws/bin/cfn-init -v '
                  - '         --stack '
                  - !Ref 'AWS::StackName'
                  - '         --resource ProviderInstanceOne '
                  - '         --region '
                  - !Ref 'AWS::Region'
                  - "\n"
                  - "runas=root\n"
            /var/www/html/index.html:
              content: !Join
                - "\n"
                - - <img src="https://s3.amazonaws.com/cloudformation-examples/cloudformation_graphic.png"
                    alt="AWS CloudFormation Logo"/>
                  - <h1>Congratulations, you have successfully launched for AWS Summit
                    Tokyo Handson.</h1>
              group: root
              mode: '000644'
              owner: root
          packages:
            yum:
              httpd: []
          services:
            sysvinit:
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
    Properties:
      ImageId: !FindInMap
        - AWSRegionArch2AMI
        - !Ref 'AWS::Region'
        - !FindInMap
          - AWSInstanceType2Arch
          - !Ref 'InstanceType'
          - Arch
      InstanceType: !Ref 'InstanceType'
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref 'ProviderInstanceSecurityGroup'
          SubnetId: !Ref 'ProviderSubnet'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: Provider_1
      UserData: !Base64
        Fn::Join:
          - ''
          - - "#!/bin/bash -xe\n"
            - "yum update -y\n"
            - "yum update -y aws-cfn-bootstrap\n"
            - '/opt/aws/bin/cfn-init -v '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource ProviderInstanceOne '
            - '         --region '
            - !Ref 'AWS::Region'
            - "\n"
            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource ProviderInstanceOne '
            - '         --region '
            - !Ref 'AWS::Region'
            - "\n"
    Type: AWS::EC2::Instance
  ProviderInstanceSecurityGroup:
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
        - CidrIp: !Ref 'SSHLocation'
          FromPort: '22'
          IpProtocol: tcp
          ToPort: '22'
        - CidrIp: '0.0.0.0/0'
          FromPort: '80'
          IpProtocol: tcp
          ToPort: '80'
      VpcId: !Ref 'ProviderVPC'
    Type: AWS::EC2::SecurityGroup
  ProviderInstanceTwo:
    Metadata:
      AWS::CloudFormation::Init:
        config:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Join
                - ''
                - - "[main]\n"
                  - stack=
                  - !Ref 'AWS::StackId'
                  - "\n"
                  - region=
                  - !Ref 'AWS::Region'
                  - "\n"
              group: root
              mode: '000400'
              owner: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join
                - ''
                - - "[cfn-auto-reloader-hook]\n"
                  - "triggers=post.update\n"
                  - "path=Resources.ProviderInstanceOne.Metadata.AWS::CloudFormation::Init\n"
                  - 'action=/opt/aws/bin/cfn-init -v '
                  - '         --stack '
                  - !Ref 'AWS::StackName'
                  - '         --resource ProviderInstanceOne '
                  - '         --region '
                  - !Ref 'AWS::Region'
                  - "\n"
                  - "runas=root\n"
            /var/www/html/index.html:
              content: !Join
                - "\n"
                - - <img src="https://s3.amazonaws.com/cloudformation-examples/cloudformation_graphic.png"
                    alt="AWS CloudFormation Logo"/>
                  - <h1>Congratulations, you have successfully launched for AWS Summit
                    Tokyo Handson.</h1>
              group: root
              mode: '000644'
              owner: root
          packages:
            yum:
              httpd: []
          services:
            sysvinit:
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
    Properties:
      ImageId: !FindInMap
        - AWSRegionArch2AMI
        - !Ref 'AWS::Region'
        - !FindInMap
          - AWSInstanceType2Arch
          - !Ref 'InstanceType'
          - Arch
      InstanceType: !Ref 'InstanceType'
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref 'ProviderInstanceSecurityGroup'
          SubnetId: !Ref 'ProviderSubnet'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: Provider_2
      UserData: !Base64
        Fn::Join:
          - ''
          - - "#!/bin/bash -xe\n"
            - "yum update -y\n"
            - "yum update -y aws-cfn-bootstrap\n"
            - '/opt/aws/bin/cfn-init -v '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource ProviderInstanceTwo '
            - '         --region '
            - !Ref 'AWS::Region'
            - "\n"
            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource ProviderInstanceTwo '
            - '         --region '
            - !Ref 'AWS::Region'
            - "\n"
    Type: AWS::EC2::Instance
  ProviderInternetGateway:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
    Type: AWS::EC2::InternetGateway
  ProviderNetworkAcl:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
      VpcId: !Ref 'ProviderVPC'
    Type: AWS::EC2::NetworkAcl
  ProviderOutBoundHTTPNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'true'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '80'
        To: '80'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '100'
    Type: AWS::EC2::NetworkAclEntry
  ProviderOutBoundHTTPSNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'true'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '443'
        To: '443'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '101'
    Type: AWS::EC2::NetworkAclEntry
  ProviderOutBoundResponsePortsNetworkAclEntry:
    Properties:
      CidrBlock: '0.0.0.0/0'
      Egress: 'true'
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      PortRange:
        From: '1024'
        To: '65535'
      Protocol: '6'
      RuleAction: allow
      RuleNumber: '102'
    Type: AWS::EC2::NetworkAclEntry
  ProviderRoute:
    DependsOn: AttachGateway
    Properties:
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'ProviderInternetGateway'
      RouteTableId: !Ref 'ProviderRouteTable'
    Type: AWS::EC2::Route
  ProviderRouteTable:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
      VpcId: !Ref 'ProviderVPC'
    Type: AWS::EC2::RouteTable
  ProviderSubnet:
    Properties:
      AvailabilityZone: us-west-2c
      CidrBlock: 10.100.0.0/24
      MapPublicIpOnLaunch: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: ProviderPublic
      VpcId: !Ref 'ProviderVPC'
    Type: AWS::EC2::Subnet
  ProviderSubnetNetworkAclAssociation:
    Properties:
      NetworkAclId: !Ref 'ProviderNetworkAcl'
      SubnetId: !Ref 'ProviderSubnet'
    Type: AWS::EC2::SubnetNetworkAclAssociation
  ProviderSubnetRouteTableAssociation:
    Properties:
      RouteTableId: !Ref 'ProviderRouteTable'
      SubnetId: !Ref 'ProviderSubnet'
    Type: AWS::EC2::SubnetRouteTableAssociation
  ProviderVPC:
    Properties:
      CidrBlock: 10.100.0.0/16
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: ProviderVPC
    Type: AWS::EC2::VPC
  Route:
    DependsOn: AttachGateway
    Properties:
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'InternetGateway'
      RouteTableId: !Ref 'RouteTable'
    Type: AWS::EC2::Route
  RouteTable:
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
      VpcId: !Ref 'ClientVPC'
    Type: AWS::EC2::RouteTable
  SMInstanceProfile:
    Properties:
      Path: /
      Roles:
        - !Ref 'SMRole'
    Type: AWS::IAM::InstanceProfile
  SMRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
        Version: '2012-10-17'
      Path: /
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - ssm:UpdateInstanceInformation
                  - ssmmessages:CreateControlChannel
                  - ssmmessages:CreateDataChannel
                  - ssmmessages:OpenControlChannel
                  - ssmmessages:OpenDataChannel
                Effect: Allow
                Resource: '*'
              - Action:
                  - s3:GetEncryptionConfiguration
                Effect: Allow
                Resource: '*'
            Version: '2012-10-17'
          PolicyName: root-0317a
    Type: AWS::IAM::Role
  SubnetNetworkAclAssociation:
    Properties:
      NetworkAclId: !Ref 'NetworkAcl'
      SubnetId: !Ref 'ClientSubnet'
    Type: AWS::EC2::SubnetNetworkAclAssociation
  SubnetRouteTableAssociation:
    Properties:
      RouteTableId: !Ref 'RouteTable'
      SubnetId: !Ref 'ClientSubnet'
    Type: AWS::EC2::SubnetRouteTableAssociation
```



#2.NLBの作成とVPC エンドポイントサービスを設定する#

##2-1.NLBを作成する##
・ロードバランサーの設定
VPCは Provider-VPCを選択
![スクリーンショット 2020-09-28 110427.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/1a916fca-3b62-9103-6dbb-b4779f92fb2c.png)

・セキュリティ設定の構成
SSLは利用しないので、そのまま進む。

・ルーティングの設定
ターゲットグループ名だけ入力し、後はデフォルトとする。

・ターゲットの登録
LBさせる2つのインスタンスを追加。
![スクリーンショット 2020-09-28 111137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/dbd8e67e-012a-c60e-5c65-bb6a8b55ccc6.png)

・確認
完了。


##2-2.VPC エンドポイントサービスを作成する##
VPCの「エンドポイントサービスの作成」から「2-1.NLBを作成する」で作成したNLBを紐づける。
![スクリーンショット 2020-09-28 111137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/23c19a90-6073-b823-1434-99e416cb5914.png)



#3.VPC エンドポイントを作成する#
「2.VPC エンドポイントサービス設定を作成し、NLBを紐づける」で作成したエンドポイントサービスをClientPublicのVPCを指定する。
![スクリーンショット 2020-09-28 114110.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/422689ff-b9ea-e7f4-6a0f-24e03b322d1a.png)

セキュリティグループは、VPC内からのHTTP通信を受け付けるようにする。
![スクリーンショット 2020-09-28 114635.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/1376be92-d6f6-5689-c062-58952dbab6c0.png)

エンドポイントサービス側に戻り、承認作業を行う。
![スクリーンショット 2020-09-28 114931.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/f29f5001-c79f-6af5-e660-369aeb82b8fc.png)



#4.動作確認#
Client側のEC2でEC2 Instance Connectで接続し、エンドポイントにcurlで接続する。