aws-cdk: (aws-rds): grantConnect for IAM authentication provides invalid permissions (surface DbiResourceId)

DatabaseInstance has a method grantConnect for granting connect access to instance using IAM based authentication.

However, the db resource ARN in the produced IAM policy is incorrect and doesn’t work. Based on the documentation The format for the resource in the IAM policy should be: arn:aws:rds-db:region:account-id:dbuser:DbiResourceId/db-user-name

The actual resource produced by grantConnect is having format: arn:aws:rds:region:account-id:db:DBInstanceId. Also, the function doesn’t provide any parameter to define the db username to be used in the policy.

Reproduction Steps

import { Stack, Construct, StackProps } from '@aws-cdk/core';
import {
  DatabaseInstance,
  DatabaseInstanceEngine,
  PostgresEngineVersion,
  Credentials,
} from '@aws-cdk/aws-rds';

import { IVpc } from '@aws-cdk/aws-ec2';
import { User } from '@aws-cdk/aws-iam';

export interface MyStackProps extends StackProps {
  vpc: IVpc;
}
export class MyStack extends Stack {
  constructor(scope: Construct, id: string, props: MyStackProps) {
    super(scope, id, props);

    const db = new DatabaseInstance(this, 'Instance', {
      engine: DatabaseInstanceEngine.postgres({ version: PostgresEngineVersion.VER_12_4 }),
      credentials: Credentials.fromGeneratedSecret('testuser'),
      vpc: props.vpc,
      port: 5432,
      iamAuthentication: true,
    });

    const user = new User(this, 'TestUser', {
      userName: 'testuser',
    });

    db.grantConnect(user);
  }
}

What did you expect to happen?

To create a IAM policy where the resource ARN would be according to the documentation i.e. arn:aws:rds-db:region:account-id:dbuser:DbiResourceId/db-user-name

What actually happened?

Instead of the correct policy, the generated template contains following definition:

           {
              "Action": "rds-db:connect",
              "Effect": "Allow",
              "Resource": {
                "Fn::Join": [
                  "",
                  [
                    "arn:",
                    {
                      "Ref": "AWS::Partition"
                    },
                    ":rds:",
                    {
                      "Ref": "AWS::Region"
                    },
                    ":",
                    {
                      "Ref": "AWS::AccountId"
                    },
                    ":db:",
                    {
                      "Ref": "InstanceC1063A87"
                    }
                  ]
                ]
              }
            }

In addition that the format of the ARN is incorrect, also wrong DB identifier is used. The template uses the DB Instance id but the correct identifier is the DB Resource id.

Environment

  • CDK CLI Version : 1.75.0
  • Framework Version: 1.75.0
  • Node.js Version: 12.18.3
  • OS : OSx
  • Language (Version): Typescript 4.1.2

Other

The support for the grantConnect was requested in this issue and added in this PR.

A comment in the original issue still stands i.e. that the DB Resource Id is not accessible in Cloudformation.


This is 🐛 Bug Report

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 28
  • Comments: 30 (18 by maintainers)

Commits related to this issue

Most upvoted comments

I see this is closed has it been fixed? if so should update the docs here: grantConnect

Is it possible for the CDK team to raise this with the CF team? I mean this is really broken.

Just run into this problem, and this is very broken. Until this issue is fixed upstream in CFN it would be nice to update the documentation to reflect that the grantConnect() method is utterly broken and cannot be used, just to save future travelers some time debugging this.

Thanks to @jdvornek for the solution provided. In our case we needed to tweak it a little to make it work as we were not using Aurora clusters and the API response was slightly larger than 4k.

    new customresource.AwsCustomResource(this, 'RdsInstanceResourceId', {
      onCreate: {
        service: 'RDS',
        action: 'describeDBInstances',
        parameters: {
          DBInstanceIdentifier: this.rdsInstance.instanceIdentifier,
        },
        physicalResourceId: customresource.PhysicalResourceId.fromResponse('DBInstances.0.DbiResourceId'),
        outputPath: 'DBInstances.0.DbiResourceId',
      },
      policy: customresource.AwsCustomResourcePolicy.fromSdkCalls({
        resources: customresource.AwsCustomResourcePolicy.ANY_RESOURCE,
      }),
    });

Note the addition of outputPath to limit the amount of data returned. In our case, if this was omitted, CFN update would fail and roll back a cryptic Response object is too long. error message.

This works for an Aurora cluster, I believe. Obviously it needs to be extended for instances, etc, but maybe this will get someone unblocked and it seems easy enough to swap out once a proper fix is available.

const someCluster = new rds.DatabaseCluster();
const someDatabaseUser = 'test';
const someRole = new iam.Role();
const dbResourceId = new custom.AwsCustomResource(this, 'MembershipDBResourceID', {
  onCreate: {
    service: 'RDS',
    action: 'describeDBClusters',
    parameters: {
      DBClusterIdentifier: someCluster.clusterIdentifier
    },
    physicalResourceId: custom.PhysicalResourceId.fromResponse('DBClusters.0.DbClusterResourceId')
  },
  policy: custom.AwsCustomResourcePolicy.fromSdkCalls({
    resources: custom.AwsCustomResourcePolicy.ANY_RESOURCE
  })
});
const scopeStack = cdk.Stack.of(this);
const resourceId = dbResourceId.getResponseField('DBClusters.0.DbClusterResourceId');
const userArn = scopeStack.formatArn({
                                       service: 'rds-db',
                                       resource: 'dbuser',
                                       resourceName: `${resourceId}/${someDatabaseUser}`,
                                       sep: ':',
                                     });
iam.Grant.addToPrincipal({
  grantee: someRole,
  actions: ["rds-db:connect"],
  resourceArns: [userArn]
});

That yields

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "rds-db:connect",
            "Resource": "arn:aws:rds-db:<region>:<account>:dbuser:cluster-0123456789/test",
            "Effect": "Allow"
        }
    ]
}

@cloventt The docs for AwsCustomResource say that onCreate will call onUpdate by default. So probably you only need to specify onUpdate. https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_custom-resources.AwsCustomResourceProps.html#oncreate

So it looks like skinny85 doesn’t work for Amazon any more, who would be the person to give an update on this particular issue?