aws / aws/aws-cdk

aws-rds: oracle password rules not followed + exclude_characters act strange

Open
#19,834 3 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-rds bug effort/small p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

Two problems in the same session ..

1. the [default exclude set](https://github.com/aws/aws-cdk/blob/1fdf1223304e15d905723553a40640b8bcb0ec56/packages/%40aws-cdk/aws-rds/lib/private/util.ts#L13-L21) isn't good enough for oracle
2. "^" can not be the first character in the exclude set.

### Expected Behavior

Specifying the construction of a RDS that happens to have an Oracle engine, should produce a password that is legal for Oracle.

When specifying an exclude string, the sequence of characters should not matter. "^ " is wildly different from " ^" right now.

### Current Behavior

```python
rds.DatabaseInstance(self, "Oracle",
engine=rds.DatabaseInstanceEngine.oracle_ ...
...
credentials=rds.Credentials.from_generated_secret(
username="admin"
),
...)
```

Will start up an Oracle RDS just fine, but the password will probably not work for Oracle, as it will not follow the rules for Oracle passwords:

Your Oracle Password must contain at least 15 characters and include 3 of the following:
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least 1 special character
- only the following special characters are allowed: # $ _

Using the `exclude_characters` specified in https://github.com/aws/aws-cdk/issues/16824#issuecomment-937520774 adjusted slightly to follow the rules, we write this, hoping to get a new database with a better password ..

```python
exclude_characters="^ %+~`$&*()|[]{}:;,-<>?!'/\\\",="

rds.DatabaseInstance(self, "Oracle",
engine=rds.DatabaseInstanceEngine.oracle_ ...
...
credentials=rds.Credentials.from_generated_secret(
username="admin",
exclude_characters=exclude_characters,
),
...)
```

This will result in this error from CloudFormation:

_The parameter MasterUserPassword is not a valid password. Only printable ASCII characters besides '/', '@', '"', ' ' may be used. (Service: AmazonRDS; Status Code: 400; Error Code: InvalidParameterValue;_

Taking the hint, that only printable ASCII characters can be used, and knowing that '/', '@', '"', can't be used in an Oracle password anyway, we can do the following

```python
import string

exclude_characters=string.printable
.replace(string.ascii_letters, "")
.replace(string.digits, "")
.replace(string.whitespace, " ")
.replace('#', "")
.replace("$", "")
.replace("_", "")

# the above evaluates to this:
exclude_characters='!"%&\'()*+,-./:;<=>?@[\\]^`{|}~ '

rds.DatabaseInstance(self, "Oracle",
engine=rds.DatabaseInstanceEngine.oracle_ ...
...
credentials=rds.Credentials.from_generated_secret(
username="admin",
exclude_characters=exclude_characters,
),
...)
```

This works, and produces an Oracle RDS with a password saved as a secret and a password that is allowed by Oracle.

At this point, I looked at the failing and the working sequences of exclude_characters:

```
exclude_characters='!"%&\'()*+,-./:;<=>?@[\\]^`{|}~ '
exclude_characters="^ %+~`&*()|[]{}:;,-<>?!'/\\\",="
```

These contain exactly the same characters, but the sequences are different

The minimal change it's possible to make to the last of the two strings above is this:

```
exclude_characters=" ^%+~`&*()|[]{}:;,-<>?!'/\\\",="
```

So if `exclude_characters` start with a `^`, CloudFormation errors as described above. If the `^` is moved a bit, everything works perfectly.

### Reproduction Steps

Make an Oracle RDS like this:

```python
rds.DatabaseInstance(self, "Oracle",
engine=rds.DatabaseInstanceEngine.oracle_ ...
...
credentials=rds.Credentials.from_generated_secret(
username="admin"
),
...)
```

Ignore the database, check the actual password in the secret store, and notice that it does not conform to the password rules set by Oracle. Try to log into the database with the password in the secret store and observe that you get an error.

SECOND

Make an Oracle RDS like this:

```python
exclude_characters="^ %+~`&*()|[]{}:;,-<>?!'/\\\",="
rds.DatabaseInstance(self, "Oracle",
engine=rds.DatabaseInstanceEngine.oracle_ ...
...
credentials=rds.Credentials.from_generated_secret(
username="admin",
exclude_characters=exclude_characters,
),
...)
```
Observe that CloudFormation chokes out with the error:

change `exclude_characters` to

```
exclude_characters=" ^%+~`&*()|[]{}:;,-<>?!'/\\\",="
```

observe that CloudFormation accepts the stack, creates the database and that the password in secret store conform to the rules set by Oracle. Check that you can log into the database.

To prove, that the leading `^` is a problem, just use `rds.Credentials.from_generated_secret` with any db engine, exclude some characters and lead with the `^`. CloudFormation will choke.

### Possible Solution

Solution for the first problem:

Just adjust

```
export const DEFAULT_PASSWORD_EXCLUDE_CHARS = " %+~`#$&*()|[]{}:;<>?!'/@\"\\";
```

to

```
export const DEFAULT_PASSWORD_EXCLUDE_CHARS = " %+~`&*()|[]{}:;<>?!'/@\"\\";
```
here:

https://github.com/aws/aws-cdk/blob/1fdf1223304e15d905723553a40640b8bcb0ec56/packages/%40aws-cdk/aws-rds/lib/private/util.ts#L13-L21

The solution to the second problem is a bit more tricky and I have no idea about a possible fix. Somewhere in the path of the use of `exclude_character`, that parameter is used as a regexp character class. ^ has a special meaning in such situations and may explain why the string with a leading ^ messes thing up.

### Additional Information/Context

This issue started as a helping reminder on this issue: https://github.com/aws/aws-cdk/issues/16824

### CDK CLI Version

2.17.0 (build f9cd009)

### Framework Version

aws-cdk-lib 2.16.0

### Node.js Version

v17.7.1

### OS

macOS Monterey, Version 12.2.1

### Language

Python

### Language Version

3.9.10

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start in packages/@aws-cdk/aws-rds/lib/private/util.ts at DEFAULT_PASSWORD_EXCLUDE_CHARS, then trace Credentials.from_generated_secret and the exclude_characters path. Reproduce both Oracle password-rule and leading-^ cases; done means the default excludes Oracle-invalid characters and any ordering of excluded characters is accepted by CloudFormation.

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
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.