Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rds): add grant method for Data API #10748

Merged
merged 35 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
80bc323
feat(module): add clusterArn property to IServerlessCluster
asterikx Oct 6, 2020
2736838
feat(rds): add clusterArn property to IServerlessCluster
asterikx Oct 6, 2020
90b8728
Merge branch 'serverlesscluster-resource-arn-prop' of github.com:aste…
asterikx Oct 6, 2020
dd20726
Merge branch 'serverlesscluster-resource-arn-prop' of github.com:aste…
asterikx Oct 6, 2020
d5b2c90
Merge branch 'serverlesscluster-resource-arn-prop' of github.com:aste…
asterikx Oct 6, 2020
e288ee1
revert yarn.lock
asterikx Oct 6, 2020
dbd394c
add grantDataApi method
asterikx Oct 6, 2020
4ed21d5
feat(rds): add grantDataApi method
asterikx Oct 6, 2020
0208ab3
feat(rds): add grantDataApi method
asterikx Oct 6, 2020
0a8822b
Merge github.com:aws/aws-cdk into grant-data-api
asterikx Oct 6, 2020
08af548
fix typos in the docs and add link to data api actions
asterikx Oct 7, 2020
10614a2
Merge branch 'master' into grant-data-api
asterikx Oct 7, 2020
63b8adf
grant only rds-data permissions
asterikx Oct 7, 2020
a61c1e2
use explicit actions instead of wildcard in rds-data statement
asterikx Oct 8, 2020
53cb6d5
move secret to ServerlessClusterBase and grant secret read
asterikx Oct 8, 2020
42980bf
add test for imported clusters with user provided secret
asterikx Oct 8, 2020
7743fbb
check http endpoint is enabled when granting data api access
asterikx Oct 8, 2020
9046d19
rename grant method to grantDataApiAccess
asterikx Oct 8, 2020
15c2694
Merge branch 'master' into grant-data-api
asterikx Oct 8, 2020
b3f721d
Merge branch 'master' into grant-data-api
asterikx Oct 10, 2020
9fd133e
enable http endpoint when granting data api access
asterikx Oct 13, 2020
08f8b36
rename enableHttpEndpoint to enableDataApi
asterikx Oct 14, 2020
cf450b2
Merge branch 'grant-data-api' of github.com:asterikx/aws-cdk into gra…
asterikx Oct 14, 2020
47e2b30
Merge branch 'master' into grant-data-api
asterikx Oct 14, 2020
108a222
add changes to Data API in readme
asterikx Oct 14, 2020
4dae9b1
grant wildcard resource (no resource-level permissions available)
asterikx Oct 15, 2020
bbc629f
show vpc creation in readme example
asterikx Oct 15, 2020
98de6e4
Merge branch 'master' into grant-data-api
asterikx Oct 19, 2020
50f78cb
Merge branch 'master' into grant-data-api
asterikx Nov 4, 2020
bac137b
use wildcard grant
asterikx Nov 4, 2020
c87a07c
Merge branch 'grant-data-api' of github.com:asterikx/aws-cdk into gra…
asterikx Nov 4, 2020
e187da9
fix failing tests
asterikx Nov 4, 2020
08a25c1
rename grantDataApi to grantDataApiAccess
asterikx Nov 4, 2020
088567c
Merge branch 'master' into grant-data-api
njlynch Nov 5, 2020
5ab5ef7
Merge branch 'master' into grant-data-api
mergify[bot] Nov 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions packages/@aws-cdk/aws-rds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ See also [@aws-cdk/aws-secretsmanager](https://github.com/aws/aws-cdk/blob/maste
### 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
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.
Expand All @@ -245,12 +245,12 @@ 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.
See <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html> for setup instructions.

### Kerberos Authentication

You can also authenticate using Kerberos to a database instance using AWS Managed Microsoft AD for authentication;
See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/kerberos-authentication.html for more information
See <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/kerberos-authentication.html> for more information
and a list of supported versions and limitations.

The following example shows enabling domain support for a database instance and creating an IAM role to access
Expand All @@ -274,7 +274,7 @@ const instance = new rds.DatabaseInstance(stack, 'Instance', {
**Note**: In addition to the setup above, you need to make sure that the database instance has network connectivity
to the domain controllers. This includes enabling cross-VPC traffic if in a different VPC and setting up the
appropriate security groups/network ACL to allow traffic between the database instance and domain controllers.
Once configured, see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/kerberos-authentication.html for details
Once configured, see <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/kerberos-authentication.html> for details
on configuring users for each available database engine.

### Metrics
Expand Down Expand Up @@ -412,7 +412,7 @@ in the cloud without managing any database instances.

The following example initializes an Aurora Serverless PostgreSql cluster.
Aurora Serverless clusters can specify scaling properties which will be used to
automatically scale the database cluster seamlessly based on the workload.
automatically scale the database cluster seamlessly based on the workload.

```ts
import * as ec2 from '@aws-cdk/aws-ec2';
Expand All @@ -431,7 +431,9 @@ const cluster = new rds.ServerlessCluster(this, 'AnotherCluster', {
}
});
```

Aurora Serverless Clusters do not support the following features:

* Loading data from an Amazon S3 bucket
* Saving data to an Amazon S3 bucket
* Invoking an AWS Lambda function with an Aurora MySQL native function
Expand All @@ -448,3 +450,35 @@ Aurora Serverless Clusters do not support the following features:
Read more about the [limitations of Aurora Serverless](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.html#aurora-serverless.limitations)

Learn more about using Amazon Aurora Serverless by reading the [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.html)

#### Data API

You can access your Aurora Serverless DB cluster using the built-in Data API. The Data API doesn't require a persistent connection to the DB cluster. Instead, it provides a secure HTTP endpoint and integration with AWS SDKs.

The following example shows granting Data API access to a Lamba function.

```ts
import * as lambda from '@aws-cdk/aws-lambda';
import * as rds from '@aws-cdk/aws-rds';

const cluster = new rds.ServerlessCluster(this, 'AnotherCluster', {
engine: rds.DatabaseClusterEngine.AURORA_MYSQL,
vpc,
asterikx marked this conversation as resolved.
Show resolved Hide resolved
enableDataApi: true,
});

const fn = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_10_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
environment: {
CLUSTER_ARN: cluster.clusterArn,
SECRET_ARN: cluster.secret.secretArn,
},
});
cluster.grantDataApiAccess(fn)
```

**Note**: To invoke the Data API, the resource will need to read the secret associated with the cluster.

To learn more about using the Data API, see the [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html).
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-rds/lib/perms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// minimal set of permissions based on https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html#data-api.access
export const DATA_API_ACTIONS = [
asterikx marked this conversation as resolved.
Show resolved Hide resolved
'rds-data:BatchExecuteStatement',
'rds-data:BeginTransaction',
'rds-data:CommitTransaction',
'rds-data:ExecuteStatement',
'rds-data:RollbackTransaction',
];
66 changes: 58 additions & 8 deletions packages/@aws-cdk/aws-rds/lib/serverless-cluster.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import { Resource, Duration, Token, Annotations, RemovalPolicy, IResource, Stack } from '@aws-cdk/core';
import { Resource, Duration, Token, Annotations, RemovalPolicy, IResource, Stack, Lazy } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { IClusterEngine } from './cluster-engine';
import { DatabaseSecret } from './database-secret';
import { Endpoint } from './endpoint';
import { IParameterGroup } from './parameter-group';
import { DATA_API_ACTIONS } from './perms';
import { applyRemovalPolicy, defaultDeletionProtection, DEFAULT_PASSWORD_EXCLUDE_CHARS } from './private/util';
import { Credentials, RotationMultiUserOptions, RotationSingleUserOptions } from './props';
import { CfnDBCluster } from './rds.generated';
Expand Down Expand Up @@ -39,6 +41,13 @@ export interface IServerlessCluster extends IResource, ec2.IConnectable, secrets
* @attribute ReadEndpointAddress
*/
readonly clusterReadEndpoint: Endpoint;

/**
* Grant the given identity to access to the Data API
*
* @param grantee The principal to grant access to
*/
grantDataApiAccess(grantee: iam.IGrantable): iam.Grant
}
/**
* Properties to configure an Aurora Serverless Cluster
Expand Down Expand Up @@ -89,14 +98,13 @@ export interface ServerlessClusterProps {
readonly deletionProtection?: boolean;

/**
* Whether to enable the HTTP endpoint for an Aurora Serverless database cluster.
* The HTTP endpoint must be explicitly enabled to enable the Data API.
* Whether to enable the Data API.
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html
*
* @default false
*/
readonly enableHttpEndpoint?: boolean;
readonly enableDataApi?: boolean;

/**
* The VPC that this Aurora Serverless cluster has been created in.
Expand Down Expand Up @@ -194,6 +202,13 @@ export interface ServerlessClusterAttributes {
* @default - no reader address
*/
readonly readerEndpointAddress?: string;

/**
* The secret attached to the database cluster
*
* @default - no secret
*/
readonly secret?: secretsmanager.ISecret;
}

/**
Expand Down Expand Up @@ -288,6 +303,13 @@ abstract class ServerlessClusterBase extends Resource implements IServerlessClus
*/
public abstract readonly connections: ec2.Connections;

/**
* The secret attached to this cluster
*/
public abstract readonly secret?: secretsmanager.ISecret

protected abstract enableDataApi?: boolean;

/**
* The ARN of the cluster
*/
Expand All @@ -300,6 +322,27 @@ abstract class ServerlessClusterBase extends Resource implements IServerlessClus
});
}

/**
* Grant the given identity to access to the Data API, including read access to the secret attached to the cluster if present
*
* @param grantee The principal to grant access to
*/
public grantDataApiAccess(grantee: iam.IGrantable): iam.Grant {
asterikx marked this conversation as resolved.
Show resolved Hide resolved
if (this.enableDataApi === false) {
throw new Error('Cannot grant Data API access when HTTP endpoint is disabled');
asterikx marked this conversation as resolved.
Show resolved Hide resolved
}

this.enableDataApi = true;
const ret = iam.Grant.addToPrincipal({
grantee,
actions: DATA_API_ACTIONS,
resourceArns: [this.clusterArn],
njlynch marked this conversation as resolved.
Show resolved Hide resolved
scope: this,
});
this.secret?.grantRead(grantee);
return ret;
}

/**
* Renders the secret attachment target specifications.
*/
Expand Down Expand Up @@ -334,11 +377,10 @@ export class ServerlessCluster extends ServerlessClusterBase {
public readonly clusterReadEndpoint: Endpoint;
public readonly connections: ec2.Connections;

/**
* The secret attached to this cluster
*/
public readonly secret?: secretsmanager.ISecret;

protected readonly enableDataApi?: boolean

private readonly subnetGroup: ISubnetGroup;
private readonly vpc: ec2.IVpc;
private readonly vpcSubnets?: ec2.SubnetSelection;
Expand All @@ -355,6 +397,8 @@ export class ServerlessCluster extends ServerlessClusterBase {
this.singleUserRotationApplication = props.engine.singleUserRotationApplication;
this.multiUserRotationApplication = props.engine.multiUserRotationApplication;

this.enableDataApi = props.enableDataApi;

const { subnetIds } = this.vpc.selectSubnets(this.vpcSubnets);

// Cannot test whether the subnets are in different AZs, but at least we can test the amount.
Expand Down Expand Up @@ -410,7 +454,7 @@ export class ServerlessCluster extends ServerlessClusterBase {
engine: props.engine.engineType,
engineVersion: props.engine.engineVersion?.fullVersion,
engineMode: 'serverless',
enableHttpEndpoint: props.enableHttpEndpoint,
enableHttpEndpoint: Lazy.anyValue({ produce: () => this.enableDataApi }),
kmsKeyId: props.storageEncryptionKey?.keyArn,
masterUsername: credentials.username,
masterUserPassword: credentials.password?.toString(),
Expand Down Expand Up @@ -509,6 +553,10 @@ class ImportedServerlessCluster extends ServerlessClusterBase implements IServer
public readonly clusterIdentifier: string;
public readonly connections: ec2.Connections;

public readonly secret?: secretsmanager.ISecret;

protected readonly enableDataApi = true

private readonly _clusterEndpoint?: Endpoint;
private readonly _clusterReadEndpoint?: Endpoint;

Expand All @@ -523,6 +571,8 @@ class ImportedServerlessCluster extends ServerlessClusterBase implements IServer
defaultPort,
});

this.secret = attrs.secret;

this._clusterEndpoint = (attrs.clusterEndpointAddress && attrs.port) ? new Endpoint(attrs.clusterEndpointAddress, attrs.port) : undefined;
this._clusterReadEndpoint = (attrs.readerEndpointAddress && attrs.port) ? new Endpoint(attrs.readerEndpointAddress, attrs.port) : undefined;
}
Expand Down
Loading