RDS用のセキュリティグループを確認する構成
RDSに適用するセキュリティグループを確認します。
EC2からRDSにアクセスする場合、ポイントとなるのはセキュリティグループです。EC2インスタンスと同様に、RDSにもセキュリティグループを適用することができます。
セキュリティグループにより DB インスタンスに対する送受信トラフィックへのアクセスを制御します。 (中略) デフォルトでは、ネットワークアクセスは、DB インスタンスで無効になっています。セキュリティグループで IP アドレス範囲、ポート、またはセキュリティグループからのアクセスを許可するルールを指定できます。
セキュリティグループによるアクセスコントロール
上記の通り、セキュリティグループを適切に設定しなければ、EC2インスタンスはRDSにアクセスすることはできません。今回はRDSに設定するセキュリティグループに関して3つのパターンを用意して、動作を確認します。
構築する環境
プライベートサブネットの1つに、EC2インスタンスを配置します。インスタンスは、後述のRDSへアクセスするクライアントとして動作します。インスタンスは、最新版のAmazon Linux2のAMIをベースにします。
MySQLタイプのRDSインスタンスを作成します。RDSとセキュリティグループの関係を明らかにするために、3つのパターンを用意します。なおセキュリティグループ以外の設定は全て同一とします。
- セキュリティグループ設定なし
- 許可するインバウンド通信が1つもないセキュリティグループを適用する
- MySQL通信(3306/tpc)を許可するセキュリティグループを適用する
S3用VPCエンドポイントを作成します。MySQLインスタンスに接続するためのクライアントをインストールするためです。
セキュリティグループによって、RDS接続時の挙動がどのように変化するかを確認するために、以下の手順で検証します。
- EC2インスタンス・RDSインスタンスを配置したプライベートサブネットでVPCフローログを有効化する。
- EC2インスタンスにリモートアクセスし、3つのRDSインスタンスにアクセスを試みる。
- アクセス結果およびアクセス時のフローログを確認する。
環境構築用のCloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置してます。
https://github.com/awstut-an-r/awstut-soa/tree/main/05/001
テンプレートファイルのポイント解説
今回のアーキテクチャを構成するための、各テンプレートファイルのポイントを取り上げます。
RDS用セキュリティグループ
RDSインスタンスに適用するセキュリティグループを確認します。いくつかパターンを用意します。
Resources:
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-InstanceSecurityGroup"
GroupDescription: Deny All.
VpcId: !Ref VPC
RDSDenySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-RDSDenySecurityGroup"
GroupDescription: Deny All.
VpcId: !Ref VPC
RDSAllowSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-RDSAllowSecurityGroup"
GroupDescription: Allow MySQL.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref MySQLPort
ToPort: !Ref MySQLPort
SourceSecurityGroupId: !Ref InstanceSecurityGroup
Code language: YAML (yaml)
RDSインスタンスに適用可能なセキュリティグループには3種類あります。
VPC セキュリティグループは、VPC 内の DB インスタンスと EC2 インスタンスへのアクセスを制御します。
DB セキュリティグループは、VPC 内にない EC2-Classic DB インスタンスへのアクセスを制御します。
EC2-Classic セキュリティグループは、EC2 インスタンスへのアクセスを制御します。
セキュリティグループによるアクセスコントロール
今回は通常のVPC内に設置されているRDSインスタンスが対象となりますので、VPCセキュリティグループを作成し、適用します。
定義する1つ目のセキュリティグループ(InstanceSecurityGroup)はEC2インスタンスに適用するものです。SecurityGroupIngressプロパティは設定しません。今回の構成では、Ec2インスタンスへのインバウンド通信は発生しないためです。
2つ目(RDSDenySecurityGroup)はRDSインスタンス用のセキュリティグループです。こちらもSecurityGroupIngressプロパティを設定しません。
3つ目(RDSAllowSecurityGroup)もRDSインスタンス用です。こちらはSecurityGroupIngressプロパティでMySQL通信(3306/tcp)を許可するように設定します。また送信元に先述のEC2インスタンス用セキュリティグループを指定することで、EC2インスタンスからのアクセスのみを許可します。
UserDataでMySQLクライアントのインストール処理を定義する
EC2インスタンスを確認します。ポイントはユーザーデータによるインスタンスの初期化処理です。
Resources:
Instance:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet1
GroupSet:
- !Ref InstanceSecurityGroup
UserData: !Base64 |
#!/bin/bash -xe
yum update -y
yum install -y mariadb
Code language: YAML (yaml)
今回のEC2インスタンスの役目は、MySQLタイプのRDSインスタンスにアクセスするクライアントとしての働きです。ですからMySQLクライアントをインストールする必要があります。
Amazon Linux 2の場合、MySQLクライアントはmariadbパッケージからインストールできます。同パッケージはS3バケット上に構築されているAmazon Linuxリポジトリから取得可能です。今回はS3用VPCエンドポイント経由で、同リポジトリにアクセスします。
RDSインスタンスのクライアントツールおよび接続方法については、以下のページをご確認ください。
EC2インスタンスの初期化処理はいくつかありますが、今回はユーザーデータを使用します。
ユーザーデータはUserDataプロパティで設定します。先述のmariadbパッケージのインストール処理を記載します。
ユーザーデータを含むインスタンスの初期化処理に関する詳細は、以下のページもご確認ください。
3パターンのRDSインスタンスを定義する
RDSインスタンスを確認します。
Resources:
DBInstance1:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: !Ref DBAllocatedStorage
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
DBInstanceClass: !Ref DBInstanceClass
DBInstanceIdentifier: dbinstance1
DBName: !Ref DBName
DBSubnetGroupName: !Ref DBSubnetGroup
Engine: !Ref DBEngine
EngineVersion: !Ref DBEngineVersion
MasterUsername: !Ref DBMasterUsername
MasterUserPassword: !Ref DBMasterUserPassword
#VPCSecurityGroups:
DBInstance2:
Type: AWS::RDS::DBInstance
Properties:
...
VPCSecurityGroups:
- !Ref RDSDenySecurityGroup
DBInstance3:
Type: AWS::RDS::DBInstance
Properties:
...
VPCSecurityGroups:
- !Ref RDSAllowSecurityGroup
Code language: YAML (yaml)
セキュリティグループに関する設定以外は全て同じRDSインスタンスを3台用意します。
1台目のインスタンス(DBInstance1)は、セキュリティグループに関する設定を行いません。
2台目のインスタンス(DBInstance2)は、VPCSecurityGroupsプロパティにて、許可するインバウンド通信が1つもないセキュリティグループを適用します。
3台目のインスタンス(DBInstance3)は、MySQL通信(3306/tcp)のみを許可するセキュリティグループを適用します。
CloudWatch LogsにVPCフローログを保存する
soa-05-001-flowlog.yamlでVPCフローログ関係のリソースを定義します。
Resources:
FlowLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "${Prefix}-FlowLogGroup"
FlowLogToCloudWatchLogs:
Type: AWS::EC2::FlowLog
DependsOn:
- FlowLogGroup
Properties:
DeliverLogsPermissionArn: !GetAtt DeliverLogRole.Arn
LogDestinationType: cloud-watch-logs
LogGroupName: !Sub "${Prefix}-FlowLogGroup"
ResourceId: !Ref VPC
ResourceType: VPC
TrafficType: ALL
Code language: YAML (yaml)
VPCフローログはVPC内のネットワークログを収集できるサービスです。
VPC フローログは、VPC のネットワークインターフェイスとの間で行き来する IP トラフィックに関する情報をキャプチャできるようにする機能です。フローログデータは Amazon CloudWatch Logs または Amazon S3 に発行できます。
VPC フローログ
今回はCloudWatch LogsにVPCフローログを配信します。収集する範囲ですが、VPC全体の全てのログとします。
VPCフローログの詳細については、以下のページをご確認ください。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- インスタンスのID:i-0fe6d50e5958523a4
- DBInstance1のエンドポイント:dbinstance1.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
- DBInstance2のエンドポイント:dbinstance2.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
- DBInstance3のエンドポイント:dbinstance3.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
- DBInstance1にアタッチされているENI:eni-085da82d3ccb0f0ad
- DBInstance2にアタッチされているENI:eni-0acae018ffc56ba56
- DBInstance3にアタッチされているENI:eni-0e71f2697f24bffbf
- CloudWatch Logsのロググループ名:soa-05-001-FlowLogGroup
各DBインスタンスにアタッチされているセキュリティグループは以下の通りです。
またDBインスタンスにアタッチされているENIは、インスタンスのページから確認することができます。
事前準備1 – SSM Session Manager経由でインスタンスでアクセスする
準備が整いましたので、EC2インスタンスにアクセスします。
アクセスはSSM Session Managerを通じて行います。
$ aws ssm start-session \
--target i-0fe6d50e5958523a4
Starting session with SessionId: root-0539c6fe4b063eeb4
sh-4.2$
Code language: Bash (bash)
正常にアクセスすることができました。
SSM Session Managerに関しては、以下をご確認ください。
アクセス後、ユーザーデータで定義した、MySQLクライアントのインストール状況を確認します。
sh-4.2$ sudo yum list installed | grep mysql
mysql-community-client.aarch64 8.0.27-1.el7 @mysql80-community
mysql-community-client-plugins.aarch64 8.0.27-1.el7 @mysql80-community
mysql-community-common.aarch64 8.0.27-1.el7 @mysql80-community
mysql-community-libs.aarch64 8.0.27-1.el7 @mysql80-community
mysql-community-libs-compat.aarch64 8.0.27-1.el7 @mysql80-community
mysql80-community-release.noarch el7-1 installed
Code language: PHP (php)
インスタンスの初期化中に、正常にインストールされていることが確認できます。
事前準備2 – CloudWatch Logsに配信されたVPCフローログを確認する
現時点で配信されているVPCフローログを確認します。
VPC上に存在するENIごとにログストリームが生成されていることがわかります。
赤枠の上から順に、DBInstance3, DBInstance2, DBInstance1のストリームです。
RDS接続試験1 – セキュリティグループなし
まずDBインスタンス1から試験を始めます。
このインスタンスにはセキュリティグループが定義されていません。
試験を始める前に、nslookupコマンドでエンドポイントに設定されているIPアドレスを確認します。これは後ほどVPCフローログを確認する際に、RDSに設定されているIPアドレスを使用してログを検索するためです。
sh-4.2$ nslookup dbinstance1.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Server: 10.0.0.2
Address: 10.0.0.2#53
Non-authoritative answer:
Name: dbinstance1.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Address: 10.0.3.213
Code language: Bash (bash)
アドレスは10.0.3.213でした。
それでは試験を始めます。インストールしたmysqlコマンドでアクセスを試みます。
sh-4.2$ mysql -u testuser -p -h dbinstance1.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Enter password:
ERROR 2003 (HY000): Can't connect to MySQL server on 'dbinstance1.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com:3306' (110)
Code language: Bash (bash)
失敗しました。しばらく待つとエラーメッセージが返ってきます。
失敗時のVPCフローログは以下の通りです。
VPCフローログの見方はVPC フローログに詳しいですが、10.0.3.213:3306宛にパケットを送った結果、REJECTされていることがわかります。
以上のことから、DBインスタンス1にはセキュリティグループを適用していないため、全インバウンド通信が拒否されるという挙動となり、アクセス失敗という結果になったことがわかります。
RDS接続試験2 – 許可するインバウンド通信が1つもなし
次にDBインスタンス2に対して試験を行います。手順は先ほどと同様です。
sh-4.2$ nslookup dbinstance2.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Server: 10.0.0.2
Address: 10.0.0.2#53
Non-authoritative answer:
Name: dbinstance2.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Address: 10.0.3.205
Code language: Bash (bash)
sh-4.2$ mysql -u testuser -p -h dbinstance2.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Enter password:
ERROR 2003 (HY000): Can't connect to MySQL server on 'dbinstance2.cl50iikpthxs.ap-northeast-1.rds.a
mazonaws.com:3306' (110)
Code language: Bash (bash)
こちらも失敗しました。フローログでも、10.0.3.205へのMySQL通信が拒否されていることがわかります。
以上のことから、セキュリティグループにおいて1つもインバウンド通信を許可しないということは、全インバウンド通信を拒否するということと同義であり、セキュリティグループを設定していない場合と同様の挙動になることをわかりました。
RDS接続試験3 – MySQL通信を許可
最後にDBインスタンス3に対して試験を行います。
sh-4.2$ nslookup dbinstance3.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Server: 10.0.0.2
Address: 10.0.0.2#53
Non-authoritative answer:
Name: dbinstance3.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Address: 10.0.3.82
Code language: Bash (bash)
sh-4.2$ mysql -u testuser -p -h dbinstance3.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 29
Server version: 8.0.23 Source distribution
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
Code language: Bash (bash)
アクセスに成功しました。フローログを確認しても、EC2インスタンスからDBインスタンス3に送信したパケットと、戻りのパケットがACCEPTされていることがわかります。
以上のことから、RDSに対して、データベースシステムに応じた適切なセキュリティグループを適用することによって、アクセスできるようになることがわかりました。
まとめ
3パターンのRDSインスタンスを作成し、セキュリティグループによる挙動の違いを確認しました。
RDSにアクセスするためには、データベースシステムに応じた適切なセキュリティグループが設定されている必要があることがわかりました。
RDSに対してセキュリティグループを適用しない、あるいは適切なインバウンド通信を許可しないセキュリティグループを適用している場合は、正常に通信することができないことを確認しました。