はじめに
先日、RDSに対してIAM認証の設定をしたのですが、手作業で作業をやったこともあり思いのほか手こずったので、CDKをつかって実装してみることにしました。
今回実装したのは、IAM認証を有効にしたRDS(PostgreSQL)とそれをメンテナンスするプライベートサブネットにおいたEC2を作るCDK(TypeScript)です。
実装例
VPC関連設定
何はともあれVPCとサブネットを作成します。
const vpc = new Vpc(this, 'Vpc', { vpcName: 'MyVPC', maxAzs: 2, natGateways: 0, subnetConfiguration: [ { // EC2用 cidrMask: 24, name: 'ec2', subnetType: SubnetType.PRIVATE_ISOLATED }, { // RDS用 cidrMask: 24, name: 'rds', subnetType: SubnetType.PRIVATE_ISOLATED } ] });
今回はNATGatewayを作りませんでしたが、作る場合は、natGateways:
の記述を削除したのち、subnetConfiguration
にパブリックサブネットを追記する必要があります。
NATGatewayを作らない場合、EC2に対してソフトのインストールやパッチ適用はどうするのか不安に思われる方もいるかもしれませんが、以下のre:PostにあるようにS3バケットにパッチが含まれているので標準の範囲内であればNATGatewayがなくても運用できます。
後々の処理でEC2用とRDS用のサブネットを指定するために以下の定数を設定しておきます。
const ec2Subnet = vpc.selectSubnets({ subnetGroupName: 'ec2' }); const dbSubnet = vpc.selectSubnets({ subnetGroupName: 'rds' });
次にセキュリティグループを作成します。EC2からSSM用のエンドポイントに向けてHTTPSを通す必要があるのでその許可設定を行なっておきます。
// EC2用セキュリティグループ const ec2SecurityGroup = new SecurityGroup(this, 'EC2SecurityGroup', { securityGroupName: 'EC2SecurityGroup', vpc: vpc, allowAllOutbound: true }); // SSMエンドポイント用セキュリティグループ const ssmSecurityGroup = new SecurityGroup(this, 'SSMSecurityGroup', { securityGroupName: 'SSMSecurityGroup', vpc: vpc, allowAllOutbound: true }); ssmSecurityGroup.addIngressRule(ec2SecurityGroup, Port.HTTPS);
エンドポイントの設定を行います。必要なものは以下のre:Postを参考に。
vpc.addInterfaceEndpoint('SSMEndpoint', { service: InterfaceVpcEndpointAwsService.SSM, securityGroups: [ssmSecurityGroup], subnets: ec2Subnet }); vpc.addInterfaceEndpoint('SSMMessagesEndpoint', { service: InterfaceVpcEndpointAwsService.SSM_MESSAGES, securityGroups: [ssmSecurityGroup], subnets: ec2Subnet }); vpc.addInterfaceEndpoint('EC2MessagesEndpoint', { service: InterfaceVpcEndpointAwsService.EC2_MESSAGES, securityGroups: [ssmSecurityGroup], subnets: ec2Subnet });
パッチ取得用のGatewayタイプのVPCエンドポイントも忘れずに作成しておきます。
// GatewayタイプのVPCエンドポイントを作成 vpc.addGatewayEndpoint('S3Endpoint', { service: GatewayVpcEndpointAwsService.S3, subnets: [ec2Subnet], });
IAMロール
手作業でちょっとハマったのがIAMロール設定です。AmazonSSMManagedInstanceCore
を設定します。また、アクセスログの保存用にCloudWatch Logsに対する権限も必要です。
const ec2Role = new Role(this, "EC2Role", { assumedBy: new ServicePrincipal("ec2.amazonaws.com"), roleName: "EC2Role", description: "IAM Role for EC2" }); // IAMロールにSSMのポリシーをアタッチ ec2Role.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName( "AmazonSSMManagedInstanceCore" ) ); // アクセスログの保存用にCloudWatch Logsのアクセスポリシーをアタッチ ec2Role.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName( "CloudWatchLogsFullAccess" ) );
なお、SSMにてアクセスログを保存させる設定もCDKで実装しようかなと思っていたのですが、現状ではコンソール上でしか設定できなさそうでした。
EC2を作成
ここまで準備できたら、EC2の作成は非常に簡素な設定となります。
const ec2 = new Instance(this, "MyEC2", { instanceType: InstanceType.of( InstanceClass.T3, InstanceSize.MICRO ), machineImage: MachineImage.latestAmazonLinux2023(), vpc: vpc, vpcSubnets: ec2Subnet, role: ec2Role, });
あらかじめインストールするソフトが決まっているのであれば、以下の記述を追加して、EC2インスタンス作成時にuserData
プロパティに指定しても良いかもしれません。
const userData = UserData.forLinux({ shebang: '#!/bin/bash', }); userData.addCommands( 'dnf install -y mariadb105', 'dnf install -y postgresql15' );
RDS
RDSの作成において特筆すべき点は2点。一つはiamAuthentication
プロパティをtrue
にすることです。
const rdsInstance = new DatabaseInstance(this, "MyRDSInstance", { instanceIdentifier: "MyRDSInstancePostgreSQL", vpc: vpc, engine: DatabaseInstanceEngine.POSTGRES, instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO), databaseName: "foobar", multiAz: false, subnetGroup: dbSubnetGroup, securityGroups: [rdsSG], iamAuthentication: true, credentials: Credentials.fromSecret(secret) });
二つ目は、ポリシーの追加です。
以前は以下のようにPolicyStatement
を書いていたのですが...
ec2Role.addToPolicy( new PolicyStatement({ actions: [ "rds-db:connect" ], resources: [ `arn:aws:rds-db:${Aws.REGION}:${Aws.ACCOUNT_ID}:dbuser:${rdsInstance.instanceResourceId}/myiam_db_user` ] }) );
今はgrantConnect
というメソッドを利用するとよいです(myiam_db_user
がIAM認証対象ユーザー)。
rdsInstance.grantConnect(ec2Role, "myiam_db_user");
最後にEC2からRDSへの通信許可を設定してあげます。
rdsInstance.connections.allowDefaultPortFrom(ec2, "allow connect from ec2");
CDKでやる内容は以上で終わりです。
RDSでの設定
RDSでの設定は以下のことをやります。
- IAM認証対象ユーザーを作る
- 権限付与
- 接続テスト
IAM認証対象ユーザーの作成はRDSに対して上記で作成したEC2からpsql
で接続して、以下のコマンドを打ちます。
foobar=> CREATE USER myiam_db_user; CREATE ROLE foobar=> GRANT rds_iam TO myiam_db_user; GRANT ROLE foobar=>
権限付与は必要なものを適切に。
foobar=> ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myiam_db_user; ALTER DEFAULT PRIVILEGES foobar=>
あとはIAM認証対象ユーザーでログインできるか試みます。
sh-5.2$ export RDSHOST=RDSのエンドポイント sh-5.2$ export PGPASSWORD="$(aws rds generate-db-auth-token --hostname $RDSHOST --port 5432 --region ap-northeast-1 --username myiam_db_user)" sh-5.2$ psql "host=$RDSHOST port=5432 dbname=foobar user=myiam_db_user password=$PGPASSWORD" psql (15.12, server 17.2) WARNING: psql major version 15, server major version 17. Some psql features might not work. SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off) Type "help" for help. foobar=>
考察
RDSに対してIAM認証の設定を実施することをCDKにて実装してみました。単純にIAM認証のプロパティをtrue
にするだけでなく、メンテナンス用のEC2を用意して安全にメンテナンスできるようにしているのが工夫点です。このとき、VPCエンドポイントの設定が地味に面倒なのですが、CDKで実装しておけば間違いも起きにくいと考えます。
また、RDSに対するrds-db:connect
アクションの設定がgrantConnect
メソッドで実装できるようになっており、CDK活用のメリットがまた一つ加わったと考えています。