Search code examples
aws-cdkaws-documentdb

AWS CDK DocDB::DBCluster fails with 'not a valid password'


I am trying to use AWS CKD (JAVA) to create a DocumentDB instance. This works with a "simple" plaintext password, but fails when I try to use a DatabaseSecret and a password stored in Secrets Manager.

The error I get is this:

1:44:42 PM | CREATE_FAILED        | AWS::DocDB::DBCluster                       | ApiDocDb15EB2C21
The parameter MasterUserPassword is not a valid password. Only printable ASCII characters besides '/', '@', '"', ' ' may
be used. (Service: AmazonRDS; Status Code: 400; Error Code: InvalidParameterValue; Request ID: c786d247-8ff2-4f30-9a8a-5
065fc89d3d1; Proxy: null)

which is clear enough, but it continues to happen, even if I set the password to something such as simplepassword - so I am now somewhat confused as to what am I supposed to fix now.

Here is the code, mostly adapted from the DocDB documentation:

    String id = String.format(DOCDB_PASSWORD_ID);

    return DatabaseSecret.Builder.create(scope, id)
        .secretName(store.getSsmSecretName())
        .encryptionKey(passwordKey)
        .username(store.getAdminUser())
        .build();

where the ssmSecretName is the name of the secret in SecretManager:

└─( aws secretsmanager get-secret-value --secret-id api-db-admin-pwd

ARN: arn:aws:secretsmanager:us-west-2:<ACCT>:secret:api-db-admin-pwd-HHxpFf

Name: api-db-admin-pwd
SecretString: '{"api-db-admin-pwd":"simplepassword"}'

This is the code used to build the DbCluster:

    DatabaseCluster dbCluster = DatabaseCluster.Builder.create(scope, id)
        .dbClusterName(properties.getDbName())
        .masterUser(Login.builder()
            .username(properties.getAdminUser())
            .kmsKey(passwordKey)
            .password(masterPassword.getSecretValue())
            .build())
        .vpc(vpc)
        .vpcSubnets(ISOLATED_SUBNETS)
        .securityGroup(dbSecurityGroup)
        .instanceType(InstanceType.of(InstanceClass.MEMORY5, InstanceSize.LARGE))
        .instances(properties.getReplicas())
        .storageEncrypted(true)
        .build();

The question I have is: should I use a DatabaseSecret? or just retrieve the password from SM and be done with it?

A sub-question then: what is one supposed to use the DatabaseSecret for then? (NOTE -- this is the same class, almost, as in the rds package; but here I am using the docdb package)

Thanks for any suggestion!


Solution

  • Turns out that the DatabaseSecret creates a key/value pair as the secret:

    {
      "username": <value of username()>,
      "password": <generated>
    }
    

    However, the call to Login.password() completely ingnores this, and treats the whole JSON body as the password (so the " double quotes trip it).

    The trick is to use DatabaseSecret.secretValueFromJson("password") in the call to Login.password() and it works just fine.

    This is (incidentally) inconsistent with the behavior of rds.DatabaseCluster and the rds.Credentials class behavior (who take a JSON SecretValue and parse it correctly for the "password" field).

    Leaving it here in case others stumble on this, as there really is NO information out there.