Skip to content

Commit

Permalink
fix(rds): Correct ARN in IAM policy for IAM database access (#25141)
Browse files Browse the repository at this point in the history
The [IAM policy for IAM database access](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html) takes the form of:

```
arn:aws:rds-db:region:account-id:dbuser:DbiResourceId/db-user-name
```

Following aws-cloudformation/cloudformation-coverage-roadmap#105, this change updates the ARN used in `grantConnect` to this format.

Additionally, update the signature of `grantConnect` to take an optional `dbUser`, which is defaulted to the master username of the database, obtained via the available `Secret`.

This signature change also matches `grantConnect` in `DatabaseProxy`. See #12416.

Closes #11851.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
akash1810 authored Apr 20, 2023
1 parent 5a836cb commit 227ea09
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 6 deletions.
72 changes: 67 additions & 5 deletions packages/aws-cdk-lib/aws-rds/lib/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export interface IDatabaseInstance extends IResource, ec2.IConnectable, secretsm
*/
readonly dbInstanceEndpointPort: string;

/**
* The AWS Region-unique, immutable identifier for the DB instance.
* This identifier is found in AWS CloudTrail log entries whenever the AWS KMS key for the DB instance is accessed.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html#aws-resource-rds-dbinstance-return-values
*/
readonly instanceResourceId?: string;

/**
* The instance endpoint.
*/
Expand All @@ -66,10 +74,11 @@ export interface IDatabaseInstance extends IResource, ec2.IConnectable, secretsm

/**
* Grant the given identity connection access to the database.
* **Note**: this method does not currently work, see https://github.com/aws/aws-cdk/issues/11851 for details.
* @see https://github.com/aws/aws-cdk/issues/11851
*
* @param grantee the Principal to grant the permissions to
* @param dbUser the name of the database user to allow connecting as to the db instance
*/
grantConnect(grantee: iam.IGrantable): iam.Grant;
grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant;

/**
* Defines a CloudWatch event rule which triggers for instance events. Use
Expand Down Expand Up @@ -97,6 +106,14 @@ export interface DatabaseInstanceAttributes {
*/
readonly port: number;

/**
* The AWS Region-unique, immutable identifier for the DB instance.
* This identifier is found in AWS CloudTrail log entries whenever the AWS KMS key for the DB instance is accessed.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html#aws-resource-rds-dbinstance-return-values
*/
readonly instanceResourceId?: string;

/**
* The security groups of the instance.
*/
Expand Down Expand Up @@ -130,6 +147,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
public readonly instanceEndpoint = new Endpoint(attrs.instanceEndpointAddress, attrs.port);
public readonly engine = attrs.engine;
protected enableIamAuthentication = true;
public readonly instanceResourceId = attrs.instanceResourceId;
}

return new Import(scope, id);
Expand All @@ -138,6 +156,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
public abstract readonly instanceIdentifier: string;
public abstract readonly dbInstanceEndpointAddress: string;
public abstract readonly dbInstanceEndpointPort: string;
public abstract readonly instanceResourceId?: string;
public abstract readonly instanceEndpoint: Endpoint;
// only required because of JSII bug: https://github.com/aws/jsii/issues/2040
public abstract readonly engine?: IInstanceEngine;
Expand All @@ -158,16 +177,33 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
});
}

public grantConnect(grantee: iam.IGrantable): iam.Grant {
public grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant {
if (this.enableIamAuthentication === false) {
throw new Error('Cannot grant connect when IAM authentication is disabled');
}

if (!this.instanceResourceId) {
throw new Error('For imported Database Instances, instanceResourceId is required to grantConnect()');
}

if (!dbUser) {
throw new Error('For imported Database Instances, the dbUser is required to grantConnect()');
}

this.enableIamAuthentication = true;
return iam.Grant.addToPrincipal({
grantee,
actions: ['rds-db:connect'],
resourceArns: [this.instanceArn],
resourceArns: [
// The ARN of an IAM policy for IAM database access is not the same as the instance ARN, so we cannot use `this.instanceArn`.
// See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html
Stack.of(this).formatArn({
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
service: 'rds-db',
resource: 'dbuser',
resourceName: [this.instanceResourceId, dbUser].join('/'),
}),
],
});
}

Expand Down Expand Up @@ -1029,6 +1065,26 @@ abstract class DatabaseInstanceSource extends DatabaseInstanceNew implements IDa
target: this,
});
}

/**
* Grant the given identity connection access to the database.
*
* @param grantee the Principal to grant the permissions to
* @param dbUser the name of the database user to allow connecting as to the db instance
*
* @default the default user, obtained from the Secret
*/
public grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant {
if (!dbUser) {
if (!this.secret) {
throw new Error('A secret or dbUser is required to grantConnect()');
}

dbUser = this.secret.secretValueFromJson('username').toString();
}

return super.grantConnect(grantee, dbUser);
}
}

/**
Expand Down Expand Up @@ -1074,6 +1130,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas
public readonly instanceIdentifier: string;
public readonly dbInstanceEndpointAddress: string;
public readonly dbInstanceEndpointPort: string;
public readonly instanceResourceId?: string;
public readonly instanceEndpoint: Endpoint;
public readonly secret?: secretsmanager.ISecret;

Expand All @@ -1095,6 +1152,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas
this.instanceIdentifier = this.getResourceNameAttribute(instance.ref);
this.dbInstanceEndpointAddress = instance.attrEndpointAddress;
this.dbInstanceEndpointPort = instance.attrEndpointPort;
this.instanceResourceId = instance.attrDbiResourceId;

// create a number token that represents the port of the instance
const portAttribute = Token.asNumber(instance.attrEndpointPort);
Expand Down Expand Up @@ -1141,6 +1199,7 @@ export class DatabaseInstanceFromSnapshot extends DatabaseInstanceSource impleme
public readonly instanceIdentifier: string;
public readonly dbInstanceEndpointAddress: string;
public readonly dbInstanceEndpointPort: string;
public readonly instanceResourceId?: string;
public readonly instanceEndpoint: Endpoint;
public readonly secret?: secretsmanager.ISecret;

Expand Down Expand Up @@ -1172,6 +1231,7 @@ export class DatabaseInstanceFromSnapshot extends DatabaseInstanceSource impleme
this.instanceIdentifier = instance.ref;
this.dbInstanceEndpointAddress = instance.attrEndpointAddress;
this.dbInstanceEndpointPort = instance.attrEndpointPort;
this.instanceResourceId = instance.attrDbiResourceId;

// create a number token that represents the port of the instance
const portAttribute = Token.asNumber(instance.attrEndpointPort);
Expand Down Expand Up @@ -1229,6 +1289,7 @@ export class DatabaseInstanceReadReplica extends DatabaseInstanceNew implements
public readonly instanceIdentifier: string;
public readonly dbInstanceEndpointAddress: string;
public readonly dbInstanceEndpointPort: string;
public readonly instanceResourceId?: string;
public readonly instanceEndpoint: Endpoint;
public readonly engine?: IInstanceEngine = undefined;
protected readonly instanceType: ec2.InstanceType;
Expand Down Expand Up @@ -1260,6 +1321,7 @@ export class DatabaseInstanceReadReplica extends DatabaseInstanceNew implements
this.instanceIdentifier = instance.ref;
this.dbInstanceEndpointAddress = instance.attrEndpointAddress;
this.dbInstanceEndpointPort = instance.attrEndpointPort;
this.instanceResourceId = instance.attrDbInstanceArn;

// create a number token that represents the port of the instance
const portAttribute = Token.asNumber(instance.attrEndpointPort);
Expand Down
81 changes: 80 additions & 1 deletion packages/aws-cdk-lib/aws-rds/test/instance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,86 @@ describe('instance', () => {
Effect: 'Allow',
Action: 'rds-db:connect',
Resource: {
'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':rds:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':db:', { Ref: 'InstanceC1063A87' }]],
'Fn::Join': [
'',
[
'arn:',
{
Ref: 'AWS::Partition',
},
':rds-db:',
{
Ref: 'AWS::Region',
},
':',
{
Ref: 'AWS::AccountId',
},
':dbuser:',
{
'Fn::GetAtt': [
'InstanceC1063A87',
'DbiResourceId',
],
},
'/{{resolve:secretsmanager:',
{
Ref: 'InstanceSecretAttachment83BEE581',
},
':SecretString:username::}}',
],
],
},
}],
Version: '2012-10-17',
},
});
});

test('createGrant - creates IAM policy and enables IAM auth for a specific user', () => {
const instance = new rds.DatabaseInstance(stack, 'Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }),
vpc,
});
const role = new Role(stack, 'DBRole', {
assumedBy: new AccountPrincipal(stack.account),
});
instance.grantConnect(role, 'my-user');

Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', {
EnableIAMDatabaseAuthentication: true,
});
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [{
Effect: 'Allow',
Action: 'rds-db:connect',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{
Ref: 'AWS::Partition',
},
':rds-db:',
{
Ref: 'AWS::Region',
},
':',
{
Ref: 'AWS::AccountId',
},
':dbuser:',
{
'Fn::GetAtt': [
'InstanceC1063A87',
'DbiResourceId',
],
},
'/my-user',
],
],
},
}],
Version: '2012-10-17',
Expand Down

0 comments on commit 227ea09

Please sign in to comment.