プライベートサブネットのインスタンスでyum/dnfを実行する

プライベートサブネット内のインスタンスでyumを実行する

プライベートサブネット内のインスタンスでyum/dnfを実行する構成

プライベートサブネット内のインスタンスで、yum/dnfを実行する方法を確認します。

今回は以下の2パターンを確認します。

  1. NATゲートウェイを使用する方法
  2. VPCエンドポイントを使用する方法

通常、yum/dnfを実行するためには、実行するインスタンスをパブリックサブネットに配置した上で、パブリックアドレスを設定し、直接インターネットにアクセスする形となります。しかし今回ご紹介する方法では、インスタンスにプライベートサブネットに配置し、プライベートアドレスだけを付与します。そのため外部からインスタンスに不正にアクセスされるリスクを低減することができ、システム全体のセキュリティを高めることができます。

構築する環境

Diagram of yum on instances in privates subnets.

VPC内の3つのサブネットを作成します。1つはインターネットにアクセス可能なパブリックサブネットとし、残りの2つは不可なプライベートサブネットとします。
2つのプライベートサブネットにEC2インスタンスを1つずつ配置します。インスタンスは最新のAmazon Linux 2023ベースとします。
パブリックサブネット内にNATゲートウェイを配置します。
2種類のVPCエンドポイントを配置します。1つ目はSystem Manager用のエンドポイントです。同サービスのSession Manager機能で、プライベートサブネット内のインスタンスにアクセスするために使用します。2つ目はS3用のVPCエンドポイントです。後述のAmazon Linux用のdnfリポジトリであるS3バケットにアクセスするために使用します。

検証のシナリオ

プライベートサブネット内のインスタンスからdnfの実行を、以下の2つのパターンで確認します。

  1. NATゲートウェイを経由して、インターネット上のdnfリポジトリにアクセスし、dnfを実行する ※パターン1
  2. VPCエンドポイントを経由して、S3バケット上のdnfリポジトリにアクセスし、dnfを実行する ※パターン2

環境構築用のCloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/002

テンプレートファイルのポイント解説

パターン1:NATゲートウェイを経由して、インターネット上のdnfリポジトリにアクセスする

パブリックサブネットにNATゲートウェイを配置する

まずパブリックサブネット周りのリソースを確認します。

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidrBlock
      EnableDnsHostnames: true
      EnableDnsSupport: true

  IGW:
    Type: AWS::EC2::InternetGateway

  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref IGW

  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP.AllocationId
      SubnetId: !Ref PublicSubnet

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref CidrIp1
      VpcId: !Ref VPC
      AvailabilityZone: !Sub ${AWS::Region}${AvailabilityZone1}

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  RouteToInternet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW
Code language: YAML (yaml)

インターネットゲートウェイおよびパブリックサブネットを定義します。

パブリックサブネットとは、直接のインターネットゲートウェイ向きのルートを持つサブネットのことです。
今回はパブリックサブネット用のルートテーブルを作成し、インターネットゲートウェイ向けのデフォルトルートを定義します。
デフォルトルートはDestinationCidrBlockプロパティに「0.0.0.0/0」を指定することで作成することができます。

パブリックサブネット内にNATゲートウェイを配置します。

NAT ゲートウェイは、ネットワークアドレス変換 (NAT) サービスです。NAT ゲートウェイを使用すると、プライベートサブネット内のインスタンスは VPC 外のサービスに接続できますが、外部サービスはそれらのインスタンスとの接続を開始できません。

NAT ゲートウェイ

NATゲートウェイを配置することによって、後述のプライベートサブネット内のインスタンス①が、インターネット向けのアウトバウンド通信を実行できるようになります。

ルートテーブルにNATゲートウェイ向きのルートを設定する

プライベートサブネット①周りのリソースを確認します。

Resources:
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref CidrIp2
      VpcId: !Ref VPC
      AvailabilityZone: !Sub ${AWS::Region}${AvailabilityZone1}

  PrivateRouteTable1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  RouteToNATGateway:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway

  PrivateSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable1
Code language: YAML (yaml)

プライベートサブネット①内のインスタンスは、NATゲートウェイを経由してdnfを実行します。
そのため同サブネット用のルートテーブルを用意し、NATゲートウェイ向けのデフォルトルートを定義します。

パターン2:VPCエンドポイントを経由して、S3バケット上のdnfリポジトリにアクセスする

VPCエンドポイント向きのルートテーブルを用意する

プライベートサブネット②周りのリソースを確認します。

Resources:
  PrivateRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PrivateSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable2
Code language: YAML (yaml)

プライベートサブネット②内のインスタンスは、S3エンドポイント経由でdnfを実行します。そのため同サブネット用のルートテーブルを用意します。
なお具体的なルートの定義は、エンドポイント定義時に指定します。

S3エンドポイントはAmazon Linux用のdnf/yumリポジトリへのアクセスを許可する

S3エンドポイント関係のリソースを確認します。

Resources:
  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - s3:GetObject
            Resource:
              - !Sub "arn:aws:s3:::al2023-repos-${AWS::Region}-de612dc2/*"
      RouteTableIds:
        - !Ref PrivateRouteTable2
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      VpcId: !Ref VPC
Code language: YAML (yaml)

通常、dnf/yumを実行するためには、インターネット上にあるdnf/yumリポジトリにアクセスする必要があります。ただ例外的に、EC2インスタンスのOSがAmazon Linux系の場合、S3用のVPCエンドポイントを設置することで、インターネットを経由することなく、同OS用のdnf/yumリポジトリにアクセスすることができます。

Amazon Linux リポジトリは、Amazon Simple Storage Service (Amazon S3) バケットでホストされます。インターネット接続なしでインスタンスにパッケージを更新したりインストールしたりするには、S3 Amazon Virtual Private Cloud (Amazon VPC) ゲートウェイエンドポイントを作成します。S3 VPC ゲートウェイエンドポイントに、リポジトリバケットへのアクセスを許可するポリシーを含めます。次に、VPC エンドポイントをインスタンスサブネットのルーティングテーブルに関連付けます。

Amazon Linux 1 または Amazon Linux 2 を実行している EC2 インスタンスで、インターネットにアクセスせずに yum を更新したり、パッケージをインストールしたりするにはどうすればよいですか?

今回の構成では、Amazon Linux  2023のEC2インスタンスを作成します。同インスタンスがS3上のリポジトリにアクセスするために必要なポリシーに関しては、以下の通り言及されています。

{
  "Statement": [
    {
      "Principal": "*",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::al2023-repos-us-east-1-de612dc2/*"
      ]
    }
  ]
}
Code language: JSON / JSON with Comments (json)

上記に従い、VPCエンドポイントのAction・Resourceプロパティを設定します。RouteTableIdsプロパティに、先述のプライベートサブネット②用のルートテーブルを指定します。

プライベートサブネット内のインスタンスには、パブリックアドレスは不要

EC2関係のリソースを確認します。

Resources:
  Instance1:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref PrivateSubnet1
          GroupSet:
            - !Ref InstanceSecurityGroup

  Instance2:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref PrivateSubnet2
          GroupSet:
            - !Ref InstanceSecurityGroup
Code language: YAML (yaml)

特別な設定は不要です。
2つのインスタンスを、2つのプライベートサブネットに1つずつ配置します。

ポイントはパブリックアドレスを設定する必要はないという点です。
インスタンス①が通信する場合、インターネットに抜ける際に、NATゲートウェイに付与したElastic IPアドレスを使用するため、パブリックアドレスは不要です。
インスタンス②の場合は、そもそもインターネットを経由しないため、パブリックアドレスが不要です。

SSM Session Manager経由でEC2インスタンスにアクセスする

EC2インスタンスへのアクセスは、キーペアを使用したSSHによるアクセスが一般的ですが、SSM Session Managerを使用することでもアクセスすることが可能です。
同サービスを使用してEC2インスタンスにアクセスするためには、同サービス向けのエンドポイントの設置、同エンドポイント用のセキュリティグループの作成、同サービスにアクセスする権限を含むIAMロールの作成とインスタンスへのアタッチが必要となります。

詳細は以下のページをご確認ください。

あわせて読みたい
LinuxインスタンスにSSM Session Manager経由でアクセスする 【LinuxインスタンスにSSM Session Manager経由でアクセスする】 EC2インスタンスにSSM Session Manager経由でアクセスする構成を確認します。 Session Manager は完全...

環境構築

CloudFormationを使用して本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
AWS CLIからCloudFormationを実行する手順については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • インスタンス1のID:i-02c9af257d624677e
  • インスタンス2のID:i-0635909198866aa1b
  • PrivateSubet1のID:subnet-091d74d3795b13935
  • PrivateSubet2のID:subnet-0a3135f67e2a4dffd
  • NATゲートウェイのID:nat-06dc9f13d6fbb74b7
  • S3用VPCエンドポイントのID:vpce-0ad8a5eb1d7adcfbe

AWS Managemet Consoleでもリソースの作成状況を確認します。
まずパターン1のルートテーブルを確認します。

To yum for the Internet, set the default route to NAT gateway.

NATゲートウェイ向けのデフォルトルートが定義されています。このルートによって、NATゲートウェイを通じてインターネット上のdnf/yumリポジトリにアクセスすることができます。

次にパターン2のルートテーブルを確認します。

Configure route for S3 endpoint to yum for s3 repository.

S3用VPCエンドポイント向けのルートが定義されています。このルートの送信先にはプレフィックスリストが設定されています。リストの中身は以下の通りです。

Route for S3 endpoint contains multiple entries.

10行分のCIDRがエントリーされています。つまり先ほどのルート1行を定義することによって、送信先が上記だった場合は、S3用VPCエンドポイントにルーティングされるということになります。

動作確認1:NATゲートウェイ経由のdnf

準備が整いましたので、実際に挙動を確認します。
まずNATゲートウェイ経由のdnfを確認します。

インスタンス①にSSM Session Manager経由でアクセスします。

<meta charset="utf-8">$ aws ssm start-session \
--target i-02c9af257d624677e
...
sh-4.2$
Code language: Bash (bash)

インターネットにアクセスできるかPingで確認します。

sh-4.2$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=104 time=2.88 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=104 time=2.43 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=104 time=2.47 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=104 time=2.48 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 2.432/2.568/2.888/0.185 ms
Code language: Bash (bash)

確かにNATゲートウェイ経由で、インターネットにアクセスすることができます。

それではdnfを実行します。

sh-4.2$ sudo dnf install httpd -y
...
Installed:
  httpd.x86_64 0:2.4.48-2.amzn2
...
Complete!Code language: Bash (bash)

正常に実行できました。
NATゲートウェイを経由して、インターネット上のdnfリポジトリにアクセスし、dnfが実行できることがわかりました。

動作確認2:VPCエンドポイント経由のdnf

続いてS3エンドポイント経由のdnfを確認します。

インスタンス②にSSM Session Manager経由でアクセスします。

$ aws ssm start-session \
--target i-0635909198866aa1b
...
sh-4.2
Code language: Bash (bash)

インターネットにアクセスできるかPingで確認します。

sh-4.2$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
^C
--- 8.8.8.8 ping statistics ---
28 packets transmitted, 0 received, 100% packet loss, time 27624ms
Code language: Bash (bash)

やはりできません。プライベートサブネット②には、インターネットに抜けられるルートがないためです。

それではdnfを実行します。

sh-4.2$ sudo dnf install httpd -y
...
Installed:
  httpd.x86_64 0:2.4.48-2.amzn2
...
Complete!
Code language: Bash (bash)

正常に実行できました。
VPCエンドポイントを経由して、S3バケット上のdnfリポジトリにアクセスし、dnfを実行できることがわかりました。

まとめ

プライベートサブネット上のEC2インスタンスで、dnf/yumを実行する方法を2パターン確認しました。
1つ目はNATゲートウェイを経由して、インターネット上のdnf/yumリポジトリにアクセスし、dnf/yumを実行する方法です。
2つ目はVPCエンドポイントを経由して、S3バケット上のdnf/yumリポジトリにアクセスし、dnf/yumを実行する方法です。
プライベートサブネットにインスタンスを配置することによって、インスタンスに不正にアクセスされるリスクを低減することができ、システム全体のセキュリティを高めることができます。今回ご紹介した方法は、その一助となるでしょう。