Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

データを守る:Amazon RDSをパブリックから分離サブネットへ

By Dr. Richard KangNov 1, 202312 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

(この画像はStable Diffusionを使って作成しました)

これまで、インターネットへのデフォルトルートを持つVPCサブネット上でAmazon RDSデータベースを運用しているお客様を数多く見てきました。

こうした環境でも、RDSの「publicly accessible」オプションを無効化する、Security Groupのインバウンドルールを許可済みのEC2やLambdaのみに限定する、ファイアウォールのステートフルルールでSQLシグネチャによる脅威やエクスプロイトを検知する、といった対策が施されているケースは少なくありません。しかし2013年に発生したTarget Corporationの情報漏えい事件が示すとおり、インバウンド通信だけを遮断してアウトバウンドをすべて許可する従来のやり方では、もはやデータ流出リスクを抑えきれません。コマンド・アンド・コントロール型マルウェア(C&CマルウェアやC2マルウェアとも呼ばれます)などの攻撃者は、HTTP:80やHTTPS:443といった一般的なポートを介したアウトバウンド通信をビーコンとして悪用できるからです。

本記事では、スナップショットからのリストアによる新規DBインスタンスの作成を行わずに、インターネットへのルートを持つパブリックサブネットから分離サブネットへAmazon RDSを移行する手順を解説します。

VPCのサブネットには3種類あります。パブリックサブネットはインターネットゲートウェイがアタッチされ、インバウンド・アウトバウンド双方のインターネット通信が可能です。プライベートサブネットはNAT(Network Address Translation)またはInternet Egress-onlyゲートウェイを備え、アウトバウンドのインターネット通信のみを許可します。分離サブネットはVPC内部の通信のみに限定され、VPC外部へのルートを一切持ちません。

データベースを分離サブネットに配置すれば攻撃対象領域を狭められるため、C&Cマルウェアのような攻撃者がデータを外部に持ち出す経路を見つけにくくなり、結果としてデータ流出リスクの低減につながります。

ただし、ネットワーク分離はリスクを下げるための重要な対策の1つにすぎず、データ流出を完全に防げる万能策ではありません。鎖の強さは、もっとも弱い輪で決まります。リスクを少しでも減らす一つひとつの対策が重要です。

Multi-AZ Amazon RDSによるテスト環境の構築

RDSインスタンスをパブリックサブネットから分離サブネットへ移行する手順を試すには、VPCのパブリックサブネット上にAmazon RDSを配置したテスト環境が必要です。VPCはパブリックサブネットと分離サブネットで構成します。

すでにパブリックサブネットにAmazon RDSインスタンスがあり、VPC内に分離サブネットも用意されている場合は、VPC作成のステップは飛ばして構いません。

AWSリソースの作成にはCDKを使用します。CDKを初めて使う方は、https://docs.aws.amazon.com/cdk/v2/guide/hello_world.htm を参考に始めてください。

VPCを作成するCDKスタック

テスト環境構築の最初のステップは、以下のCDKスタックを使って、2つのアベイラビリティーゾーンそれぞれにパブリックサブネットと分離サブネットを1つずつ持つVPCを作成することです。

VPC IDなどの必要なVPCパラメーターはエクスポートしておき、次のステップでRDSを作成するスタックからインポートできるようにします。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

const app = new cdk.App();
const vpcStack = new cdk.Stack(app, "vpc-stack");
const vpc = new cdk.aws_ec2.Vpc(vpcStack, `test-vpc`, {
    ipAddresses: cdk.aws_ec2.IpAddresses.cidr("10.0.0.0/20"),
    vpcName: `test-vpc`,
    enableDnsHostnames: true,
    enableDnsSupport: true,
    maxAzs: 2,
    subnetConfiguration: [\
        {\
            name: "public",\
            subnetType: cdk.aws_ec2.SubnetType.PUBLIC,\
            cidrMask: 27\
        },\
        {\
            name: "isolated",\
            subnetType: cdk.aws_ec2.SubnetType.PRIVATE_ISOLATED,\
            cidrMask: 27\
        },\
    ]
});

new cdk.CfnOutput(
  vpcStack, 'testVpcVpcID', {
  value: vpc.vpcId,
  description: 'Test Vpc ID',
  exportName: 'test-vpc-VpcID',
});

new cdk.CfnOutput(
  vpcStack, 'testVpcAz', {
  value: vpc.availabilityZones.join(","),
  description: 'Vpc AZs',
  exportName: 'test-Vpc-AZs',
});

new cdk.CfnOutput(
  vpcStack, 'testVpcPublicSubnets', {
  value: vpc.selectSubnets({
    subnetType: cdk.aws_ec2.SubnetType.PUBLIC
  }).subnetIds.join(","),
  description: 'Vpc Public Subnet IDs',
  exportName: 'test-Vpc-PublicSubnetIds',
});

new cdk.CfnOutput(
  vpcStack, 'testVpcIsolatedSubnets', {
  value: vpc.selectSubnets({
    subnetType: cdk.aws_ec2.SubnetType.PRIVATE_ISOLATED
  }).subnetIds.join(","),
  description: 'Vpc Isolated Subnet IDs',
  exportName: 'test-Vpc-IsolatedSubnetIds',
});

cdk deploy vpc-stackが成功すると、次のような出力が得られます。

✅  vpc-stack

✨  Deployment time: 76.83s

Outputs:
vpc-stack.testVpcAz = us-east-1a,us-east-1b
vpc-stack.testVpcIsolatedSubnets = subnet-03e58f5428463ad26,subnet-06721c9fdaac5f333
vpc-stack.testVpcVpcID = vpc-090949477d23289ce
vpc-stack.testeVpcPublicSubnets = subnet-0271290b1164913ad,subnet-079b9aaba653c430b

VPC ID、パブリックサブネットID、分離サブネットIDを控えておきます。

本例では、Amazon RDSの作成時に次のパラメーターを使います。

  • VPC ID: vpc-090949477d23289ce
  • パブリックサブネットID: subnet-0271290b1164913adsubnet-079b9aaba653c430b
  • 分離サブネットID: subnet-06721c9fdaac5f333subnet-03e58f5428463ad26

Amazon RDSを作成するCDKスタック

続いて、VPCのパブリックサブネット上にAmazon Postgres RDSをプロビジョニングします。

CDKに関するワンポイント:cdk.Fn.importValue() 遅延バインディング属性 です。そのため、TypeScriptの文字列分割ではなく、CloudFormationの文字列分割関数cdk.Fn.Split() を使う必要があります。cdk.Fn.importValue(‘test-Vpc-AZs’).split(",") のように書くと、Some input subnets in :[subnet-0271290b1164913ad,subnet-079b9aaba653c430b] are invalid.のようなエラーになります。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

const app = new cdk.App();
const rdsStack = new cdk.Stack(app, "rds-stack");
const vpcLookup = cdk.aws_ec2.Vpc.fromVpcAttributes(
  rdsStack, `${rdsStack.stackName}-vpc-lookup`,
  <cdk.aws_ec2.VpcAttributes>{
    vpcId: cdk.Fn.importValue('test-vpc-VpcID'),
    availabilityZones: cdk.Fn.split(",",cdk.Fn.importValue('test-Vpc-AZs')),
    publicSubnetIds: cdk.Fn.split(",",cdk.Fn.importValue('test-Vpc-PublicSubnetIds')),
    isolatedSubnetIds: cdk.Fn.split(",",cdk.Fn.importValue('test-Vpc-IsolatedSubnetIds'))
  });
const subnetType: cdk.aws_ec2.SubnetSelection = { subnetType: cdk.aws_ec2.SubnetType.PUBLIC };
const subnetGroup = new cdk.aws_rds.SubnetGroup(
  rdsStack, `${rdsStack.stackName}-rds-subnetgroup`,
  <cdk.aws_rds.SubnetGroupProps>{
    subnetGroupName: `${rdsStack.stackName}-rds-subnetgroup`,
    vpc: vpcLookup,
    vpcSubnets: {
      subnets: vpcLookup.selectSubnets(subnetType).subnets
    },
    description: "RDS subnetgroup"
  });
const rdsInstance = new cdk.aws_rds.DatabaseInstance(
  rdsStack, `${rdsStack.stackName}-rdsInstance`,
  <cdk.aws_rds.DatabaseInstanceProps>{
    engine: cdk.aws_rds.DatabaseInstanceEngine.postgres({
      version: cdk.aws_rds.PostgresEngineVersion.VER_15_3
    }),
    vpcLookup,
    subnetGroup: subnetGroup,
    publiclyAccessible: false,
    multiAz: true,
    credentials: cdk.aws_rds.Credentials.fromSecret(
      new cdk.aws_rds.DatabaseSecret( rdsStack, `${rdsStack.stackName}-database-secret`,
        <cdk.aws_rds.DatabaseSecretProps>{
          username: "dbMaster",
          secretName: `dbMaster-credential`,
        }
      )
    ),
    instanceIdentifier: `${rdsStack.stackName}-rds`,
    instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.BURSTABLE4_GRAVITON, cdk.aws_ec2.InstanceSize.MICRO),
    storageType: cdk.aws_rds.StorageType.GP3,
});

new cdk.CfnOutput(
  rdsStack, `${rdsStack.stackName}-rds-name`, {
    value: rdsInstance.instanceIdentifier,
    description: 'RDS instance identifier'
});

new cdk.CfnOutput(
  rdsStack, `${rdsStack.stackName}-subnetgroup-name`, {
    value: subnetGroup.subnetGroupName,
    description: "RDS Subnet Group Name"
});

既存のVPC内にRDSを作成したい場合は、vpcLookupを次のように該当する値で置き換えます。

const vpcLookup = cdk.aws_ec2.Vpc.fromVpcAttributes(rdsStack, `${rdsStack.stackName}-vpc-lookup`, {
  vpcId: "vpc-090949477d23289ce",
  availabilityZones: ["us-east-1a", "us-east-1b"],
  publicSubnetIds: ["subnet-0271290b1164913ad", "subnet-079b9aaba653c430b"],
  isolatedSubnetIds: ["subnet-03e58f5428463ad26", "subnet-06721c9fdaac5f333"],
});

cdk deploy rds-stackが成功すると、次のような出力になります。

 ✅  rds-stack

✨  Deployment time: 20.95s

Outputs:
rds-stack.rdsstackrdsname = rds-stack-rds

ここまでで作成したもの

2つのアベイラビリティーゾーンを持つVPCを作成しました。各ゾーンにはパブリックサブネットと分離サブネットが1つずつあります。

さらに、両方のアベイラビリティーゾーンのパブリックサブネット上にマルチAZのAmazon RDSをプロビジョニングしました。

パブリックサブネット上のマルチAZ RDS

Amazon RDSをパブリックサブネットから分離サブネットへ移動する

RDS Subnet Groupは、各アベイラビリティーゾーンに1つずつ、複数のサブネットで構成されます。たとえば、AZ-1にプライマリインスタンス、AZ-2にスタンバイインスタンスが配置される、といった具合です。

ただし、各インスタンスのサブネットを変更するには、そのサブネットを使用中のインスタンスをいったん取り除く必要があります。RDSインスタンスがそのサブネット上で稼働している状態でサブネットを変更しようとすると、次のようなエラーになります。

An error occurred (InvalidParameterValue) when calling the ModifyDBSubnetGroup operation: Some of the subnets to be deleted are currently in use: subnet-0271290b1164913ad

Amazon RDSをパブリックサブネットから分離サブネットへ移動する手順の概要は以下のとおりです。

  1. スタンバイインスタンスを停止してパブリックサブネットを解放し、RDSサブネットグループ内のそのパブリックサブネットを分離サブネットに置き換えたうえで、スタンバイインスタンスを分離サブネットに再デプロイします。
  2. スタンバイをプライマリに昇格させ、新たにスタンバイへ降格したインスタンスに対して同じ手順を繰り返します。
  3. CDKの状態を更新します。

ステップ1:スタンバイインスタンスを移動する

作業を始める前に、いくつか重要なパラメーターを把握しておきます。

プライマリとスタンバイのRDSインスタンスのサブネットを確認する

次のAWS CLIコマンドを実行して、プライマリとセカンダリのアベイラビリティーゾーンを確認します。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone}" --output table
------------------------------
|     DescribeDBInstances    |
+-------------+--------------+
|   Primary   |   Standby    |
+-------------+--------------+
|  us-east-1a |  us-east-1b  |
+-------------+--------------+

続いて、次のCLIコマンドでアベイラビリティーゾーンとRDSサブネットグループ内のサブネットを対応付けます。

% aws rds describe-db-subnet-groups --db-subnet-group rds-stack-rds-subnetgroup --query "DBSubnetGroups[*].Subnets[*].{AvailabilityZone:SubnetAvailabilityZone.Name,SubnetIdentifier:SubnetIdentifier}" --output table
--------------------------------------------------
|             DescribeDBSubnetGroups             |
+-------------------+----------------------------+
| AvailabilityZone  |     SubnetIdentifier       |
+-------------------+----------------------------+
|  us-east-1b       |  subnet-079b9aaba653c430b  |
|  us-east-1a       |  subnet-0271290b1164913ad  |
+-------------------+----------------------------+

これら2つの出力から、次のことがわかります。

  • プライマリRDSインスタンスはsubnet-0271290b1164913adにある
  • スタンバイRDSインスタンスはsubnet-079b9aaba653c430bにある

スタンバイインスタンスを停止し、サブネットを変更する

次のmodify-db-instance CLIコマンドでRDSインスタンスを変更します。

% aws rds modify-db-instance --db-instance-identifier rds-stack-rds --no-multi-az --apply-immediately --query "DBInstance.MultiAZ"
true

RDSインスタンスがシングルAZになったかを確認するため、describe-db-instances CLIを再実行します。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone}" --output table
---------------------------
|   DescribeDBInstances   |
+-------------+-----------+
|   Primary   |  Standby  |
+-------------+-----------+
|  us-east-1a |  None     |
+-------------+-----------+

RDSサブネットグループを更新し、停止したスタンバイのパブリックサブネットを、同じアベイラビリティーゾーンの分離サブネットに置き換えます。

% aws rds modify-db-subnet-group --db-subnet-group-name rds-stack-rds-subnetgroup --subnet-ids "subnet-0271290b1164913ad" "subnet-06721c9fdaac5f333"
{
    "DBSubnetGroup": {
        "DBSubnetGroupName": "rds-stack-rds-subnetgroup",
        "DBSubnetGroupDescription": "RDS subnetgroup",
        "VpcId": "vpc-090949477d23289ce",
        "SubnetGroupStatus": "Complete",
        "Subnets": [\
            {\
                "SubnetIdentifier": "subnet-0271290b1164913ad",\
                "SubnetAvailabilityZone": {\
                    "Name": "us-east-1a"\
                },\
                "SubnetOutpost": {},\
                "SubnetStatus": "Active"\
            },\
            {\
                "SubnetIdentifier": "subnet-06721c9fdaac5f333",\
                "SubnetAvailabilityZone": {\
                    "Name": "us-east-1b"\
                },\
                "SubnetOutpost": {},\
                "SubnetStatus": "Active"\
            }\
        ],
        "DBSubnetGroupArn": "arn:aws:rds:us-east-1:825202810339:subgrp:rds-stack-rds-subnetgroup",
        "SupportedNetworkTypes": [\
            "IPV4"\
        ]
    }
}

describe-db-subnet-groupsコマンドでRDSサブネットグループを確認します。

% aws rds describe-db-subnet-groups --db-subnet-group rds-stack-rds-subnetgroup --query "DBSubnetGroups[*].Subnets[*].{AvailabilityZone:SubnetAvailabilityZone.Name,SubnetIdentifier:SubnetIdentifier}" --output table
--------------------------------------------------
|             DescribeDBSubnetGroups             |
+-------------------+----------------------------+
| AvailabilityZone  |     SubnetIdentifier       |
+-------------------+----------------------------+
|  us-east-1a       |  subnet-0271290b1164913ad  |
|  us-east-1b       |  subnet-06721c9fdaac5f333  |
+-------------------+----------------------------+

modify-db-instanceコマンドでマルチAZデプロイを再度有効化します。

% aws rds modify-db-instance --db-instance-identifier rds-stack-rds --multi-az --apply-immediately --query "DBInstance.MultiAZ"
false

変更にはしばらく時間がかかる場合があります。進捗はDBInstanceStatusで確認できます。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone}" --output table
----------------------------------------
|          DescribeDBInstances         |
+------------+-----------+-------------+
|   Primary  |  Standby  |   Status    |
+------------+-----------+-------------+
|  us-east-1a|  None     |  modifying  |
+------------+-----------+-------------+

スタンバイインスタンスが作成されたことをdescribe-db-instancesで確認します。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone}" --output table
-------------------------------------------
|           DescribeDBInstances           |
+------------+--------------+-------------+
|   Primary  |   Standby    |   Status    |
+------------+--------------+-------------+
|  us-east-1a|  us-east-1b  |  available  |
+------------+--------------+-------------+

現時点の状態

セカンダリアベイラビリティーゾーンのスタンバイインスタンスを、パブリックサブネットから分離サブネットへ移動しました。

プライマリがパブリックサブネット、スタンバイが分離サブネットに配置されたマルチAZ RDS

ステップ2:スタンバイを昇格させ、旧プライマリを移動する

フェイルオーバー後にエンドポイントアドレスが変わらず、可用性が維持されることを確認するため、RDSエンドポイントを控えておきます。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address}" --output table
-----------------------------------------------------------------------------------------------------
|                                        DescribeDBInstances                                        |
+---------------------------------------------------------+-------------+-------------+-------------+
|                        Endpoint                         |   Primary   |   Standby   |   Status    |
+---------------------------------------------------------+-------------+-------------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  us-east-1a |  us-east-1b |  available  |
+---------------------------------------------------------+-------------+-------------+-------------+

注意:再起動中はインスタンスが利用できなくなるため、必ずメンテナンスウィンドウまたは負荷の低い時間帯に再起動してください。

--force-failoverパラメーターを付けてRDSインスタンスを再起動します。

aws rds reboot-db-instance --db-instance-identifier rds-stack-rds --force-failover

describe-db-instancesでフェイルオーバーを確認します。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address,MultiAZ:MultiAZ}" --output table
----------------------------------------------------------------------------------------------------------------
|                                              DescribeDBInstances                                             |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|                        Endpoint                         | MultiAZ  |   Primary   |   Standby   |   Status    |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  True    |  us-east-1b |  us-east-1a |  available  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

パブリックサブネットにある新しいスタンバイ(旧プライマリ)を取り除く

新しいプライマリが分離サブネットにある状態で、新たにスタンバイへ降格したインスタンスを取り除きます。

% aws rds modify-db-instance --db-instance-identifier rds-stack-rds --no-multi-az --apply-immediately --query "DBInstance.MultiAZ"
true

RDSインスタンスはmodifyingステータスになります。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address,MultiAZ:MultiAZ}" --output table
----------------------------------------------------------------------------------------------------------------
|                                              DescribeDBInstances                                             |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|                        Endpoint                         | MultiAZ  |   Primary   |   Standby   |   Status    |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  True    |  us-east-1b |  us-east-1a |  modifying  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

RDSインスタンスがavailableステータスに戻ると、スタンバイ用のサブネットが解放され、次のステップに進めるようになります。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address,MultiAZ:MultiAZ,Pending:PendingModifiedValues}" --output table
-------------------------------------------------------------------------------------------------------------
|                                            DescribeDBInstances                                            |
+---------------------------------------------------------+----------+-------------+----------+-------------+
|                        Endpoint                         | MultiAZ  |   Primary   | Standby  |   Status    |
+---------------------------------------------------------+----------+-------------+----------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  False   |  us-east-1b |  None    |  available  |
+---------------------------------------------------------+----------+-------------+----------+-------------+

続けて、RDSサブネットグループを変更し、パブリックサブネットを分離サブネットに置き換えます。

% aws rds modify-db-subnet-group --db-subnet-group-name rds-stack-rds-subnetgroup --subnet-ids "subnet-03e58f5428463ad26" "subnet-06721c9fdaac5f333"
{
    "DBSubnetGroup": {
        "DBSubnetGroupName": "rds-stack-rds-subnetgroup",
        "DBSubnetGroupDescription": "RDS subnetgroup",
        "VpcId": "vpc-090949477d23289ce",
        "SubnetGroupStatus": "Complete",
        "Subnets": [\
            {\
                "SubnetIdentifier": "subnet-06721c9fdaac5f333",\
                "SubnetAvailabilityZone": {\
                    "Name": "us-east-1b"\
                },\
                "SubnetOutpost": {},\
                "SubnetStatus": "Active"\
            },\
            {\
                "SubnetIdentifier": "subnet-03e58f5428463ad26",\
                "SubnetAvailabilityZone": {\
                    "Name": "us-east-1a"\
                },\
                "SubnetOutpost": {},\
                "SubnetStatus": "Active"\
            }\
        ],
        "DBSubnetGroupArn": "arn:aws:rds:us-east-1:825202810339:subgrp:rds-stack-rds-subnetgroup",
        "SupportedNetworkTypes": [\
            "IPV4"\
        ]
    }
}

マルチAZ RDSを再デプロイします。

% aws rds modify-db-instance --db-instance-identifier rds-stack-rds --multi-az --apply-immediately

RDSインスタンスは、MultiAZの値が保留中のmodifyingステータスになります。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address,MultiAZ:MultiAZ,NewValue:PendingModifiedValues}" --output table
-------------------------------------------------------------------------------------------------------------
|                                            DescribeDBInstances                                            |
+---------------------------------------------------------+----------+-------------+----------+-------------+
|                        Endpoint                         | MultiAZ  |   Primary   | Standby  |   Status    |
+---------------------------------------------------------+----------+-------------+----------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  False   |  us-east-1b |  None    |  modifying  |
+---------------------------------------------------------+----------+-------------+----------+-------------+
||                                                NewValue                                                 ||
|+------------------------------------------------------------+--------------------------------------------+|
||  MultiAZ                                                   |  True                                      ||
|+------------------------------------------------------------+--------------------------------------------+|

RDSインスタンスがavailableになると、新しいスタンバイが分離サブネットにデプロイされます。

% aws rds describe-db-instances --db-instance-identifier rds-stack-rds --query "DBInstances[*].{Status:DBInstanceStatus,Primary:AvailabilityZone,Standby:SecondaryAvailabilityZone,Endpoint:Endpoint.Address,MultiAZ:MultiAZ,NewValue:PendingModifiedValues}" --output table
----------------------------------------------------------------------------------------------------------------
|                                              DescribeDBInstances                                             |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|                        Endpoint                         | MultiAZ  |   Primary   |   Standby   |   Status    |
+---------------------------------------------------------+----------+-------------+-------------+-------------+
|  rds-stack-rds.cmkhfj0htgnu.us-east-1.rds.amazonaws.com |  True    |  us-east-1b |  us-east-1a |  available  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

現時点の状態

セカンダリアベイラビリティーゾーンのスタンバイインスタンスを、パブリックサブネットから分離サブネットへ移動しました。

分離サブネットに配置されたマルチAZ RDS

ステップ3:CDKスタックを更新する

実環境のアーキテクチャ変更が完了したので、CDKテンプレートも変更内容に合わせて更新します。subnetTypePRIVATE_ISOLATEDに書き換えてCDKスタックを修正します。

const subnetType: cdk.aws_ec2.SubnetSelection = { subnetType: cdk.aws_ec2.SubnetType.PRIVATE_ISOLATED };

CDKテンプレートと実環境のスタックの整合性を確認したい場合は、変更後にcdk deploy rds-stackを実行します。サブネットグループはすでに更新済みのため、実体的な変更は発生しません。cdk deploy rds-stackは問題なく完了するはずです。

rds-stack
rds-stack: deploying... [2/2]

 ✅  rds-stack (no changes)

✨  Deployment time: 2.25s

Outputs:
rds-stack.rdsstackrdsname = rds-stack-rds
rds-stack.rdsstacksubnetgroupname = rds-stack-rds-subnetgroup

CDKの状態同期が本当に行われているか疑わしい場合は、const subnetType: cdk.aws_ec2.SubnetSelection = { subnetType: cdk.aws_ec2.SubnetType.PUBLIC };でスタックをデプロイしてみてください。

cdk deploy rds-stackでは、同じくサブネット使用中エラーが発生するはずです:UPDATE_FAILED | AWS::RDS::DBSubnetGroup | rdsstackrdssubnetgroup Resource handler returned message: "Some of the subnets to be deleted are currently in use: subnet-03e58f5428463ad26, subnet-06721c9fdaac5f333

インターネットへのエグレスルートを持たない分離環境でAmazon RDSを運用することは、データセキュリティ、プライバシー、運用上の健全性において、次のような重要なメリットをもたらします。

  • データ流出からの保護:インターネットへのエグレスルートを排除することで、データ流出のリスクを大幅に低減できます。機微なデータを管理された環境内に閉じ込められるため、意図的・偶発的な漏えいの可能性を最小限に抑えられます。これはデータ保護規制への準拠や、機密情報の保護において特に重要なポイントです。
  • 運用上の健全性の維持:データベースを分離環境で運用することで、運用面の健全性を維持しやすくなります。設定ミスや脆弱性による意図しないインターネット接続は、想定外の挙動、データ破損、不正なデータ変更を招く恐れがあります。
  • コンプライアンスと規制への適合:多くの業界では、データの取り扱いと保管に厳格な統制を求めるコンプライアンス規制が課せられています。データベースをネットワーク的に分離することは、ISO27001:2013 A.13.1.3への適合などのコンプライアンス対応を後押しします。
  • 攻撃対象領域の縮小:分離環境はデータベースの攻撃対象領域を狭め、さまざまな攻撃に対する耐性を高めます。インターネットに直接つながっていなければ、攻撃者が脆弱性を突いたり、悪意あるコードを送り込んだり、偵察を行ったりする機会は大きく減ります。
  • 内部脅威への備え:外部からの脅威はもちろん懸念事項ですが、内部からの脅威も同程度の被害をもたらし得ます。データベースを分離してアクセスを許可された担当者のみに制限すれば、インサイダー脅威や偶発的なデータ露出、データ改ざんの可能性を抑えられます。