Blog

Securing Your Data: Moving Amazon RDS from Public to Isolated Subnet

(This image was created with the assistance of Stable Diffusion)

Step-by-Step: Securing your Amazon RDS from the risk of data exfiltration

I have been working with customers that host the Amazon RDS database in a VPC subnet with a default route to the internet.

There could be safety measures in place, such as the “publicly accessible” option of RDS disabled; Security Group inbound rules being restricted to only the allowed EC2 or Lambda; and firewall stateful rules detecting threats and exploits with SQL signatures. However, incidents such as Target Corporation’s Data breach in 2013 show that just blocking incoming internet traffic while allowing all outgoing internet traffic is no longer sufficient to minimize the data exfiltration risk. A threat actor such as command-and-control malware (also known as C&C or C2 malware) can use the outbound traffic over commonly used ports like HTTP:80 and HTTPS:443 for beaconing.

This article describes the steps to move an Amazon RDS from a public subnet with an internet-bounded route table to an isolated subnet, without the need to create a new DB instance using snapshot-restore.

Any VPC can have 3 types of subnet: a public subnet which has an internet gateway attached to it to allow both incoming and outgoing internet traffic; a private subnet that has a Network Address Translation (NAT) or Internet Egress-only gateway to allow outgoing internet traffic; an isolated subnet allows only traffic within the VPC and has no routes to destinations outside the VPC.

Hosting the database in an isolated subnet can help reduce the risk of data exfiltration, by reducing the attack surface, making it less likely for an attacker such as a C&C malware to find a way to exfiltrate the data.

However, do note that network isolation is only one of the important security measures to help reduce the risk, but not foolproof to prevent data exfiltration. A chain is only as strong as its weakest link. Every measure in reducing the risk counts.

Setting up the test environment with Multi-AZ Amazon RDS

To follow the steps of moving the RDS Instance from a public subnet to an isolated subnet, you will need to set up a test environment, consisting of an Amazon RDS in the public subnets of a VPC. The VPC consists of public and isolated subnets.

Feel free to skip the VPC creation step if you already have Amazon RDS instances in public subnets, and the VPC has isolated subnets.

We will be creating the AWS resources using CDK. If you are new to CDK, you can follow https://docs.aws.amazon.com/cdk/v2/guide/hello_world.htm on how to start.

CDK stack that creates a VPC

The first step of setting up the test environment is to create a VPC with one public and isolated subnet in each Availability Zone, across two Availability Zones using the CDK stack as below.

We will export the necessary VPC parameters such as the VPC ID so that we can import them into the stack that creates the RDS in the next step.

#!/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',
});

If cdk deploy vpc-stackis successful, you should see the outputs such as below:

✅  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

Take note of the VPC ID, the Public Subnet IDs, and the Isolated Subnet IDs.

In this example, these are the parameters that we will be using when creating the Amazon RDS:

  • VPC ID: vpc-090949477d23289ce
  • Public subnet IDs: subnet-0271290b1164913ad, subnet-079b9aaba653c430b
  • Isolated subnet IDs: subnet-06721c9fdaac5f333, subnet-03e58f5428463ad26

CDK stack that creates Amazon RDS

Next, we will provision an Amazon Postgres RDS in the public subnet of the VPC:

Quick tips on CDK:  cdk.Fn.importValue() is a late-bound attribute. Therefore, we need to use the CloudFormation string split function cdk.Fn.Split() instead of Typescript of string split such as cdk.Fn.importValue(‘test-Vpc-AZs’).split(“,”)which would cause errors such as 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"
});

If you have an existing VPC that you would want the RDS to be created in, change the vpcLookupwith the relevant values, such as:

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"],
});

If cdk deploy rds-stackis successful, you should see the outputs such as below:

 ✅  rds-stack

✨  Deployment time: 20.95s

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

What have we created?

We have created a VPC with two Availability Zones. Each zone has a public and an isolated subnet.

We also provisioned multi-AZ Amazon RDS in the public subnet of both availability zones.

Multi-AZ RDS in public subnets

Multi-AZ RDS in public subnets

Move Amazon RDS from public subnet to isolated subnets

We have an RDS Subnet Group that consists of multiple subnets, each for one Availability Zone, for example, the primary instance in AZ-1 and the stand-by instance in AZ-2.

However, to change the subnet of each instance, we need to remove the instance that is using that subnet, otherwise, if we are to modify the subnet with an RDS instance in that subnet, an error will occur:

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

Overview of the steps to move the Amazon RDS from the public subnet to the isolated subnet:

  • Stop the standby instance to free up the public subnet, so that the RDS subnet group can change that public subnet to the isolated subnet before re-deploying the standby instance in the isolated subnet.
  • Promote the standby as primary, and repeat the same on the newly demoted standby instance.
  • Updates the state of CDK.

Step 1: Move the standby instance

Before we start, we need to capture some key parameters:

Take note of the subnet of Primary and Standby RDS instance

Run this AWS CLI command to query for the Primary and Secondary Availability Zone:

% 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  |
+-------------+--------------+

Map the Availability Zone to the Subnets in the RDS Subnet Group with the CLI command:

% 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  |
+-------------------+----------------------------+

Based on these 2 outputs, we can conclude that

  • The primary RDS instance is insubnet-0271290b1164913ad.
  • The standby RDS instance is in subnet-079b9aaba653c430b.

Stop the standby instance and change the subnet

Modify the RDS instance with  modify-db-instance CLI command as below:

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

To confirm if the RDS Instance is a single AZ, rerun the describe-db-instancesCLI

% 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     |
+-------------+-----------+

Update the RDS Subnet group to replace the public subnet of terminated standby with an isolated subnet of the same availability zone.

% 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"
        ]
    }
}

Confirm the RDS Subnet Group with describe-db-subnet-groupscommand:

% 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  |
+-------------------+----------------------------+

Re-enable the Multi-AZ deployment with the command modify-db-instance

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

The modification might take a while, which can be monitored through 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  |
+------------+-----------+-------------+

Confirm the standby instances created with 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  |
+------------+--------------+-------------+

What do we have now?

We have moved the standby instance in the Secondary Availability Zone from the public subnet to the isolated subnet.

Multi-AZ RDS with the primary in the public subnet and standby in the isolated subnet

Step 2: Promote the standby and move the old primary

Take note of the RDS endpoint to make sure the endpoint address does not change after failover to ensure availability:

% 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  |
+---------------------------------------------------------+-------------+-------------+-------------+

Note: Make sure you reboot your instance during maintenance windows or low peak hours due to the unavailability of the instances during the reboot.

Reboot the RDS instance with the parameter --force-failover :

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

Confirm the failover with 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  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

Remove the new standby (old primary) that is in the public subnet

With the new primary in the isolated subnet, proceed to remove the newly demoted standby instance

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

The RDS instance will now be in the modifyingstatus:

% 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  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

Once the RDS instance is in availablestatus, the standby subnet should be freed up for the next step:

% 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  |
+---------------------------------------------------------+----------+-------------+----------+-------------+

Proceed to modify the RDS subnet group to change the public subnet to the isolated subnet:

% 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"
        ]
    }
}

Redeploy the multi-AZ RDS:

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

The RDS instance will be in  modifying status with the MultiAZ values pending modification:

% 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                                      ||
|+------------------------------------------------------------+--------------------------------------------+|

Once the RDS Instance is available, the new standby will be deployed in the isolated subnet:

% 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  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

What do we have now?

We have moved the standby instance in the Secondary Availability Zone from the public subnet to the isolated subnet.

Mutli-AZ RDS in the isolated subnets

Mutli-AZ RDS in the isolated subnets

Step 3: Update the CDK stack

Now that you have the actual architecture change, you should update your CDK template to reflect the changes as well. Modify the CDK stack by updating the  subnetType to  PRIVATE_ISOLATED

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

After the offline update of the CDK template, but if you want to ensure a closed loop between the CDK template and the actual stack, do an cdk deploy rds-stackafter the change. Because the real subnet group has been updated, there are no materialized changes.  cdk deploy rds-stack should return successfully:

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

If you are skeptical about the CDK state synchronization, try to deploy the stack with const subnetType: cdk.aws_ec2.SubnetSelection = { subnetType: cdk.aws_ec2.SubnetType.PUBLIC };.

cdk deploy rds-stack should encounter the same subnet in-use error: 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

Conclusion

Hosting the Amazon RDS in an isolated environment without internet egress routing offers a range of critical benefits for data security, privacy, and operational integrity:

  • Protection from Data Exfiltration: By preventing internet egress routing, the risk of data exfiltration is significantly reduced. Sensitive data remains within the controlled environment, minimizing the chances of accidental or intentional data leakage. This is especially important for compliance with data protection regulations and safeguarding proprietary information.
  • Preservation of Operational Integrity: Hosting a database in an isolated environment ensures that the operational integrity of the database is maintained. Unintended internet access, whether due to misconfigurations or vulnerabilities, can lead to unexpected behavior, data corruption, or unauthorized data changes.
  • Compliance and Regulatory Alignment: Many industries are subject to stringent compliance regulations that demand strict controls over data handling and storage. Network isolating the database supports compliance efforts such as towards compliance with ISO27001:2013 A.13.1.3.
  • Reduction of Attack Surface: An isolated environment reduces the attack surface of the database, making it less susceptible to various types of attacks. Without a direct internet connection, attackers have fewer opportunities to exploit vulnerabilities, inject malicious code, or perform reconnaissance.
  • Focus on Internal Threats: While external threats are a concern, internal threats can be equally damaging. Isolating the database limits access to authorized personnel only, reducing the potential for insider threats, accidental data exposure, or data tampering.

Subscribe to updates, news and more.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related blogs

Connect With Us