aws / aws/aws-cdk

(aws-rds): DatabaseProxy does not inherit defaultPort from ProxyTarget, causing allowDefaultPortFrom to fail

Open
#37,635 1 comment 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-rds bug effort/medium p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
1d 19h
Merged PRs (30d)
74

Description

### Describe the bug

When creating an `rds.DatabaseProxy` that wraps an `rds.DatabaseCluster` or `rds.DatabaseInstance`, the resulting proxy object does not inherit the `defaultPort` of its target.

If a developer attempts to grant network access to the proxy using the standard `proxy.connections.allowDefaultPortFrom(compute)` pattern, the CDK synthesis crashes with the following error:

```
Cannot call allowDefaultPortFrom(): this resource has no default port
```

This forces developers to break the CDK abstraction and hardcode the database port (e.g., `ec2.Port.tcp(5432)`) when configuring security group rules for the proxy.

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Library Version

_No response_

### Expected Behavior

The `DatabaseProxy` should automatically know the default port of the database it is proxying.

Calling `proxy.connections.allowDefaultPortFrom(compute)` should successfully create an inbound Security Group rule on the Proxy's security group for the correct port (e.g., 5432 for Postgres, 3306 for MySQL), exactly as it does when calling that method directly on the `DatabaseCluster`.

### Current Behavior

The deployment crashes during synthesis because `this.connections.defaultPort` is `undefined` on the `DatabaseProxy` instance.

### Reproduction Steps

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

const vpc = new ec2.Vpc(this, 'Vpc');

const cluster = new rds.DatabaseCluster(this, 'Cluster', {
engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_15_4 }),
vpc,
writer: rds.ClusterInstance.serverlessV2('writer'),
});

const proxy = new rds.DatabaseProxy(this, 'Proxy', {
proxyTarget: rds.ProxyTarget.fromCluster(cluster),
secrets: [cluster.secret!],
vpc,
});

const myLambda = new lambda.Function(this, 'MyLambda', { /* ... */ });

// THIS CRASHES: Cannot call allowDefaultPortFrom(): this resource has no default port
proxy.connections.allowDefaultPortFrom(myLambda);

// Workaround required:
// proxy.connections.allowFrom(myLambda, ec2.Port.tcp(5432));
```

### Possible Solution

The root cause is in `packages/aws-cdk-lib/aws-rds/lib/proxy.ts`.

The `DatabaseProxy` initializes its `ec2.Connections` object without a `defaultPort`. It then calls `props.proxyTarget.bind(this)`.

Because `bind()` internally calls `allowDefaultPortFrom(proxy)` on the cluster to grant the proxy access, `bind()` requires `proxy.connections` to already be instantiated so it can read its security groups.

Proposed Solution: Pre-flight Port Extraction

To fix this, the `DatabaseProxy` must extract the port from the target _before_ instantiating its `Connections` object.

1. Expose the port on `ProxyTarget`: Add a getter to the `ProxyTarget` class (or update the interface) so the proxy can access the underlying port before calling `bind().
```ts
// packages/aws-cdk-lib/aws-rds/lib/proxy.ts
export class ProxyTarget {
// ... existing code ...

/**
* The default port of the underlying database cluster or instance.
* @internal
*/
public get _defaultPort(): ec2.Port | undefined {
return this.dbCluster?.connections.defaultPort ?? this.dbInstance?.connections.defaultPort;
}
}
```

2. Initialize `Connections` with the target's port:
```ts
// packages/aws-cdk-lib/aws-rds/lib/proxy.ts (DatabaseProxy constructor)
const securityGroups = props.securityGroups ?? [new ec2.SecurityGroup(...)];

// Extract the port before binding
const defaultPort = props.proxyTarget._defaultPort;

// Initialize with the correct port
this.connections = new ec2.Connections({
securityGroups,
defaultPort // <-- The proxy now knows its port!
});

// Bind can now execute safely, and allowDefaultPortFrom() works downstream
const bindResult = props.proxyTarget.bind(this);
```

### Additional Information/Context

This is particularly painful in Serverless (Lambda) architectures where `DatabaseProxy` is practically mandatory for connection pooling, and dynamically generating API infrastructure relies heavily on the generic `.allowDefaultPortFrom()` abstraction.

### AWS CDK Library version (aws-cdk-lib)

2.250.0

### AWS CDK CLI version

2.1118.0

### Node.js Version

v25.4.0

### OS

macOS (Darwin Kernel Version 25.4.0 / ARM64)

### Language

TypeScript

### Language Version

TypeScript (5.9.3)

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start in packages/aws-cdk-lib/aws-rds/lib/proxy.ts, focusing on ProxyTarget.bind() and DatabaseProxy construction. Reproduce the TypeScript example and inspect how Connections is initialized before binding; done means proxy.connections.allowDefaultPortFrom(compute) synthesizes successfully with the target's default port for both cluster and instance targets.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.