Skip to content

Commit

Permalink
feat(rds): grantConnect for database instances (#9887)
Browse files Browse the repository at this point in the history
As a follow-on to enabling IAM database auth, this change makes it easier to
grant a user/role access to the database via policy.

fixes #1558

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
njlynch authored Aug 24, 2020
1 parent 1bbadda commit e893828
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 75 deletions.
22 changes: 22 additions & 0 deletions packages/@aws-cdk/aws-rds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,28 @@ The rotation will start as soon as this user exists.

See also [@aws-cdk/aws-secretsmanager](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-secretsmanager/README.md) for credentials rotation of existing clusters/instances.

### IAM Authentication

You can also authenticate to a database instance using AWS Identity and Access Management (IAM) database authentication;
See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html for more information
and a list of supported versions and limitations.

The following example shows enabling IAM authentication for a database instance and granting connection access to an IAM role.

```ts
const instance = new rds.DatabaseInstance(stack, 'Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }),
masterUsername: 'admin',
vpc,
iamAuthentication: true, // Optional - will be automatically set if you call grantConnect().
});
const role = new Role(stack, 'DBRole', { assumedBy: new AccountPrincipal(stack.account) });
instance.grantConnect(role); // Grant the role connection access to the DB.
```

**Note**: In addition to the setup above, a database user will need to be created to support IAM auth.
See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html for setup instructions.

### Metrics

Database instances expose metrics (`cloudwatch.Metric`):
Expand Down
25 changes: 24 additions & 1 deletion packages/@aws-cdk/aws-rds/lib/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export interface IDatabaseInstance extends IResource, ec2.IConnectable, secretsm
*/
addProxy(id: string, options: DatabaseProxyOptions): DatabaseProxy;

/**
* Grant the given identity connection access to the database.
*/
grantConnect(grantee: iam.IGrantable): iam.Grant;

/**
* Defines a CloudWatch event rule which triggers for instance events. Use
* `rule.addEventPattern(pattern)` to specify a filter.
Expand Down Expand Up @@ -103,6 +108,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
public readonly dbInstanceEndpointAddress = attrs.instanceEndpointAddress;
public readonly dbInstanceEndpointPort = attrs.port.toString();
public readonly instanceEndpoint = new Endpoint(attrs.instanceEndpointAddress, attrs.port);
protected enableIamAuthentication = true;
}

return new Import(scope, id);
Expand All @@ -112,6 +118,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
public abstract readonly dbInstanceEndpointAddress: string;
public abstract readonly dbInstanceEndpointPort: string;
public abstract readonly instanceEndpoint: Endpoint;
protected abstract enableIamAuthentication?: boolean;

/**
* Access to network connections.
Expand All @@ -128,6 +135,19 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase
});
}

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

this.enableIamAuthentication = true;
return iam.Grant.addToPrincipal({
grantee,
actions: ['rds-db:connect'],
resourceArns: [this.instanceArn],
});
}

/**
* Defines a CloudWatch event rule which triggers for instance events. Use
* `rule.addEventPattern(pattern)` to specify a filter.
Expand Down Expand Up @@ -494,6 +514,8 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData
private readonly cloudwatchLogsRetention?: logs.RetentionDays;
private readonly cloudwatchLogsRetentionRole?: iam.IRole;

protected enableIamAuthentication?: boolean;

constructor(scope: Construct, id: string, props: DatabaseInstanceNewProps) {
super(scope, id);

Expand Down Expand Up @@ -532,6 +554,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData
this.cloudwatchLogsExports = props.cloudwatchLogsExports;
this.cloudwatchLogsRetention = props.cloudwatchLogsRetention;
this.cloudwatchLogsRetentionRole = props.cloudwatchLogsRetentionRole;
this.enableIamAuthentication = props.iamAuthentication;

this.newCfnProps = {
autoMinorVersionUpgrade: props.autoMinorVersionUpgrade,
Expand All @@ -544,7 +567,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData
deleteAutomatedBackups: props.deleteAutomatedBackups,
deletionProtection,
enableCloudwatchLogsExports: this.cloudwatchLogsExports,
enableIamDatabaseAuthentication: props.iamAuthentication,
enableIamDatabaseAuthentication: Lazy.anyValue({ produce: () => this.enableIamAuthentication }),
enablePerformanceInsights: props.enablePerformanceInsights,
iops,
monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(),
Expand Down
Loading

0 comments on commit e893828

Please sign in to comment.