Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Proteggere i dati: spostare Amazon RDS da una subnet pubblica a una isolata

By Dr. Richard KangNov 1, 202312 min read

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

(Immagine creata con il supporto di Stable Diffusion)

Mi capita spesso di lavorare con clienti che ospitano il database Amazon RDS in una subnet VPC con una route predefinita verso Internet.

In questi scenari possono essere attive diverse misure di sicurezza: l'opzione "publicly accessible" di RDS disabilitata, regole inbound del Security Group ristrette alle sole istanze EC2 o Lambda autorizzate e regole stateful del firewall che rilevano minacce ed exploit tramite firme SQL. Tuttavia, episodi come la violazione dei dati di Target Corporation del 2013 dimostrano che bloccare il traffico Internet in ingresso lasciando però libero quello in uscita non basta più a contenere il rischio di esfiltrazione dei dati. Un attore malevolo come un malware command-and-control (noto anche come C&C o malware C2) può sfruttare il traffico in uscita su porte di uso comune quali HTTP:80 e HTTPS:443 per il beaconing.

Questo articolo illustra i passaggi per spostare un'istanza Amazon RDS da una subnet pubblica con tabella di routing connessa a Internet a una subnet isolata, senza dover creare una nuova istanza DB tramite snapshot-restore.

Una VPC può contenere 3 tipi di subnet: una subnet pubblica, dotata di un internet gateway che consente sia il traffico Internet in entrata sia quello in uscita; una subnet privata, con un Network Address Translation (NAT) o un Internet Egress-only gateway che consente il traffico Internet in uscita; una subnet isolata, che ammette solo il traffico interno alla VPC e non ha route verso destinazioni esterne.

Ospitare il database in una subnet isolata contribuisce a ridurre il rischio di esfiltrazione dei dati, perché diminuisce la superficie d'attacco e rende più difficile per un attaccante, ad esempio un malware C&C, individuare una via per portare i dati all'esterno.

Va però precisato che l'isolamento di rete è solo una delle misure di sicurezza utili a ridurre il rischio, non una soluzione infallibile contro l'esfiltrazione dei dati. Una catena è forte quanto il suo anello più debole: ogni accorgimento adottato per ridurre il rischio fa la differenza.

Configurazione dell'ambiente di test con Amazon RDS Multi-AZ

Per seguire i passaggi necessari a spostare l'istanza RDS da una subnet pubblica a una isolata occorre predisporre un ambiente di test composto da un'istanza Amazon RDS nelle subnet pubbliche di una VPC. La VPC, a sua volta, comprende sia subnet pubbliche sia subnet isolate.

Chi dispone già di istanze Amazon RDS in subnet pubbliche e di una VPC con subnet isolate può saltare la fase di creazione della VPC.

Creeremo le risorse AWS con CDK. Per chi è alle prime armi con CDK è disponibile la guida introduttiva: https://docs.aws.amazon.com/cdk/v2/guide/hello_world.htm.

Stack CDK per la creazione di una VPC

Il primo passo per allestire l'ambiente di test consiste nel creare una VPC con una subnet pubblica e una isolata in ciascuna Availability Zone, distribuite su due Availability Zone, utilizzando lo stack CDK riportato di seguito.

Esporteremo i parametri necessari della VPC, come l'ID della VPC, per poterli importare nello stack che creerà l'istanza RDS al passo successivo.

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

Se cdk deploy vpc-stack va a buon fine, l'output dovrebbe essere simile al seguente:

✅  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

Annotare l'ID della VPC, gli ID delle subnet pubbliche e gli ID delle subnet isolate.

In questo esempio utilizzeremo i seguenti parametri per creare l'istanza Amazon RDS:

  • ID VPC: vpc-090949477d23289ce
  • ID subnet pubbliche: subnet-0271290b1164913ad, subnet-079b9aaba653c430b
  • ID subnet isolate: subnet-06721c9fdaac5f333, subnet-03e58f5428463ad26

Stack CDK per la creazione di Amazon RDS

Procediamo ora al provisioning di un'istanza Amazon Postgres RDS nella subnet pubblica della VPC:

Suggerimento rapido su CDK: cdk.Fn.importValue() è un attributo a binding tardivo. Occorre quindi usare la funzione di split di stringhe di CloudFormation cdk.Fn.Split() anziché lo split di stringhe di Typescript come cdk.Fn.importValue('test-Vpc-AZs').split(","), che genererebbe errori del tipo 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"
});

Per creare l'istanza RDS in una VPC esistente è sufficiente modificare vpcLookup con i valori pertinenti, ad esempio:

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

Se cdk deploy rds-stack va a buon fine, l'output dovrebbe essere simile al seguente:

 ✅  rds-stack

✨  Deployment time: 20.95s

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

Cosa abbiamo creato?

Abbiamo creato una VPC con due Availability Zone. Ciascuna zona dispone di una subnet pubblica e di una isolata.

Abbiamo inoltre eseguito il provisioning di un'istanza Amazon RDS Multi-AZ nella subnet pubblica di entrambe le Availability Zone.

RDS Multi-AZ in subnet pubbliche

Spostare Amazon RDS dalla subnet pubblica alle subnet isolate

Disponiamo di un RDS Subnet Group composto da più subnet, una per ciascuna Availability Zone: ad esempio, l'istanza primaria in AZ-1 e l'istanza di standby in AZ-2.

Per modificare la subnet di una singola istanza occorre però rimuovere prima l'istanza che la utilizza; in caso contrario, modificando una subnet con un'istanza RDS ancora attiva, si verifica un errore:

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

Panoramica dei passaggi per spostare Amazon RDS dalla subnet pubblica alla subnet isolata:

  1. Arrestare l'istanza di standby per liberare la subnet pubblica, in modo che l'RDS subnet group possa sostituire quella subnet pubblica con una isolata, prima di ridistribuire l'istanza di standby nella nuova subnet isolata.
  2. Promuovere lo standby a primaria e ripetere lo stesso processo sulla nuova istanza di standby appena retrocessa.
  3. Aggiornare lo stato di CDK.

Passaggio 1: spostare l'istanza di standby

Prima di iniziare occorre individuare alcuni parametri chiave:

Annotare le subnet delle istanze RDS Primary e Standby

Eseguire questo comando AWS CLI per interrogare le Availability Zone Primary e Secondary:

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

Mappare le Availability Zone alle subnet dell'RDS Subnet Group con il comando CLI:

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

Da questi 2 output possiamo concludere che:

  • L'istanza RDS primaria si trova in subnet-0271290b1164913ad.
  • L'istanza RDS di standby si trova in subnet-079b9aaba653c430b.

Arrestare l'istanza di standby e modificare la subnet

Modificare l'istanza RDS con il comando CLI modify-db-instance come segue:

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

Per verificare che l'istanza RDS sia diventata single-AZ, eseguire di nuovo il comando CLI describe-db-instances:

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

Aggiornare l'RDS Subnet Group sostituendo la subnet pubblica dello standby ormai terminato con una subnet isolata della stessa 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"\
        ]
    }
}

Verificare l'RDS Subnet Group con il comando describe-db-subnet-groups:

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

Riattivare la distribuzione Multi-AZ con il comando modify-db-instance:

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

La modifica può richiedere del tempo e si può monitorare tramite 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  |
+------------+-----------+-------------+

Verificare la creazione delle istanze di standby con 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  |
+------------+--------------+-------------+

A che punto siamo?

Abbiamo spostato l'istanza di standby della Secondary Availability Zone dalla subnet pubblica alla subnet isolata.

RDS Multi-AZ con istanza primaria nella subnet pubblica e standby nella subnet isolata

Passaggio 2: promuovere lo standby e spostare la vecchia istanza primaria

Annotare l'endpoint RDS per assicurarsi che il suo indirizzo non cambi dopo il failover, salvaguardando così la continuità del servizio:

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

Nota: si consiglia di riavviare l'istanza durante le finestre di manutenzione o nelle ore di minor traffico, poiché durante il riavvio l'istanza non sarà disponibile.

Riavviare l'istanza RDS con il parametro --force-failover:

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

Confermare il failover con 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  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

Rimuovere il nuovo standby (vecchia istanza primaria) che si trova nella subnet pubblica

Con la nuova istanza primaria nella subnet isolata, procedere alla rimozione dell'istanza di standby appena retrocessa:

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

L'istanza RDS passerà allo stato 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  |
+---------------------------------------------------------+----------+-------------+-------------+-------------+

Quando l'istanza RDS torna in stato available, la subnet di standby risulterà liberata e pronta per il passaggio successivo:

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

Procedere alla modifica dell'RDS subnet group sostituendo la subnet pubblica con la subnet isolata:

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

Ridistribuire l'RDS Multi-AZ:

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

L'istanza RDS sarà nello stato modifying, con i valori MultiAZ in attesa di modifica:

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

Una volta che l'istanza RDS torna in stato available, il nuovo standby sarà distribuito nella subnet isolata:

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

A che punto siamo ora?

Abbiamo spostato l'istanza di standby della Secondary Availability Zone dalla subnet pubblica alla subnet isolata.

RDS Multi-AZ nelle subnet isolate

Passaggio 3: aggiornare lo stack CDK

Ora che la modifica all'architettura è effettiva, occorre aggiornare anche il template CDK per riflettere i cambiamenti. Modificare lo stack CDK aggiornando subnetType in PRIVATE_ISOLATED:

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

Per garantire la coerenza tra il template CDK e lo stack reale dopo l'aggiornamento del template, è possibile eseguire un cdk deploy rds-stack dopo la modifica. Poiché il subnet group reale è già stato aggiornato, non vi saranno modifiche concrete da applicare. cdk deploy rds-stack dovrebbe completarsi con successo:

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

Per fugare ogni dubbio sulla sincronizzazione dello stato di CDK, provare a distribuire lo stack con const subnetType: cdk.aws_ec2.SubnetSelection = { subnetType: cdk.aws_ec2.SubnetType.PUBLIC };.

cdk deploy rds-stack dovrebbe restituire lo stesso errore di subnet in uso: 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

Ospitare Amazon RDS in un ambiente isolato, privo di routing di egress verso Internet, offre una serie di vantaggi fondamentali in termini di sicurezza dei dati, privacy e integrità operativa:

  • Protezione dall'esfiltrazione dei dati: eliminando il routing di egress verso Internet, il rischio di esfiltrazione si riduce in modo significativo. I dati sensibili restano all'interno di un ambiente controllato, riducendo al minimo le possibilità di fughe accidentali o intenzionali. È un aspetto particolarmente rilevante per la conformità alle normative sulla protezione dei dati e per la tutela delle informazioni proprietarie.
  • Tutela dell'integrità operativa: ospitare un database in un ambiente isolato preserva la sua integrità operativa. Un accesso a Internet non intenzionale, dovuto a configurazioni errate o a vulnerabilità, può causare comportamenti imprevisti, corruzione dei dati o modifiche non autorizzate.
  • Conformità e allineamento normativo: numerosi settori sono soggetti a regolamentazioni stringenti che impongono controlli rigorosi sulla gestione e sull'archiviazione dei dati. Isolare il database a livello di rete supporta gli sforzi di conformità, ad esempio rispetto allo standard ISO27001:2013 A.13.1.3.
  • Riduzione della superficie d'attacco: un ambiente isolato riduce la superficie d'attacco del database, rendendolo meno esposto a varie tipologie di attacco. Senza una connessione diretta a Internet, gli attaccanti hanno meno occasioni di sfruttare vulnerabilità, iniettare codice malevolo o condurre attività di ricognizione.
  • Attenzione alle minacce interne: pur essendo le minacce esterne motivo di preoccupazione, quelle interne possono essere altrettanto dannose. Isolare il database limita l'accesso al solo personale autorizzato, riducendo il rischio di insider threat, esposizione accidentale dei dati o manomissione degli stessi.