はじめに
_s__o_ です。
AWS で検証をする際、お客様オンプレ環境が必要になる場合があります (Site-to-Site VPN 経由での接続など)。
とは言えど、おいそれと簡単に用意できるものではないので、代替として「オンプレ環境を模した」 AWS の NW を構築します。手動でポチポチと作成してもいいのですが、あくまで検証用途 (=使い終わったらすぐに消す) の想定のため、環境の改廃がしやすい CloudFormation を使いたいと思います。
検証 NW の構築
構成イメージ
赤太字の箇所が、CloudFormation で作成する部分です。
CloudFormation のコード (YAML)
細かい中身は [前提事項] をご確認ください。
AWSTemplateFormatVersion: 2010-09-09
# ------------------------------------------------------------#
# [前提事項]
# ・疑似オンプレ環境 (以下、疑似オンプレ) を想定した VPC。
# ・Site-to-Site VPN を用いて AWS (以下、接続先 AWS) に接続する想定。
# ・ALB にアクセスさせる (=名前解決必要) ため、AWS の Route53 Resolver を参照させる想定。
# ・このテンプレートで作成するオブジェクトは下記の通り。無料、且つ、作りきり (変更の可能性が低い) のものを対象としている (故に、すべて NW 系のオブジェクト)
# - DHCP オプションセット x 1
# - VCP x 1
# - サブネット x 2
# - ルートテーブル x 2
# - インターネット GW x 1
# - ENI x 2
#
# ・このテンプレートで対象外のオブジェクト (=自分で作成する必要がある) は下記の通り。
# - 疑似オンプレ側
# -- セキュリティグループ
# -- VPN ルータ@パブリック NW ※VyOS など
# -- EC2 サーバ@プライベート NW
# - 接続先 AWS 側
# -- CGW 設定
# -- VPN 設定
# -- VGW or TGW 設定
# -- Route53 設定
# -- Route53 Resolver 設定
#
# [留意事項]
# ・VPC の名前解決を Off にすると、SSH の接続に時間がかかる。下記 URL を参考にして、
# sshd_config の修正を推奨する (「UseDNS=no」、「GSSAPIAuthentication no」)
# https://yuyarin.hatenadiary.org/entry/20090410/1239298235
# ・VyOS の場合は「set service ssh disable-host-validation」で DNS 名前解決を無効化できる。
# ------------------------------------------------------------#
# ------------------------------------------------------------#
# Parameter
# ------------------------------------------------------------#
Parameters:
# 疑似オンプレの VPC 名。
VpcNAME:
Type: String
Description: VPC name.
# 疑似オンプレの CIDR (例 : 172.31.0.0/16)
VpcCIDR:
Type: String
Description: VPC CIDR address.
# 疑似オンプレのパブリック NW (例 : 172.31.1.0/24)
PublicNwCIDR:
Type: String
Description: Public NW CIDR address.
# 疑似オンプレのプライベート NW (例 : 172.31.101.0/24)
PrivateNwCIDR:
Type: String
Description: Private NW CIDR address.
# 疑似オンプレの VPN ルータ@パブリック NW の IP アドレス (例 : 172.31.1.254)
PublicVpnrt1IP:
Type: String
Description: Public VPN Router01 IP address (Private IP address).
# 疑似オンプレの サーバ@プライベート NW の IP アドレス (例 : 172.31.101.51)
PrivateSv1IP:
Type: String
Description: Private Server01 IP address.
# 接続先 AWS の Route53 Resolver の IP。DHCP オプションセットで使用する。複数の場合はコンマ区切りで記入する (例 : 192.168.100.100, 192.168.101.100)
Route53ResolverIP:
Type: String
Description: Route53Resolver IP address.
# 接続先 AWS の CIDR。ルートテーブルで使用する。
RemoteSiteNwCIDR:
Type: String
Description: Remote NW CIDR address.
# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
# 1. AWS::EC2::DHCPOptions
# ------------------------------------------------------------#
# オンプレ想定の VPC のため、AWS の DNS (Route53 Resolver) を
# サーバの DNS として指定する。
DHCP:
Type: AWS::EC2::DHCPOptions
Properties:
DomainNameServers:
- !Ref Route53ResolverIP
Tags:
- Key: Name
Value: !Sub "dhcp-${VpcNAME}"
# ------------------------------------------------------------#
# 2. AWS::EC2::VPC
# ------------------------------------------------------------#
# オンプレ想定の VPC のため、EnableDnsSupport (Amazon DNS の利用)、
# EnableDnsHostnames (Amazon DNS 名の付与) は false とする。
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: "false"
EnableDnsHostnames: "false"
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${VpcNAME}"
# ------------------------------------------------------------#
# 3. AWS::EC2::VPCDHCPOptionsAssociation
# ------------------------------------------------------------#
VpcDhcpAssociation:
Type: AWS::EC2::VPCDHCPOptionsAssociation
DependsOn:
- VPC
- DHCP
Properties:
VpcId: !Ref VPC
DhcpOptionsId: !Ref DHCP
# ------------------------------------------------------------#
# 4. AWS::EC2::Subnet
# ------------------------------------------------------------#
NwPublic1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PublicNwCIDR
MapPublicIpOnLaunch: 'true'
AvailabilityZone: "ap-northeast-1a"
Tags:
- Key: Name
Value: !Sub "nw-${VpcNAME}-public1"
NwPrivate1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PrivateNwCIDR
MapPublicIpOnLaunch: 'false'
AvailabilityZone: "ap-northeast-1c"
Tags:
- Key: Name
Value: !Sub "nw-${VpcNAME}-private1"
# ------------------------------------------------------------#
# 5. AWS::EC2::RouteTable
# ------------------------------------------------------------#
RtPublic:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "rt-${VpcNAME}-public"
RtPrivate:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "rt-${VpcNAME}-private"
# ------------------------------------------------------------#
# 6. AWS::EC2::InternetGateway
# ------------------------------------------------------------#
IGW:
Type: 'AWS::EC2::InternetGateway'
Properties:
Tags:
- Key: Name
Value: !Sub "igw-${VpcNAME}"
# ------------------------------------------------------------#
# 7. AWS::EC2::VPCGatewayAttachment
# ------------------------------------------------------------#
IgwAttachGateway:
Type: 'AWS::EC2::VPCGatewayAttachment'
DependsOn:
- VPC
- IGW
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref IGW
# ------------------------------------------------------------#
# 8. AWS::EC2::SubnetRouteTableAssociation
# ------------------------------------------------------------#
RouteTableAssociationPublic:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DependsOn:
- NwPublic1
- RtPublic
Properties:
SubnetId: !Ref NwPublic1
RouteTableId: !Ref RtPublic
RouteTableAssociationPrivate:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DependsOn:
- NwPrivate1
- RtPrivate
Properties:
SubnetId: !Ref NwPrivate1
RouteTableId: !Ref RtPrivate
# ------------------------------------------------------------#
# 9. AWS::EC2::SecurityGroup
# ------------------------------------------------------------#
# 変更が頻繁に発生して管理が面倒なので、セキュリティグループは作成しない。
# SgPublicVpnrt:
# Type: 'AWS::EC2::SecurityGroup'
# DependsOn:
# - VPC
# Properties:
# GroupName: !Sub "sgr-${VpcNAME}-public-vpnrt"
# GroupDescription: 'For public VPN router.'
# VpcId: !Ref VPC
# Tags:
# - Key: Name
# Value: !Sub "sgr-${VpcNAME}-public-vpnrt"
# SgPrivateSV:
# Type: 'AWS::EC2::SecurityGroup'
# DependsOn:
# - VPC
# Properties:
# GroupName: !Sub "sgr-${VpcNAME}-private-sv"
# GroupDescription: 'For private EC2 server.'
# VpcId: !Ref VPC
# Tags:
# - Key: Name
# Value: !Sub "sgr-${VpcNAME}-private-sv"
# ------------------------------------------------------------#
# 10. AWS::EC2::NetworkInterface
# ------------------------------------------------------------#
EniPublicVpnrt1:
Type: AWS::EC2::NetworkInterface
DependsOn:
- NwPublic1
Properties:
SourceDestCheck: 'false'
SubnetId: !Ref NwPublic1
PrivateIpAddress: !Ref PublicVpnrt1IP
Tags:
- Key: 'Name'
Value: !Sub "eni-${VpcNAME}-public-vpnrt1"
EniPrivateSv1:
Type: AWS::EC2::NetworkInterface
DependsOn:
- NwPrivate1
Properties:
SourceDestCheck: 'true'
SubnetId: !Ref NwPrivate1
PrivateIpAddress: !Ref PrivateSv1IP
Tags:
- Key: 'Name'
Value: !Sub "eni-${VpcNAME}-private-sv1"
# ------------------------------------------------------------#
# 11. AWS::EC2::Route
# ------------------------------------------------------------#
# Default : 0.0.0.0/0
RtPublicDefault:
Type: 'AWS::EC2::Route'
DependsOn:
- RtPublic
- IGW
Properties:
RouteTableId: !Ref RtPublic
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref IGW
# Remote Site
RtPublicRemoteSite:
Type: 'AWS::EC2::Route'
DependsOn:
- RtPublic
- EniPublicVpnrt1
Properties:
RouteTableId: !Ref RtPublic
DestinationCidrBlock: !Ref RemoteSiteNwCIDR
NetworkInterfaceId: !Ref EniPublicVpnrt1
RtPrivateRemoteSite:
Type: 'AWS::EC2::Route'
DependsOn:
- RtPrivate
- EniPublicVpnrt1
Properties:
RouteTableId: !Ref RtPrivate
DestinationCidrBlock: !Ref RemoteSiteNwCIDR
NetworkInterfaceId: !Ref EniPublicVpnrt1
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
VpcID:
Value: !Ref VPC
Export:
Name: !Sub "${VpcNAME}-VPC"
NwPublic1ID:
Value: !Ref NwPublic1
Export:
Name: !Sub "${VpcNAME}-NW-Public1"
NwPrivate1ID:
Value: !Ref NwPrivate1
Export:
Name: !Sub "${VpcNAME}-NW-Private1"
# SgPublicVpnrt:
# Value: !Ref SgPublicVpnrt
# Export:
# Name: !Sub "${VpcNAME}-SG-Public-VPNRT"
# SgPrivateSv:
# Value: !Ref SgPrivateSV
# Export:
# Name: !Sub "${VpcNAME}-SG-Private-SV"
EniPublicVpnrt1:
Value: !Ref EniPublicVpnrt1
Export:
Name: !Sub "${VpcNAME}-ENI-Public-VPNRT1"
EniPrivateSv1:
Value: !Ref EniPrivateSv1
Export:
Name: !Sub "${VpcNAME}-ENI-Private-SV1"
ポイント解説
DHCP オプションセット
疑似オンプレ環境から、接続先 AWS の Route53 Resolver を参照させるため、DHCP オプションセットを新規作成しています。デフォルトの DHCP オプションセットは Amazon DNS を参照するようになっているので、参照先を Route53 Resolver に変更しています。
VPC
オンプレ想定のため、Amazon DNS を参照しないように「DNS による名前解決」と「ホスト名の使用」を false にしています。
Security Group
最初はセキュリティグループも CloudFormation で定義していましたが、ちょこちょこ内容に変更が入って、そのたびにコードを更新するのが面倒だったので、CloudFormation の対象から外しました。Security Group のように中身の変更頻度が多いオブジェクトは、CloudFormation の管理対象にしない方がいいかもしれないです。
Outputs
今後、クロススタックの利用も想定し、作成したオブジェクトを Export しています。
その他の注意事項
コードの [前提事項] に記載していますが、VPC で「DNS による名前解決」を Off にすると、Linux などに SSH ログインする際に時間がかかります。そのため、サーバを構築する際は、sshd_config の修正を推奨します (「UseDNS=no」、「GSSAPIAuthentication no」)。参考
おまけ
今回、VPN ルータとして VyOS を使用しました。どうやら、VyOS でルーティングが「静的」の場合、ルータ投入用の config がダウンロードできないみたいです (というか、そもそもダウンロードの項目に存在しない)。
config の修正に関しては、こちら のサイトが参考になるので、あわせてご参照ください。
まとめ
以上、オンプレ環境を模した検証 NW の構築でした。
いつも必要ではないですが、時々ふと必要になったりするので、こういったものは CloudFormation でテンプレート化しておくと色々捗ります。