VPCにおいて、プライベートサブネットからインターネットにアクセスするには、NATが必要になります。AWSから「NATゲートウェイ」というサービスが提供されており、数クリックで簡単にNATをデプロイすることができます。
しかし、どうしてもNATゲートウェイを利用できないケースがあります。また、NATゲートウェイの料金が高く無料利用枠がないことも、初心者からすると敷居が高い理由の一つでしょう。
そんなときに、EC2のインスタンスを利用して、NAT機能を実装した「NATインスタンス」を利用するといいです。NATインスタンスとは、EC2インスタンス上にNATに必要なソフトをインストールすることで、EC2インスタンスをNATとして利用するものです。単なるEC2インスタンスなのでもちろん無料利用枠がありますし、ニーズに応じてインスタンスタイプを選択することで費用の節約が可能になります。また固定IP(Elastic IP)の必要性もないのでEIPが利用不可の環境でもNATが使えるようになります。
最新のAmazon Linux、AL2023を使ってNATインスタンスを構築する方法はクラスメソッドさんのブログにもありますので、ここで割愛しますが、CloudFormationでIaC化しました。このCloudFormationテンプレートを利用すると、一発でNATインスタンスを構築することができます。
文末に掲載しているこのCloudFormationテンプレートの仕様、注意点は以下です。
- デプロイ先のVPC・サブネット、許可するIPアドレス範囲、インスタンスタイプ(t2.microなど)をパラメータ化していますが、それ以外は直接テンプレートを編集することで変更できます
- キーペアを設定していないためSSHでインスタンスにログインすることはできません。SSHでログインする必要がある場合、キーペアの設定を追加してください
- AL2023にiptablesを搭載していないため、nftablesを利用しています
- AL2023のlatestでのみ動作確認済みです(2023年5月24日時点)
ちなみに、AL2023は頻繁にアップデート版のAMIを出しています。以下のAWS CLIコマンドでAL2023のx86フルバージョンのイメージ一覧を確認できます。
C:\> aws ec2 describe-images --owners amazon --filters "Name=name,Values=al2023-ami-20*" "Name=architecture,Values=x86_64" --query "sort_by(Images, &Name)[].{Name: Name, ImageId: ImageId}" --output table
-----------------------------------------------------------------------------
| DescribeImages |
+------------------------+--------------------------------------------------+
| ImageId | Name |
+------------------------+--------------------------------------------------+
| ami-067871d950411e643 | al2023-ami-2023.0.20230315.0-kernel-6.1-x86_64 |
| ami-02a2700d37baeef8b | al2023-ami-2023.0.20230322.0-kernel-6.1-x86_64 |
| ami-079a2a9ac6ed876fc | al2023-ami-2023.0.20230329.0-kernel-6.1-x86_64 |
| ami-01b32aa8589df6208 | al2023-ami-2023.0.20230419.0-kernel-6.1-x86_64 |
| ami-0e0820ad173f20fbb | al2023-ami-2023.0.20230503.0-kernel-6.1-x86_64 |
+------------------------+--------------------------------------------------+
CloudFormationテンプレートはこちらです。
AWSTemplateFormatVersion: "2010-09-09"
Description: CFn template for creating NAT instance
Parameters:
TargetVpcId:
Type: AWS::EC2::VPC::Id
AllowedIpCidr:
Type: String
TargetSubnet:
Type: AWS::EC2::Subnet::Id
LastestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
NatInstanceType:
Type: String
Default: t2.micro
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: Select the VPC and Subnet where you want to deploy the NAT instance
Parameters:
- TargetVpcId
- TargetSubnet
- AllowedIpCidr
-
Label:
default: Specify the EC2 parameter
Parameters:
- LastestAmiId
- NatInstanceType
ParameterLabels:
TargetVpcId:
default: "VPC ID (select from drop down list)"
TargetSubnet:
default: "Subnet ID to which you want to deploy (select from drop down list)"
AllowedIpCidr:
default: "Allowed IP CIDR (eg.: 192.168.0.0/24) from which the traffic goes in"
LastestAmiId:
default: "AMI for the EC2 instance"
NatInstanceType:
default: "Instance type"
Resources:
NatEC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref LastestAmiId
InstanceType: !Ref NatInstanceType
SourceDestCheck: false
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: gp3
VolumeSize: 8
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref TargetSubnet
AssociatePublicIpAddress: true
GroupSet:
- !Ref NatInstanceSecurityGroup
Tags:
- Key: Name
Value: nat-instance
UserData:
Fn::Base64: |
#!/bin/bash
sysctl -w net.ipv4.ip_forward=1 >> /etc/sysctl.conf
yum install -y nftables
nft add table nat
nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule nat postrouting oifname "$(ip -o link show device-number-0 | awk -F': ' '{print $2}')" masquerade
nft list table nat > /etc/nftables/al2023-nat.nft
echo 'include "/etc/nftables/al2023-nat.nft"' >> /etc/sysconfig/nftables.conf
systemctl start nftables
systemctl enable nftables
NatInstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: natinstance-sg
GroupDescription: Allow all traffic only from the specified CIDR
VpcId: !Ref TargetVpcId
SecurityGroupIngress:
- IpProtocol: -1
CidrIp: !Ref AllowedIpCidr
Tags:
- Key: Name
Value: natinstance-sg