AWS CDKでAmazon Aurora DBクラスターのCAを指定したいな
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKでAmazon Aurora DBクラスターの認証局(CA)を指定したいなと思ったことはありますか? 私はあります。
Aurora DBクラスターのデフォルトのCAはrds-ca-2019
です。
以下記事で紹介している通り、rds-ca-2019
のCAの失効日は2024/8/23です。
そのため、どうせならrds-ca-rsa2048-g1
など失効期限までの猶予がより長いCAを使いたいところです。
ただし、AWS CDKのL2 ConstructではCAを指定することができません。その対応としてみんな大好きEscape hatchを使うことになります。DevelopesIOでもそのような記事が紹介されています。
しかし、以下記事で紹介しているようなAurora Serverless v2をL2 Constructで定義した場合、上述の記事で紹介されている方法では対応できませんでした。
以下、その理由と対応方法を紹介します。
2023/9/25 : AWS CDK v2.97.0からL2 ConstructでDBクラスターのインスタンスに対してCAを設定できるようになりました。
new DatabaseCluster(this, 'Database', {
engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_01_0 }),
writer: rds.ClusterInstance.provisioned('writer', {
caCertificate: rds.CaCertificate.RDS_CA_RDS2048_G1,
}),
readers: [
rds.ClusterInstance.serverlessV2('reader', {
caCertificate: rds.CaCertificate.of('custom-ca'),
}),
],
vpc,
});
いきなりまとめ
- DBクラスターの新APIと旧APIとでIDやプロパティが指す値が変わった
instanceIdentifiers
やinstanceEndpoints
はReaderの情報しか返さなくなった- DBクラスター内のDBインスタンスのIDが
Instance${instanceIndex}
などの連番ではなく、ClusterInstance.provisioned
やClusterInstance.serverlessV2
のIDが使われるようになった
- DBクラスターの子ConstructのdefaultChildの型を確認して、DBインスタンスであればCAを指定することで対応
Aurora Serverless v2をL2 Constructで定義した場合に上手くCAが設定できない理由
Aurora Serverless v2をL2 Constructで定義した場合に上手くCAが設定できない理由は、新APIと旧APIとでIDやプロパティが指す値が変わったためです。
上述で紹介した記事ではCAを指定する際、instanceIdentifiers
からDBクラスター内のインスタンス数分ループしてInstance${instanceIndex}
であるConstructを引っ張ってきています。
const aurora = new rds.DatabaseCluster(this, "aurora", {
engine: rds.DatabaseClusterEngine.auroraMysql({
version: rds.AuroraMysqlEngineVersion.VER_3_03_0
}),
instanceProps: {
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.MEDIUM),
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
},
removalPolicy: cdk.RemovalPolicy.DESTROY, // for experimental
});
for (let i = 1; i <= aurora.instanceIdentifiers.length; i++) {
const instance = aurora.node.findChild(`Instance${i}`) as rds.CfnDBInstance;
instance.addPropertyOverride("CACertificateIdentifier", "rds-ca-rsa2048-g1");
}
新APIでは旧APIからDBクラスター内のDBインスタンスの作成方法が変わりました。
その影響か新APIではinstanceIdentifiers
やinstanceEndpoints
はReaderの情報しか返さなくなりました。
新APIのDBクラスター内のDBインスタンスを作成方法はcluster.ts の _createInstancesを確認しましょう。
/**
* Create cluster instances
*
* @internal
*/
protected _createInstances(props: DatabaseClusterProps): InstanceConfig {
const instanceEndpoints: Endpoint[] = [];
const instanceIdentifiers: string[] = [];
const readers: IAuroraClusterInstance[] = [];
// need to create the writer first since writer is determined by what instance is first
const writer = props.writer!.bind(this, this, {
monitoringInterval: props.monitoringInterval,
monitoringRole: props.monitoringRole,
removalPolicy: props.removalPolicy ?? RemovalPolicy.SNAPSHOT,
subnetGroup: this.subnetGroup,
promotionTier: 0, // override the promotion tier so that writers are always 0
});
(props.readers ?? []).forEach(instance => {
const clusterInstance = instance.bind(this, this, {
monitoringInterval: props.monitoringInterval,
monitoringRole: props.monitoringRole,
removalPolicy: props.removalPolicy ?? RemovalPolicy.SNAPSHOT,
subnetGroup: this.subnetGroup,
});
readers.push(clusterInstance);
if (clusterInstance.tier < 2) {
this.validateReaderInstance(writer, clusterInstance);
}
instanceEndpoints.push(new Endpoint(clusterInstance.dbInstanceEndpointAddress, this.clusterEndpoint.port));
instanceIdentifiers.push(clusterInstance.instanceIdentifier);
});
this.validateClusterInstances(writer, readers);
return {
instanceEndpoints,
instanceIdentifiers,
};
}
props.readers
がある場合のみinstanceEndpoints
やinstanceIdentifiers
にpushしているのが分かりますね。
そのため、新APIではループのさせ方を変える必要があります。
続いてIDも変わりました。
aws-cdk/cluster.ts の ClusterInstanceとAuroraClusterInstance確認しましょう。
aws-cdk/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts
/**
* Create an RDS Aurora Cluster Instance. You can create either provisioned or
* serverless v2 instances.
*
* @example
*
* declare const vpc: ec2.Vpc;
* const cluster = new rds.DatabaseCluster(this, 'Database', {
* engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_08_1 }),
* writer: rds.ClusterInstance.provisioned('writer', {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.R6G, ec2.InstanceSize.XLARGE4),
* }),
* serverlessV2MinCapacity: 6.5,
* serverlessV2MaxCapacity: 64,
* readers: [
* // will be put in promotion tier 1 and will scale with the writer
* rds.ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true }),
* // will be put in promotion tier 2 and will not scale with the writer
* rds.ClusterInstance.serverlessV2('reader2'),
* ]
* vpc,
* });
*/
export class ClusterInstance implements IClusterInstance {
/**
* Add a provisioned instance to the cluster
*
* @example
* ClusterInstance.provisioned('ClusterInstance', {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.R6G, ec2.InstanceSize.XLARGE4),
* });
*/
public static provisioned(id: string, props: ProvisionedClusterInstanceProps = {}): IClusterInstance {
return new ClusterInstance(id, {
...props,
instanceType: ClusterInstanceType.provisioned(props.instanceType),
});
}
/**
* Add a serverless v2 instance to the cluster
*
* @example
* ClusterInstance.serverlessV2('ClusterInstance', {
* scaleWithWriter: true,
* });
*/
public static serverlessV2(id: string, props: ServerlessV2ClusterInstanceProps = {}): IClusterInstance {
return new ClusterInstance(id, {
...props,
promotionTier: props.scaleWithWriter ? 1 : 2,
instanceType: ClusterInstanceType.serverlessV2(),
});
}
private constructor(private id: string, private readonly props: ClusterInstanceProps) { }
/**
* Add the ClusterInstance to the cluster
*/
public bind(scope: Construct, cluster: IDatabaseCluster, props: ClusterInstanceBindOptions): IAuroraClusterInstance {
return new AuroraClusterInstance(scope, this.id, {
cluster,
...this.props,
...props,
});
}
}
aws-cdk/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts
class AuroraClusterInstance extends Resource implements IAuroraClusterInstance {
public readonly dbInstanceArn: string;
public readonly dbiResourceId: string;
public readonly dbInstanceEndpointAddress: string;
public readonly instanceIdentifier: string;
public readonly type: InstanceType;
public readonly tier: number;
public readonly instanceSize?: string;
constructor(scope: Construct, id: string, props: AuroraClusterInstanceProps) {
super(
scope,
props.isFromLegacyInstanceProps ? `${id}Wrapper` : id,
{
physicalName: props.instanceIdentifier,
});
this.tier = props.promotionTier ?? 2;
if (this.tier > 15) {
throw new Error('promotionTier must be between 0-15');
}
const isOwnedResource = Resource.isOwnedResource(props.cluster);
let internetConnected;
let publiclyAccessible = props.publiclyAccessible;
if (isOwnedResource) {
const ownedCluster = props.cluster as DatabaseCluster;
internetConnected = ownedCluster.vpc.selectSubnets(ownedCluster.vpcSubnets).internetConnectivityEstablished;
publiclyAccessible = ownedCluster.vpcSubnets && ownedCluster.vpcSubnets.subnetType === ec2.SubnetType.PUBLIC;
}
// Get the actual subnet objects so we can depend on internet connectivity.
const instanceType = (props.instanceType ?? ClusterInstanceType.serverlessV2());
this.type = instanceType.type;
this.instanceSize = this.type === InstanceType.PROVISIONED ? props.instanceType?.toString() : undefined;
// engine is never undefined on a managed resource, i.e. DatabaseCluster
const engine = props.cluster.engine!;
const enablePerformanceInsights = props.enablePerformanceInsights
|| props.performanceInsightRetention !== undefined || props.performanceInsightEncryptionKey !== undefined;
if (enablePerformanceInsights && props.enablePerformanceInsights === false) {
throw new Error('`enablePerformanceInsights` disabled, but `performanceInsightRetention` or `performanceInsightEncryptionKey` was set');
}
const instanceParameterGroup = props.parameterGroup ?? (
props.parameters
? new ParameterGroup(props.cluster, 'InstanceParameterGroup', {
engine: engine,
parameters: props.parameters,
})
: undefined
);
const instanceParameterGroupConfig = instanceParameterGroup?.bindToInstance({});
const instance = new CfnDBInstance(
props.isFromLegacyInstanceProps ? scope : this,
props.isFromLegacyInstanceProps ? id : 'Resource',
{
// Link to cluster
engine: engine.engineType,
dbClusterIdentifier: props.cluster.clusterIdentifier,
promotionTier: props.isFromLegacyInstanceProps ? undefined : this.tier,
dbInstanceIdentifier: this.physicalName,
// Instance properties
dbInstanceClass: props.instanceType ? databaseInstanceType(instanceType) : undefined,
publiclyAccessible,
enablePerformanceInsights: enablePerformanceInsights || props.enablePerformanceInsights, // fall back to undefined if not set
performanceInsightsKmsKeyId: props.performanceInsightEncryptionKey?.keyArn,
performanceInsightsRetentionPeriod: enablePerformanceInsights
? (props.performanceInsightRetention || PerformanceInsightRetention.DEFAULT)
: undefined,
// only need to supply this when migrating from legacy method.
// this is not applicable for aurora instances, but if you do provide it and then
// change it it will cause an instance replacement
dbSubnetGroupName: props.isFromLegacyInstanceProps ? props.subnetGroup?.subnetGroupName : undefined,
dbParameterGroupName: instanceParameterGroupConfig?.parameterGroupName,
monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(),
monitoringRoleArn: props.monitoringRole && props.monitoringRole.roleArn,
autoMinorVersionUpgrade: props.autoMinorVersionUpgrade,
allowMajorVersionUpgrade: props.allowMajorVersionUpgrade,
});
// For instances that are part of a cluster:
//
// Cluster DESTROY or SNAPSHOT -> DESTROY (snapshot is good enough to recreate)
// Cluster RETAIN -> RETAIN (otherwise cluster state will disappear)
instance.applyRemovalPolicy(helperRemovalPolicy(props.removalPolicy));
// We must have a dependency on the NAT gateway provider here to create
// things in the right order.
if (internetConnected) {
instance.node.addDependency(internetConnected);
}
this.dbInstanceArn = this.getResourceArnAttribute(instance.attrDbInstanceArn, {
resource: 'db',
service: 'rds',
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
resourceName: this.physicalName,
});
this.instanceIdentifier = this.getResourceNameAttribute(instance.ref);
this.dbiResourceId = instance.attrDbiResourceId;
this.dbInstanceEndpointAddress = instance.attrEndpointAddress;
}
}
Instance${instanceIndex}
などの連番ではなく、ClusterInstance.provisioned
やClusterInstance.serverlessV2
のIDがそのまま使われていることが分かります。
対応してみる
対応箇所が分かったので、新APIの対応版を作成してみます。
まず、適当にAurora DBクラスターを作成します。
./lib/constructs/aurora.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export interface AuroraProps {
vpc: cdk.aws_ec2.IVpc;
securityGroup: cdk.aws_ec2.ISecurityGroup;
}
export class Aurora extends Construct {
constructor(scope: Construct, id: string, props: AuroraProps) {
super(scope, id);
// DB Cluster Parameter Group
const dbClusterParameterGroup = new cdk.aws_rds.ParameterGroup(
this,
"DbClusterParameterGroup",
{
engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
}),
description: "aurora-postgresql15",
parameters: {
log_statement: "none",
"pgaudit.log": "all",
"pgaudit.role": "rds_pgaudit",
shared_preload_libraries: "pgaudit",
},
}
);
// DB Parameter Group
const dbParameterGroup = new cdk.aws_rds.ParameterGroup(
this,
"DbParameterGroup",
{
engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
}),
description: "aurora-postgresql15",
}
);
// Subnet Group
const subnetGroup = new cdk.aws_rds.SubnetGroup(this, "SubnetGroup", {
description: "description",
vpc: props.vpc,
subnetGroupName: "SubnetGroup",
vpcSubnets: props.vpc.selectSubnets({
onePerAz: true,
subnetType: cdk.aws_ec2.SubnetType.PRIVATE_ISOLATED,
}),
});
// Monitoring Role
const monitoringRole = new cdk.aws_iam.Role(this, "MonitoringRole", {
assumedBy: new cdk.aws_iam.ServicePrincipal(
"monitoring.rds.amazonaws.com"
),
managedPolicies: [
cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
"service-role/AmazonRDSEnhancedMonitoringRole"
),
],
});
// DB Cluster
const dbCluster = new cdk.aws_rds.DatabaseCluster(this, "Default", {
engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
}),
writer: cdk.aws_rds.ClusterInstance.provisioned("InstanceA", {
instanceType: cdk.aws_ec2.InstanceType.of(
cdk.aws_ec2.InstanceClass.T3,
cdk.aws_ec2.InstanceSize.MEDIUM
),
allowMajorVersionUpgrade: false,
autoMinorVersionUpgrade: true,
enablePerformanceInsights: true,
parameterGroup: dbParameterGroup,
performanceInsightRetention:
cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
publiclyAccessible: false,
instanceIdentifier: "db-instance-a",
}),
readers: [
cdk.aws_rds.ClusterInstance.serverlessV2("InstanceB", {
autoMinorVersionUpgrade: false,
allowMajorVersionUpgrade: false,
enablePerformanceInsights: false,
parameterGroup: dbParameterGroup,
publiclyAccessible: false,
scaleWithWriter: true,
instanceIdentifier: "db-instance-b",
}),
],
backup: {
retention: cdk.Duration.days(7),
preferredWindow: "16:00-16:30",
},
cloudwatchLogsExports: ["postgresql"],
cloudwatchLogsRetention: cdk.aws_logs.RetentionDays.ONE_YEAR,
clusterIdentifier: "db-cluster",
copyTagsToSnapshot: true,
defaultDatabaseName: "testDB",
deletionProtection: false,
iamAuthentication: false,
monitoringInterval: cdk.Duration.minutes(1),
monitoringRole,
parameterGroup: dbClusterParameterGroup,
preferredMaintenanceWindow: "Sat:17:00-Sat:17:30",
storageEncrypted: true,
vpc: props.vpc,
securityGroups: [props.securityGroup],
subnetGroup,
});
// Manage master user password
const cfnDbCluster = dbCluster.node
.defaultChild as cdk.aws_rds.CfnDBCluster;
cfnDbCluster.manageMasterUserPassword = true;
cfnDbCluster.masterUsername = "postgresAdmin";
cfnDbCluster.addPropertyDeletionOverride("MasterUserPassword");
dbCluster.node.tryRemoveChild("Secret");
dbCluster.node.children.forEach((children) => {
console.log(`DB Cluster children id : ${children.node.id}`);
});
}
}
デプロイする際、DBクラスターの子ConstructのIDを出力するようにしてみました。
$ npx cdk deploy
DB Cluster children id : Resource
DB Cluster children id : LogRetentionpostgresql
DB Cluster children id : InstanceA
DB Cluster children id : InstanceB
✨ Synthesis time: 7.41s
.
.
(以下略)
.
.
ClusterInstance.provisioned (InstanceA)
やClusterInstance.serverlessV2 (InstanceB)
のIDとなっているのがDBクラスター内のDBインスタンスのConstructですね。
デプロイ後、DBインスタンスのCAを確認します。
rds-ca-2019
となっていますね。(なぜかWriter / Readerが定義したものと逆になっていますが、ここでは突っ込まないでおきます)
AWS CLIでも確認しておきます。
$ aws rds describe-db-instances \
--filters Name=db-cluster-id,Values=db-cluster \
--query 'DBInstances[*].CACertificateIdentifier'
[
"rds-ca-2019",
"rds-ca-2019"
]
それでは、DBインスタンスにCArds-ca-rsa4096-g1
を指定するように変更してみます。
変更箇所は以下のとおりです。
// CA
dbCluster.node.children.forEach((children) => {
if (children.node.defaultChild instanceof cdk.aws_rds.CfnDBInstance) {
(
children.node.defaultChild as cdk.aws_rds.CfnDBInstance
).addPropertyOverride("CACertificateIdentifier", "rds-ca-rsa4096-g1");
}
});
DBクラスターの子ConstructのdefaultChildがCfnDBInstance
かチェックし、CfnDBInstance
であればCAをEscape hatchで指定します。
編集後、diffを確認します。
$ npx cdk diff
Stack AuroraStack
Resources
[~] AWS::RDS::DBInstance Aurora/Default/InstanceA AuroraInstanceA387C8329
└─ [+] CACertificateIdentifier
└─ rds-ca-rsa4096-g1
[~] AWS::RDS::DBInstance Aurora/Default/InstanceB AuroraInstanceBE4D8957F
└─ [+] CACertificateIdentifier
└─ rds-ca-rsa4096-g1
CArds-ca-rsa4096-g1
が設定されそうですね。
デプロイ後、本当にCAがrds-ca-rsa4096-g1
となっているか確認します。
CAがrds-ca-rsa4096-g1
になっていますね。
AWS CLIでも確認しておきます。
$ aws rds describe-db-instances \
--filters Name=db-cluster-id,Values=db-cluster \
--query 'DBInstances[*].CACertificateIdentifier'
[
"rds-ca-rsa4096-g1",
"rds-ca-rsa4096-g1"
]
SSL/TLSを使った接続確認
Amazon Linux 2023からDBクラスターにSSL/TLSで接続できるか確認します。
# PostgreSQL 15のクライアントのインストール
$ sudo dnf install postgresql15 -y
Last metadata expiration check: 0:41:27 ago on Tue Jun 6 21:13:01 2023.
Dependencies resolved.
==========================================================================================================================
Package Architecture Version Repository Size
==========================================================================================================================
Installing:
postgresql15 x86_64 15.0-1.amzn2023.0.2 amazonlinux 1.6 M
Installing dependencies:
postgresql15-private-libs x86_64 15.0-1.amzn2023.0.2 amazonlinux 143 k
Transaction Summary
==========================================================================================================================
Install 2 Packages
Total download size: 1.8 M
Installed size: 6.5 M
Downloading Packages:
(1/2): postgresql15-private-libs-15.0-1.amzn2023.0.2.x86_64.rpm 1.3 MB/s | 143 kB 00:00
(2/2): postgresql15-15.0-1.amzn2023.0.2.x86_64.rpm 9.0 MB/s | 1.6 MB 00:00
--------------------------------------------------------------------------------------------------------------------------
Total 7.3 MB/s | 1.8 MB 00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : postgresql15-private-libs-15.0-1.amzn2023.0.2.x86_64 1/2
Installing : postgresql15-15.0-1.amzn2023.0.2.x86_64 2/2
Running scriptlet: postgresql15-15.0-1.amzn2023.0.2.x86_64 2/2
Verifying : postgresql15-15.0-1.amzn2023.0.2.x86_64 1/2
Verifying : postgresql15-private-libs-15.0-1.amzn2023.0.2.x86_64 2/2
Installed:
postgresql15-15.0-1.amzn2023.0.2.x86_64 postgresql15-private-libs-15.0-1.amzn2023.0.2.x86_64
Complete!
# シークレットから認証情報を取得
$ get_secrets_value=$(aws secretsmanager get-secret-value \
--secret-id 'rds!cluster-8a84d67a-61f3-421d-8cd4-aaa0c1877f56' \
--region us-east-1 \
| jq -r .SecretString)
# 環境変数に埋め込み
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)
$ export PGHOST=db-cluster.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com
$ export PGPORT=5432
$ export PGDATABASE=testDB
$ psql
psql (15.0, server 15.2)
SSL connection (protocol: TLSv1.2, cipher: AES128-SHA256, compression: off)
Type "help" for help.
# sslinfo のエクステンションを作成
testDB=> create extension sslinfo;
CREATE EXTENSION
# SSL/TLSを使用しているか確認
testDB=> select ssl_is_used();
ssl_is_used
-------------
t
(1 row)
# 使用しているSSL/TLSの暗号スイートの確認
testDB=> select ssl_cipher();
ssl_cipher
---------------
AES128-SHA256
(1 row)
バナーなどからTLS 1.2 (AES128-SHA256) で接続していることが分かります。
SSL/TLS周りの情報を確認します。
testDB=> SELECT name as "Parameter name", setting as value, short_desc FROM pg_settings WHERE name LIKE '%ssl%';
Parameter name | value
| short_desc
----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------
ssl | on
| Enables SSL connections.
ssl_ca_file | /rdsdbdata/rds-metadata/ca-cert.pem
| Location of the SSL certificate authority file.
ssl_cert_file | /rdsdbdata/rds-metadata/server-cert.pem
| Location of the SSL server certificate file.
ssl_ciphers | TLS_RSA_WITH_AES_128_CBC_SHA256:DHE-RSA-AES128-SHA:TLS_RSA_WITH_AES_128_GCM_SHA256:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-GCM-SHA384:TLS_RSA_WITH_AES_256_GCM_SHA384:DHE-RSA-AES128-GCM-SHA256:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:DHE-RSA-AES256-SHA256:ECDHE-RSA-AES256-SHA384:TLS_RSA_WITH_AES_256_CBC_SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA2
56:DHE-RSA-AES128-SHA256:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES256-SHA:TLS_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | Sets the list of allowed SSL ciphers.
ssl_crl_dir | /rdsdbdata/rds-metadata/ssl_crl_dir/
| Location of the SSL certificate revocation list directory.
ssl_crl_file |
| Location of the SSL certificate revocation list file.
ssl_dh_params_file |
| Location of the SSL DH parameters file.
ssl_ecdh_curve | prime256v1
| Sets the curve to use for ECDH.
ssl_key_file | /rdsdbdata/rds-metadata/server-key.pem
| Location of the SSL server private key file.
ssl_library | OpenSSL
| Shows the name of the SSL library.
ssl_max_protocol_version | TLSv1.2
| Sets the maximum SSL/TLS protocol version to use.
ssl_min_protocol_version | TLSv1.2
| Sets the minimum SSL/TLS protocol version to use.
ssl_passphrase_command |
| Command to obtain passphrases for SSL.
ssl_passphrase_command_supports_reload | off
| Controls whether ssl_passphrase_command is called during server reload.
ssl_prefer_server_ciphers | on
| Give priority to server ciphersuite order.
(15 rows)
デフォルトではTLS 1.2しか使用しないようですね。
Aurora PostgreSQLのSSL/TLS周りの情報は以下AWS公式ドキュメントにまとまっています。
せっかくなので、より強い256bit暗号を使うように設定変更してみましょう。
Amazon Linux 2023にデフォルトでインストールされているOpenSSLがサポートしている暗号化スイートを確認します。
$ openssl ciphers -v 'HIGH:!ADH:!MD5:!RC4:!SRP:!PSK:!DSS:!ECDHE:!ECDSA:!EDH:!DH:!ECDH:!CAMELLIA256'
TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any Au=any Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESGCM(128) Mac=AEAD
TLS_AES_128_CCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESCCM(128) Mac=AEAD
AES256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(256) Mac=AEAD
AES256-CCM8 TLSv1.2 Kx=RSA Au=RSA Enc=AESCCM8(256) Mac=AEAD
AES256-CCM TLSv1.2 Kx=RSA Au=RSA Enc=AESCCM(256) Mac=AEAD
ARIA256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=ARIAGCM(256) Mac=AEAD
AES128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(128) Mac=AEAD
AES128-CCM8 TLSv1.2 Kx=RSA Au=RSA Enc=AESCCM8(128) Mac=AEAD
AES128-CCM TLSv1.2 Kx=RSA Au=RSA Enc=AESCCM(128) Mac=AEAD
ARIA128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=ARIAGCM(128) Mac=AEAD
AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256
AES128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256
CAMELLIA128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=Camellia(128) Mac=SHA256
AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1
AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1
CAMELLIA128-SHA SSLv3 Kx=RSA Au=RSA Enc=Camellia(128) Mac=SHA1
今回はAES256-GCM-SHA384
を使ってみましょう。
DBクラスターのパラメーターグループを変更します。
// DB Cluster Parameter Group
const dbClusterParameterGroup = new cdk.aws_rds.ParameterGroup(
this,
"DbClusterParameterGroup",
{
engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
}),
description: "aurora-postgresql15",
parameters: {
log_statement: "none",
"pgaudit.log": "all",
"pgaudit.role": "rds_pgaudit",
shared_preload_libraries: "pgaudit",
ssl_ciphers: "TLS_RSA_WITH_AES_256_GCM_SHA384",
},
}
);
修正後、diffを確認します。
$ npx cdk diff
Stack AuroraStack
Resources
[~] AWS::RDS::DBClusterParameterGroup Aurora/DbClusterParameterGroup AuroraDbClusterParameterGroupC83D24F1
└─ [~] Parameters
└─ [+] Added: .ssl_ciphers
デプロイ後、再度DBクラスターに接続します。
$ psql
psql (15.0, server 15.2)
SSL connection (protocol: TLSv1.2, cipher: AES256-GCM-SHA384, compression: off)
Type "help" for help.
testDB=> select ssl_cipher();
ssl_cipher
-------------------
AES256-GCM-SHA384
(1 row)
testDB=> SELECT name as "Parameter name", setting as value, short_desc FROM pg_settings WHERE name LIKE 'ssl_ciphers';
Parameter name | value | short_desc
----------------+---------------------------------+---------------------------------------
ssl_ciphers | TLS_RSA_WITH_AES_256_GCM_SHA384 | Sets the list of allowed SSL ciphers.
(1 row)
設定したとおりTLS_RSA_WITH_AES_256_GCM_SHA384
で接続していることが確認できました。
証明書の検証ができるかも確認しておきます。
# すべての AWS リージョン の中間証明書とルート証明書の両方を含む証明書バンドルのダウンロード
$ wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
--2023-06-06 22:14:36-- https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
Resolving truststore.pki.rds.amazonaws.com (truststore.pki.rds.amazonaws.com)... 18.165.98.93, 18.165.98.60, 18.165.98.84, ...
Connecting to truststore.pki.rds.amazonaws.com (truststore.pki.rds.amazonaws.com)|18.165.98.93|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 174184 (170K) [application/octet-stream]
Saving to: ‘global-bundle.pem’
global-bundle.pem 100%[==============================================>] 170.10K --.-KB/s in 0.007s
2023-06-06 22:14:37 (23.0 MB/s) - ‘global-bundle.pem’ saved [174184/174184]
# 証明書を検証して接続
$ psql "sslrootcert=global-bundle.pem sslmode=verify-full"
psql (15.0, server 15.2)
SSL connection (protocol: TLSv1.2, cipher: AES256-GCM-SHA384, compression: off)
Type "help" for help.
testDB=>
問題なく接続できましたね。
Construct treeを制するものがAWS CDKを制する
AWS CDKで新API版のAmazon Aurora DBクラスターのCAを指定してみました
「Construct treeを制するものがAWS CDKを制する」ような気がしてきました。APIに変更があったときは必ずdiffをして影響範囲を調べることが重要ですね。
使用したコードは以下リポジトリに保存しています。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!