EC2(Amazon Linux2023)のフォワードプロキシをCloudFormationで作成してみた!【環境構築編】

AWS

はじめに

今回は、AmazonLinux2023のEC2を使用してプロキシをCloudFormationで作成していきます。

本記事は、環境構築編ということで、後述の構成図の構成をCloudFormationで構築するところまでの回となります。

次回検証編で、SSM SessionManagerを使用してプライベートサブネットに配置したEC2インスタンスにアクセスするためにVPCエンドポイントを作成します。CloudFormationで作成するため以下を参考にしていきます。

ステップ 2: VPC エンドポイントを作成する - AWS Systems Manager
Amazon VPC でインターフェイス VPC エンドポイントを使用するように Systems Manager を設定することで、マネージドインスタンスのセキュリティ体制をさらに向上できます。

構成図

CloudFormationで構築

CloudFormationテンプレートは以下にございます。
ダウンロードしてデスクトップなど任意のフォルダに保存します。

GitHub - kosments/forward-proxy-ec2
Contribute to kosments/forward-proxy-ec2 development by creating an account on GitHub.

CloudFormation:YAMLテンプレートのポイント

YAMLテンプレートのポイントを記述します。

forward-proxy-vpc.yml

  1. VPC (Virtual Private Cloud): これは仮想ネットワークで、AWSリソースが起動するプライベートな分離された領域です。このコードでは、CidrBlock(VPCのIPv4 CIDRブロック)を指定し、DNSサポートとDNSホスト名を有効にしています。
  2. Internet Gateway: VPCとインターネットとの間のゲートウェイです。これにより、VPC内のインスタンスがインターネットにアクセスできるようになります。
  3. Subnets: VPC内に作成されるサブネットです。このコードではパブリックサブネットとプライベートサブネットが作成されます。それぞれ異なるCIDRブロックと利用可能ゾーンが指定されています。
  4. Route Tables: ネットワークトラフィックのルーティングを制御するためのルートテーブルです。このコードではパブリックルートテーブルとプライベートルートテーブルが作成されます。パブリックルートテーブルにはインターネットゲートウェイへのルートが追加されています。
  5. Route Table Associations: サブネットとルートテーブルの関連付けです。これにより、サブネットから送信されるトラフィックが適切なルートテーブルを使用するようになります。
Resources:
  # ------------------------------ #
  # VPC
  # ------------------------------ #
  Vpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'vpc']]
  # ------------------------------ #
  # InternetGateway
  # ------------------------------ #
  Igw:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'igw']]
  AttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref Igw
  # ------------------------------ #
  # Subnet
  # ------------------------------ #
  PublicSubnet1a:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Ref PublicSubnet1aCidr
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'pblcsbnt1a']]
  PrivateSubnet1a:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Ref PrivateSubnet1aCidr
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'prvtsbnt1a']]
  # ------------------------------ #
  # RouteTable
  # ------------------------------ #
  PublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'pblcrttbl']]
  PublicRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Igw
  PrivateRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref 'Env', !Ref 'ResourceName', 'prvtrttbl']]
  # ------------------------------ #
  # RouteTableAssociation
  # ------------------------------ #
  PublicSubnetRouteTableAssociation1a:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnet1a
      RouteTableId: !Ref PublicRouteTable
  PrivateSubnetRouteTableAssociation1a:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PrivateSubnet1a
      RouteTableId: !Ref PrivateRouteTable

forward-proxy-ec2.yml

  1. Launch TemplatesProxyLaunchTemplateClientLaunchTemplateは、それぞれプロキシサーバとクライアントサーバのEC2インスタンスを起動するためのテンプレートです。これらは、インスタンスタイプ、AMI ID、キーペア、IAMインスタンスプロファイル、ネットワークインターフェース設定など、インスタンス起動時の設定を定義します。
  2. EC2 InstancesProxyEc2InstanceClientEc2Instanceは、上記の起動テンプレートを使用して作成されるEC2インスタンスです。プロキシサーバはパブリックサブネットに、クライアントサーバはプライベートサブネットに配置されます。Amiの指定をパラメータで受け取るようにしています。AmiIDは、EC2コンソール>[AMI]>[AMIカタログ]からAmazonLinux2023のIDを使用します。
  3. Security GroupEC2SecurityGroupは、これらのEC2インスタンスに適用されるセキュリティグループです。このセキュリティグループは、インバウンドとアウトバウンドのトラフィックを制御します。
  4. User Data: これらのEC2インスタンスにはユーザデータが指定されています。これは、インスタンス起動時に実行されるシェルスクリプトです。プロキシサーバではSquidをインストールし設定し、クライアントサーバではcurlコマンドのプロキシ設定を行っています。
Resources:
  # ------------------------------ #
  # Proxy LaunchTemplate
  # ------------------------------ #
  ProxyLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Join ['-', [!Ref Env, !Ref ResourceName, 'proxy-launchtemplate']]
      LaunchTemplateData:
        KeyName: !Ref Keypair
        ImageId: !Ref AmiId
        InstanceType: t2.micro
        IamInstanceProfile:
          Arn: !GetAtt Ec2InstanceProfile.Arn
        NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          SubnetId: !Ref PublicSubnetId
          Groups:
            - !Ref EC2SecurityGroup
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Name
            Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'ec2-proxy']]
        UserData: 
          Fn::Base64: |
            #!/bin/bash
            yum -y update
            yum -y install squid
            # Squid設定ファイルのバックアップ
            cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
            # Squid設定ファイルの編集
            sed -i 's/#cache_dir ufs \/var\/spool\/squid 100 16 256/cache_dir ufs \/var\/spool\/squid 100 16 256/g' /etc/squid/squid.conf
            echo ".google.com" >> /etc/squid/blacklist.acl
            echo -e "acl BLACKLIST dstdomain \"/etc/squid/blacklist.acl\"\nhttp_access deny BLACKLIST\nhttp_access allow all\n$(cat /etc/squid/squid.conf)" > /etc/squid/squid.conf
            systemctl start squid
            systemctl enable squid
            lsof -i:3128
  # ------------------------------ #
  # Client LaunchTemplate
  # ------------------------------ #
  ClientLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Join ['-', [!Ref Env, !Ref ResourceName, 'client-launchtemplate']]
      LaunchTemplateData:
        KeyName: !Ref Keypair
        ImageId: !Ref AmiId
        InstanceType: t2.micro
        IamInstanceProfile:
          Arn: !GetAtt Ec2InstanceProfile.Arn
        NetworkInterfaces: 
        - AssociatePublicIpAddress: false
          DeviceIndex: 0
          SubnetId: !Ref PrivateSubnetId
          Groups:
            - !Ref EC2SecurityGroup
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Name
            Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'ec2-client']]
        UserData: 
          Fn::Base64: |
            #!/bin/bash
            echo 'proxy = "http://10.0.0.x:3128"' >> ~/.curlrc
  # ------------------------------ #
  # Proxy EC2Instance
  # ------------------------------ #
  ProxyEc2Instance:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref ProxyLaunchTemplate
        Version: !GetAtt ProxyLaunchTemplate.LatestVersionNumber
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          SubnetId: !Ref PublicSubnetId
          GroupSet:
            - !Ref EC2SecurityGroup
  # ------------------------------ #
  # Client EC2Instance
  # ------------------------------ #
  ClientEc2Instance:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref ClientLaunchTemplate
        Version: !GetAtt ClientLaunchTemplate.LatestVersionNumber
      NetworkInterfaces: 
        - AssociatePublicIpAddress: false
          DeviceIndex: 0
          SubnetId: !Ref PrivateSubnetId
          GroupSet:
            - !Ref EC2SecurityGroup
  # ------------------------------ #
  # EC2 SecurityGroup
  # ------------------------------ #
  EC2SecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: !Join ['-', [!Ref Env, !Ref ResourceName, 'sg-for-ec2']]
      GroupDescription: Security group for WebApServerEC2
      VpcId: !ImportValue Vpc
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'sg-for-ec2']]
  # ------------------------------ #
  # SSM Endpoint SecurityGroup
  # ------------------------------ #
  SsmEndpointSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !ImportValue Vpc
      GroupName: !Join ['-', [!Ref Env, !Ref ResourceName, 'sg-for-ssm-endpoint']]
      GroupDescription: Security group for SsmEndpoint
      Tags:
        - Key: Name
          Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'sg-for-ssm-endpoint']]
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: Allow https traffic
  # ------------------------------ #
  # VPC Endpoint
  # ------------------------------ #
  EC2Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2"
      VpcEndpointType: Interface
      PrivateDnsEnabled: true
      VpcId: !ImportValue Vpc
      SubnetIds:
        - !Ref PrivateSubnetId
      SecurityGroupIds:
        - !Ref SsmEndpointSecurityGroup
      # Tags:
      # - Key: Name
      #   Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'endpoint-ec2']]
  EC2MessageEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages"
      VpcEndpointType: Interface
      PrivateDnsEnabled: true
      VpcId: !ImportValue Vpc
      SubnetIds:
        - !Ref PrivateSubnetId
      SecurityGroupIds:
        - !Ref SsmEndpointSecurityGroup
      # Tags:
      # - Key: Name
      #   Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'endpoint-ec2messages']]
  SsmEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
      VpcEndpointType: Interface
      PrivateDnsEnabled: true
      VpcId: !ImportValue Vpc
      SubnetIds:
        - !Ref PrivateSubnetId
      SecurityGroupIds:
        - !Ref SsmEndpointSecurityGroup
      # Tags:
      # - Key: Name
      #   Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'endpoint-ssm']]
  SsmAgentEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
      VpcEndpointType: Interface
      PrivateDnsEnabled: true
      VpcId: !ImportValue Vpc
      SubnetIds:
        - !Ref PrivateSubnetId
      SecurityGroupIds:
        - !Ref SsmEndpointSecurityGroup
      # Tags:
      # - Key: Name
      #   Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'endpoint-ssmagent']]
  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcId: !ImportValue Vpc
      RouteTableIds:
        - !ImportValue PrivateRouteTable
      # Tags:
      # - Key: Name
      #   Value: !Join ['-', [!Ref Env, !Ref ResourceName, 'endpoint-s3']]

スタックの作成

早速、CloudFormationを使用してリソースを作成していきます。
手順は、以下の通りで、リンクにも詳細がございます。

  1. CloudFormation画面を開きスタックの作成>新しいソースを使用(標準)を選択
  2. テンプレートの準備完了>テンプレートファイルのアップロードを選択して、保存したymlを選択
  3. スタックに任意の名称を入力して、パラメータは環境に合わせて変更>次へを選択。※後述の手順に従う場合、デフォルト値でOK
  4. スタックオプションは特に変更せず、次へを選択
  5. レビュー画面を確認して送信を選択

forward-proxy-vpc.ymlの実行

問題なくスタックが作成されました。

forward-proxy-ec2.ymlの実行

こちらも問題なくスタックが作成されましたので、構成図のような形でリソースが揃いました。

まとめ

今回は、プロキシサーバとしてのEC2インスタンスの構築を実施しました。 次回検証編では、実際にクライアントのEC2インスタンスからインターネットに通信してみて、プロキシサーバの動作を確認していきます。
最後までご覧いただきありがとうございました。

コメント

タイトルとURLをコピーしました