はじめに
2024年2月より、AWSのパブリックIPアドレスが有料化(課金対象)になるとの情報が下記のブログにてAWS社より発表されています。
これまで筆者の個人環境では、パブリック・サブネット上にEC2インスタンスをプロビジョニングして、外部からSSH接続してEC2インスタンスにアクセスしていましたが、今回の発表に伴いEC2インスタンスへのアクセス手段を見直すことにしました。現在は、EC2 Instance ConnectがプライベートIPアドレスを使用した接続ができるようにアップデートされているようですが、PC側のターミナルからプライベートIPアドレスでインスタンスに接続できればもっと便利かなと思い、色々模索していたところ、下記のAWS Client VPNを使用すればそれが可能になるのではと考え、実際にClient VPN環境を構築してみました。
本記事では、その構築例をご紹介します。
AWS Client VPN環境構築
今回作成する環境
先日投稿した下記のQiita記事の中で、一度環境構築して稼働確認自体はできたのですが、今回は以下の点を考慮して構成の見直しを行いました。
- パッチ適用やツール等の稼働検証ができるように、プライベート・インスタンスのOutbound通信ができるようにする。
- パブリック・サブネットを用意し、その中にNAT Gatewayを追加。
- Internet Gatewayを追加し、VPCにアタッチ。
なお、Client VPN接続した状態のプライベート・インスタンスにおいて、他のAWSサービスも使用できるか否かを確認するため、今回は1例として久々にEFSを使ってみることにしました。(re:Invent2023において、EFSにも新機能やアップデートがあり、使ってみたくなりました。)
また、接続元のClient PCには、Redhat Enterprise Linux V8.8を使用しています。
認証方式の選定
AWS Client VPNを使用する場合、認証方法にはActive Directory認証、証明書ベースの相互認証、SSO(SAMLベースのフェデレーション認証)の3種類があります。筆者は個人ユーザーですので、証明書ベースの相互認証方式を使用することにしました。
証明書作成
接続元のPCでクライアント証明書とサーバー証明書を作成して、AWS Certification Managerにアップロードする必要があります。証明書の作成方法は、下記AWS Client VPNのマニュアル記載を参考に実施しました。特に躓くことなく作成及びアップロードはできました。
- OpenVPN easy-rsaをダウンロード
- PKI(公開鍵暗号基盤)の作成
$ ./easyrsa init-pki
Notice
------
'init-pki' complete; you may now create a CA or requests.
Your newly created PKI dir is:
* /home/testuser/data/easy-rsa/easyrsa3/pki
* IMPORTANT: Easy-RSA 'vars' file has now been moved to your PKI above.
- CA(認証局)の作成
$ ./easyrsa build-ca nopass
* Using Easy-RSA configuration from: /home/kiyomasa/data/easy-rsa/easyrsa3/pki/vars
* Using SSL: openssl OpenSSL 1.1.1p 21 Jun 2022
EasyRSA DN 'commonName-Only' mode (cn_only)
* Current CA Distinguished Name fields:
commonName = Easy-RSA CA
Type the word 'yes' to continue, or any other input to abort.
Create CA certificate with these DN settings ? yes
...........................................+++++
.....+++++
Notice
------
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/testuser/data/easy-rsa/easyrsa3/pki/ca.crt
- サーバー証明書の作成
$ ./easyrsa build-server-full server nopass
* Using Easy-RSA configuration from: /home/kiyomasa/data/easy-rsa/easyrsa3/pki/vars
* Using SSL: openssl OpenSSL 1.1.1p 21 Jun 2022
Generating a RSA private key
............................+++++
........................+++++
writing new private key to '/home/testuser/data/easy-rsa/easyrsa3/pki/64e332b6/temp.0e912600'
-----
Notice
------
Keypair and certificate request completed. Your files are:
req: /home/testuser/data/easy-rsa/easyrsa3/pki/reqs/server.req
key: /home/testuser/data/easy-rsa/easyrsa3/pki/private/server.key
Using configuration from /home/testuser/data/easy-rsa/easyrsa3/pki/safessl-easyrsa.cnf.init-tmp
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'server'
Certificate is to be certified until Mar 21 07:46:20 2026 GMT (825 days)
Write out database with 1 new entries
Data Base Updated
Notice
------
Certificate created at: /home/testuser/data/easy-rsa/easyrsa3/pki/issued/server.crt
- クライアント証明書の作成
$ ./easyrsa build-client-full testuser.RHEL.tld nopass
* Using Easy-RSA configuration from: /home/testuser/data/easy-rsa/easyrsa3/pki/vars
* Using SSL: openssl OpenSSL 1.1.1p 21 Jun 2022
Generating a RSA private key
.................+++++
...............................................................................................................................................+++++
writing new private key to '/home/testuser/data/easy-rsa/easyrsa3/pki/a7087a3c/temp.746a7203'
-----
Notice
------
Keypair and certificate request completed. Your files are:
req: /home/testuser/data/easy-rsa/easyrsa3/pki/reqs/testuser.RHEL.tld.req
key: /home/testuser/data/easy-rsa/easyrsa3/pki/private/testuser.RHEL.tld.key
Using configuration from /home/testuser/data/easy-rsa/easyrsa3/pki/safessl-easyrsa.cnf.init-tmp
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'testuser.RHEL.tld'
Certificate is to be certified until Mar 21 07:49:58 2026 GMT (825 days)
Write out database with 1 new entries
Data Base Updated
Notice
------
Certificate created at: /home/testuser/data/easy-rsa/easyrsa3/pki/issued/testuser.RHEL.tld.crt
- 作成した証明書類を、/home/testuser/custom_crtkeyにコピー
- AWS Certification Managerへのサーバー証明書アップロード
$ aws acm import-certificate --certificate fileb://server.crt --private-key fileb://server.key --certificate-chain fileb://ca.crt
{
"CertificateArn": "arn:aws:acm:ap-northeast-1:xxxxxxxxxxx:certificate/9ef809ca-5db0-45aa-b600-20f88a9a8f82"
}
- AWS Certification Managerへのクライアント証明書アップロード
$ aws acm import-certificate --certificate fileb://testuser.RHEL.tld.
crt --private-key fileb://testuser.RHEL.tld.key --certificate-chain fileb://ca.crt
{
"CertificateArn": "arn:aws:acm:ap-northeast-1:xxxxxxxxxxxx:certificate/983954a7-52d2-4ef9-a342-5b7e8c8ef951"
}
アップロードされた証明書が、AWS Certification Managerの証明書一覧に表示されていることが確認できます。
CloudFormationサンプルコード
今回見直し後のAWS Client VPN環境をプロビジョニングするためのCloudFormationのサンプルコードは下記のcreate_ClientVPN.yamlになります。
create_ClientVPN.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Client-VPN Environment
Resources:
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
Tags:
- Key: Name
Value: My-VPC
MyPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: My-Public-Subnet
DependsOn: MyVPC
MyPrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: My-Private-Subnet
DependsOn: MyVPC
MyIGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: My-Internet-Gateway
DependsOn: MyVPC
MyIGWAttachToVPC:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref MyVPC
InternetGatewayId: !Ref MyIGW
MyEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
DependsOn: MyIGWAttachToVPC
MyNGW:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt MyEIP.AllocationId
ConnectivityType: public
SubnetId: !Ref MyPublicSubnet
Tags:
- Key: Name
Value: My-NAT-Gateway
DependsOn: MyIGWAttachToVPC
MyPublicRtbl:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: My-Public-Rtbl
DependsOn: MyIGW
MyPublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: MyPublicRtbl
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref MyIGW
MyPublicSubnetRouteTblAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref MyPublicRtbl
SubnetId: !Ref MyPublicSubnet
MyPrivateRtbl:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: My-Private-Rtbl
DependsOn: MyVPC
MyPrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: MyPrivateRtbl
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref MyNGW
DependsOn: MyNGW
DependsOn: MyIGWAttachToVPC
MyPrivateSubnetRouteTblAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref MyPrivateRtbl
SubnetId: !Ref MyPrivateSubnet
MyClientVPNSecGrp:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security Group for Client VPN Endpoint
VpcId: !Ref MyVPC
Tags:
- Key: Name
Value: My-ClientVPN-Security-Group
MyPrivateSecGrp:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security Group for VPC Private Subnet
VpcId: !Ref MyVPC
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref MyClientVPNSecGrp
IpProtocol: tcp
FromPort: 22
ToPort: 22
- SourceSecurityGroupId: !Ref MyClientVPNSecGrp
IpProtocol: icmp
FromPort: 8
ToPort: -1
Tags:
- Key: Name
Value: My-PrivateSubnet-Security-Group
MyKey:
Type: AWS::EC2::KeyPair
Properties:
KeyName: MyEC2Key
PublicKeyMaterial: '{{resolve:ssm:publickeymaterial}}'
Tags:
- Key: Name
Value: My-Key
MyPrivateInstance:
Type: AWS::EC2::Instance
Properties:
CreditSpecification:
CPUCredits: standard
InstanceType: t2.micro
KeyName: !Ref MyKey
ImageId: ami-078296f82eb463377
NetworkInterfaces:
- AssociatePublicIpAddress: "false"
DeviceIndex: "0"
SubnetId: !Ref MyPrivateSubnet
GroupSet:
- !Ref MyPrivateSecGrp
Tags:
- Key: Name
Value: My-Private-Instance
MyClientVPNEndpoint:
Type: AWS::EC2::ClientVpnEndpoint
Properties:
AuthenticationOptions:
- Type: certificate-authentication
MutualAuthentication:
ClientRootCertificateChainArn: '{{resolve:ssm:RootCertARN}}'
ClientCidrBlock: 192.168.0.0/22
ConnectionLogOptions:
Enabled: false
ServerCertificateArn: '{{resolve:ssm:ServerCertARN}}'
VpcId: !Ref MyVPC
VpnPort: 443
SecurityGroupIds:
- !Ref MyClientVPNSecGrp
TransportProtocol: udp
TagSpecifications:
- ResourceType: client-vpn-endpoint
Tags:
- Key: Name
Value: My-Client-VPN-Endpoint
MyClientVPNTargetNetworkAssociation:
Type: AWS::EC2::ClientVpnTargetNetworkAssociation
Properties:
ClientVpnEndpointId: !Ref MyClientVPNEndpoint
SubnetId: !Ref MyPrivateSubnet
MyClientVPNAuthorizationRule:
Type: AWS::EC2::ClientVpnAuthorizationRule
Properties:
AuthorizeAllGroups: true
ClientVpnEndpointId: !Ref MyClientVPNEndpoint
TargetNetworkCidr: 10.0.2.0/24
Outputs:
MyPrivateInstanceIPaddr:
Description: My Private Instance's Private IP address
Value: !GetAtt MyPrivateInstance.PrivateIp
MyPrivateSubnetSecurityGroupID:
Description: My Private Subnet Security Group's ID
Value: !Ref MyPrivateSecGrp
Export:
Name: !Sub "${AWS::StackName}--PrivateSubnetSecGrpId"
MyPrivateSubnetID:
Description: My Private Subnet's ID
Value: !Ref MyPrivateSubnet
Export:
Name: !Sub "${AWS::StackName}--PrivateSubnetId"
MyVPCID:
Description: My VPC's ID
Value: !Ref MyVPC
Export:
Name: !Sub "${AWS::StackName}--VPCId"
改良コードのポイントとしては下記のとおりです。
- パブリック・サブネットを追加し、そこにNAT Gatewayを配置。
- NAT Gatewayを使用するため、EIPを用意。
- Internet Gatewayを作成し、VPCにアタッチ。
- 後続で紹介するEFS作成用のCloudFormationコードで、VPC、プライベート・サブネット、プライベート・サブネットのセキュリティーグループのIDが必要になるため、それらの値をエクスポート
Client VPN固有の設定ポイントとしては下記のとおりです。
-
MyClientVPNSecGrp
- Client VPN Endpointに紐づける専用のセキュリティーグループを用意する。
- インバウンド・アクセスのルールは不要です。
-
MyPrivateSecGrp
- プライベート・サブネットに紐づくセキュリティーグループのインバウンド・アクセスのルールに下記の指定を行います。
- Client VPN EndpointのセキュリティーグループからのSSHアクセス(Port=22)を許可する。
- Client VPN Endpointのセキュリティーグループからのping発行を許可する。
- プライベート・サブネットに紐づくセキュリティーグループのインバウンド・アクセスのルールに下記の指定を行います。
-
MyClientVPNEndpoint
- Client VPN Endpoint作成時に、既にCertification Managerにアップロード済のクライアント証明書とサーバー証明書各々のARNを指定する。
- 直接指定するのは煩雑ですので、Systems Manager Parameter Storeに各々のARNを保管する専用のパラメータを作成し、コードの実行時に呼び出すようにしています。
- Client VPNの送信元のIPアドレス範囲(自身のPCのIPアドレス範囲)を指定する。
- Client VPN Endpoint作成時に、既にCertification Managerにアップロード済のクライアント証明書とサーバー証明書各々のARNを指定する。
-
MyClientVPNTargetAssociation
- Client VPNの宛先となるサブネットを指定します。
-
MyClientVPNAuthorizationRule
- Client VPNの宛先となるサブネットのIPアドレスのCIDR範囲を指定します。
構築後のAWS Client VPN画面イメージ
-
VPC → 仮想プライベートネットワーク(VPN)の下に「クライアントVPNエンドポイント」というものがありますので、そこから確認することができます。
「クライアントCIDR」列のところに、接続元のPCのIPアドレス範囲が表示されています。 -
「クライアントVPNエンドポイントID」直下の青字(ID部分)をクリックすると詳細が表示されます。
当画面の赤枠で囲んでいる「クライアント設定をダウンロード」ボタンをクリックして、VPN接続に必要な設定ファイルをPCにダウンロードします。ファイル名は「downloaded-client-config.ovpn」という名前でダウンロードされますが、ダウンロード後はファイル名をリネームして問題ありません。ただし、OpenVPN準拠の設定ファイルですので、拡張子のovpnは変更しないでください。
AWS Client VPN設定ファイル
ダウンロードした中身は下記のようになっています。
client
dev tun
proto udp
remote cvpn-endpoint-0cad7eb018ce257d1.prod.clientvpn.ap-northeast-1.amazonaws.com 443
remote-random-hostname
resolv-retry infinite
nobind
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
-----BEGIN CERTIFICATE-----
~
~
-----END CERTIFICATE-----
</ca>
reneg-sec 0
verify-x509-name server name
下記マニュアル記述に従い、PC側のクライアント証明書とクライアント秘密鍵を絶対パス形式で、上記ファイルの「reneg-sec 0」の真上に記載します。
追記するのは以下の2行です。
cert /home/testuser/custom_crtkey/testuser.RHEL.tld.crt
key /home/testuser/custom_crtkey/testuser.RHEL.tld.key
AWS Client VPN接続確認
接続開始
接続元のクライアントPCのターミナルから下記のコマンドを実行します。
sudo openvpn --config client_config.ovpn
コマンド実行後、VPN接続が開始されます。下記は出力例です。
Tue Dec 19 16:50:03 2023 OpenVPN 2.4.12 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Nov 10 2023
Tue Dec 19 16:50:03 2023 library versions: OpenSSL 1.1.1k FIPS 25 Mar 2021, LZO 2.08
Tue Dec 19 16:50:03 2023 TCP/UDP: Preserving recently used remote address: [AF_INET]52.193.69.78:443
Tue Dec 19 16:50:03 2023 Socket Buffers: R=[212992->212992] S=[212992->212992]
Tue Dec 19 16:50:03 2023 UDP link local: (not bound)
Tue Dec 19 16:50:03 2023 UDP link remote: [AF_INET]52.193.69.78:443
Tue Dec 19 16:50:03 2023 TLS: Initial packet from [AF_INET]52.193.69.78:443, sid=903f2c53 ebafd4b6
Tue Dec 19 16:50:03 2023 VERIFY OK: depth=1, CN=Easy-RSA CA
Tue Dec 19 16:50:03 2023 VERIFY KU OK
Tue Dec 19 16:50:03 2023 Validating certificate extended key usage
Tue Dec 19 16:50:03 2023 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
Tue Dec 19 16:50:03 2023 VERIFY EKU OK
Tue Dec 19 16:50:03 2023 VERIFY X509NAME OK: CN=server
Tue Dec 19 16:50:03 2023 VERIFY OK: depth=0, CN=server
Tue Dec 19 16:50:03 2023 Control Channel: TLSv1.2, cipher TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 2048 bit RSA
Tue Dec 19 16:50:03 2023 [server] Peer Connection Initiated with [AF_INET]52.193.69.78:443
Tue Dec 19 16:50:05 2023 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Tue Dec 19 16:50:05 2023 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1 bypass-dhcp,block-outside-dns,dhcp-option DOMAIN-ROUTE .,route-gateway 192.168.0.161,topology subnet,ping 1,ping-restart 20,echo,ifconfig 192.168.0.162 255.255.255.224,peer-id 0,cipher AES-256-GCM'
Tue Dec 19 16:50:05 2023 Options error: Unrecognized option or missing or extra parameter(s) in [PUSH-OPTIONS]:2: block-outside-dns (2.4.12)
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: timers and/or timeouts modified
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: --ifconfig/up options modified
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: route options modified
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: route-related options modified
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: peer-id set
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: adjusting link_mtu to 1624
Tue Dec 19 16:50:05 2023 OPTIONS IMPORT: data channel crypto options modified
Tue Dec 19 16:50:05 2023 Outgoing Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
Tue Dec 19 16:50:05 2023 Incoming Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
Tue Dec 19 16:50:05 2023 ROUTE_GATEWAY 192.168.20.2/255.255.255.0 IFACE=bridge0 HWADDR=00:0c:29:27:53:90
Tue Dec 19 16:50:05 2023 TUN/TAP device tun0 opened
Tue Dec 19 16:50:05 2023 TUN/TAP TX queue length set to 100
Tue Dec 19 16:50:05 2023 /sbin/ip link set dev tun0 up mtu 1500
Tue Dec 19 16:50:05 2023 /sbin/ip addr add dev tun0 192.168.0.162/27 broadcast 192.168.0.191
Tue Dec 19 16:50:05 2023 /sbin/ip route add 52.193.69.78/32 via 192.168.20.2
Tue Dec 19 16:50:05 2023 /sbin/ip route add 0.0.0.0/1 via 192.168.0.161
Tue Dec 19 16:50:05 2023 /sbin/ip route add 128.0.0.0/1 via 192.168.0.161
Tue Dec 19 16:50:05 2023 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
Tue Dec 19 16:50:05 2023 Initialization Sequence Completed
上記出力の内、ポイントは下記の行になります。
Tue Dec 19 16:50:05 2023 TUN/TAP device tun0 opened
tun0というネットワーク・デバイスがオープンされます。接続中はこのデバイスが使用されます。
Tue Dec 19 16:50:05 2023 /sbin/ip addr add dev tun0 192.168.0.162/27 broadcast 192.168.0.191
VPN接続用に「192.168.0.162/27」というCIDR範囲のアドレスがアサインされます。この範囲でのIPアドレスが使用されていることが、AWSコンソールのクライアントVPNエンドポイントの「接続」タブから確認できます。
また、VPN接続に伴い「イングレスバイト数」「エグレスバイト数」「イングレスパケット」「エグレスパケット」数もカウントアップしています。
プライベートEC2インスタンスへの接続確認
CloudFormationの下記「出力」タブに記載されているプライベートEC2インスタンスのプライベートIPアドレスは、10.0.2.10となっていることがわかります。
PCのターミナルから、このアドレスに対してまずはPINGコマンドを発行してみます。
$ ping 10.0.2.10
64 bytes from 10.0.2.10: icmp_seq=1 ttl=254 time=7.69 ms
64 bytes from 10.0.2.10: icmp_seq=2 ttl=254 time=9.17 ms
64 bytes from 10.0.2.10: icmp_seq=3 ttl=254 time=10.2 ms
:
:
問題なく応答が返ってきます。続いて、SSH接続を試みます。
$ ssh -i ~/.ssh/MyEC2Key.pem ec2-user@10.0.2.10
The authenticity of host '10.0.2.10 (10.0.2.10)' can't be established.
ECDSA key fingerprint is SHA256:kIihAMZQYWdMuBfk1I74IjM6JGqIWaVrF09KSkrmhR4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.0.2.10' (ECDSA) to the list of known hosts.
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-0-2-10 ~]$
無事にプライベートIPアドレスを使って、プライベートEC2インスタンスにSSH接続することができました!
プライベートEC2インスタンスからのOutbound通信確認
後続のEFS関連で必要になるマウント・ヘルパーのユーティリティーをダウンロードします。
[ec2-user@ip-10-0-2-10 ~]$ sudo yum install -y amazon-efs-utils
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
amzn2-core | 3.6 kB 00:00:00
amzn2extra-docker | 2.9 kB 00:00:00
amzn2extra-kernel-5.10 | 3.0 kB 00:00:00
(1/7): amzn2-core/2/x86_64/group_gz | 2.7 kB 00:00:00
(2/7): amzn2-core/2/x86_64/updateinfo | 760 kB 00:00:00
(3/7): amzn2extra-docker/2/x86_64/updateinfo | 13 kB 00:00:00
(4/7): amzn2extra-docker/2/x86_64/primary_db | 105 kB 00:00:00
(5/7): amzn2extra-kernel-5.10/2/x86_64/updateinfo | 42 kB 00:00:00
(6/7): amzn2extra-kernel-5.10/2/x86_64/primary_db | 21 MB 00:00:00
(7/7): amzn2-core/2/x86_64/primary_db | 69 MB 00:00:01
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ amazon-efs-utils.noarch 0:1.35.0-1.amzn2 を インストール
--> 依存性の処理をしています: stunnel5 のパッケージ: amazon-efs-utils-1.35.0-1.amzn2.noarch
--> トランザクションの確認を実行しています。
---> パッケージ stunnel5.x86_64 0:5.58-1.amzn2.0.1 を インストール
--> 依存性解決を終了しました。
依存性を解決しました
=======================================================================================================
Package アーキテクチャー バージョン リポジトリー 容量
=======================================================================================================
インストール中:
amazon-efs-utils noarch 1.35.0-1.amzn2 amzn2-core 57 k
依存性関連でのインストールをします:
stunnel5 x86_64 5.58-1.amzn2.0.1 amzn2-core 165 k
トランザクションの要約
=======================================================================================================
インストール 1 パッケージ (+1 個の依存関係のパッケージ)
総ダウンロード容量: 221 k
インストール容量: 527 k
Downloading packages:
(1/2): amazon-efs-utils-1.35.0-1.amzn2.noarch.rpm | 57 kB 00:00:00
(2/2): stunnel5-5.58-1.amzn2.0.1.x86_64.rpm | 165 kB 00:00:00
-------------------------------------------------------------------------------------------------------
合計 1.5 MB/s | 221 kB 00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
インストール中 : stunnel5-5.58-1.amzn2.0.1.x86_64 1/2
インストール中 : amazon-efs-utils-1.35.0-1.amzn2.noarch 2/2
検証中 : amazon-efs-utils-1.35.0-1.amzn2.noarch 1/2
検証中 : stunnel5-5.58-1.amzn2.0.1.x86_64 2/2
インストール:
amazon-efs-utils.noarch 0:1.35.0-1.amzn2
依存性関連をインストールしました:
stunnel5.x86_64 0:5.58-1.amzn2.0.1
完了しました!
[ec2-user@ip-10-0-2-10 ~]$
無事に外部へのOutbound通信が実行できて、必要なパッケージをダウンロードすることができました!
他サービスへの接続確認
Client VPN接続することで、外部からSSHでプライベートEC2インスタンスにアクセスすることが可能になりました。ここではVPN接続状態のプライベートEC2インスタンスにおいて、他サービスへの接続もできるかを確認してみることにします。今回は構成図にも記載しましたが、EFSを使ってみます。
EFS作成
EFS作成用サンプルコードは、下記のcreate_EFS.yamlです。
create_EFS.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Elastic File System
Parameters:
MyClientVPNStackName:
Description: This stack name is already created Network Stack for provisioning Client VPN Environment
Type: String
Default: MyClientVPN
Resources:
MyMountTargetSecGrp:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow access to MyEFS from MyPrivateSubnet
VpcId:
Fn::ImportValue:
!Sub "${MyClientVPNStackName}--VPCId"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '2049'
ToPort: '2049'
SourceSecurityGroupId:
Fn::ImportValue:
!Sub "${MyClientVPNStackName}--PrivateSubnetSecGrpId"
MyEFS:
Type: AWS::EFS::FileSystem
Properties:
Encrypted: true
FileSystemTags:
- Key: Name
Value: My-EFS
MyEFSMountTarget:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref MyEFS
SubnetId:
Fn::ImportValue:
!Sub "${MyClientVPNStackName}--PrivateSubnetId"
SecurityGroups:
- !Ref MyMountTargetSecGrp
このコードはClient VPNのスタックがアクティブ状態で実施する必要がある点だけが注意です。当コード実行完了後のEFSの状態は下記のとおりです。
EFSのマウント実施
上記画面の右上にある「アタッチ」ボタンを押下すると、マウントするために必要なコマンド等を参照することができます。
赤枠で囲んでいる箇所が今回のEFSマウント用のコマンドになります。これをターミナルから実行します。
※事前にマウントポイントのefsディレクトリを作成しておくことを忘れないように注意。
[ec2-user@ip-10-0-2-10 ~]$ sudo mkdir efs
[ec2-user@ip-10-0-2-10 ~]$ sudo mount -t efs -o tls fs-0f782368482ed0ef5:/ efs
[ec2-user@ip-10-0-2-10 ~]$
無事にエラーなくプロンプトが返ってきましたので、EFSのマウントは成功です。mountコマンドを実行すると、最下行に下記のメッセージが出力されており、コマンド上からもマウントできていることが確認できます。
127.0.0.1:/ on /home/ec2-user/efs type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,noresvport,proto=tcp,port=20873,timeo=600,retrans=2,sec=sys,clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1)
後はefsディレクトリに移動すれば普通にファイル作成などが可能です。
EFSアンマウント実施
親ディレクトリに戻り、umountコマンドを実行します。
[ec2-user@ip-10-0-2-10 efs]$ cd
[ec2-user@ip-10-0-2-10 ~]$ sudo umount efs
[ec2-user@ip-10-0-2-10 ~]$
さいごに
今回は2024年2月にAWSのパブリックIPアドレスが有料化になることへの対策として、下記2点の検証を行い、問題ないことを確認できました。
- Client VPNを使用すれば、パブリックEC2インスタンスを作成しなくても、外部のPC端末からプライベートIPを使用して、プライベートEC2インスタンスにSSH接続できること。
- VPN接続状態のプライベートEC2インスタンスから他のサービスも使用できること。
結果的には問題ないものの、個人的には以下の点が辛いところだと感じています。
-
Client VPN環境のプロビジョニングには、CloudFormationのコード開始から終了までの処理時間が10分近く要する。
- パネルからClient VPN Endpointを作成しても同じように時間がかかります。接続先のネットワークのアタッチに時間を要するようです。
- 以前のパブリックEC2インスタンスのプロビジョニングであれば、コード開始から終了まで1分強で完了していたことを思うと、待ち時間が長い。
-
有料の為、今まで使用を避けてきた、NAT GatewayやEIPの利用が必要になる。
- オブジェクトストレージであるS3に対してプライベート・インスタンスからOutbound通信がしたいのであれば、VPC Gateway Endpointを使用することで無料作成は可能。ただし、VPC内からインターネットへ向けてのOutbound通信を使用したい場合には避けては通れない。
実際に2024年2月に有料化された後、今回の構築の費用対効果を検証する意味で、パブリックIP使用する場合とそうでない場合との比較をしてみようと思います。