Skip to content

Commit 57435eb

Browse files
durranjyeminkevinAlbs
authored
feat(DRIVERS-2903): use custom aws configuration (#1743)
Co-authored-by: Jeff Yemin <[email protected]> Co-authored-by: Kevin Albertson <[email protected]>
1 parent ccd72de commit 57435eb

File tree

4 files changed

+175
-7
lines changed

4 files changed

+175
-7
lines changed

source/auth/auth.md

+27-2
Original file line numberDiff line numberDiff line change
@@ -987,12 +987,29 @@ those credentials will be used by default if AWS auth environment variables are
987987
application. Alternatively, you can create an AWS profile specifically for your MongoDB credentials and set the
988988
`AWS_PROFILE` environment variable to that profile name."
989989
990+
##### Custom Credential Providers
991+
992+
Drivers that choose to use the AWS SDK to fetch credentials MAY also allow users to provide a custom credential provider
993+
as an option to the `MongoClient`. The interface for the option provided depends on the individual language SDK and
994+
drivers MUST consult AWS SDK documentation to determine that format when implementing. The name of the option MUST be
995+
`AWS_CREDENTIAL_PROVIDER` and be part of the authentication mechanism properties options that can be provided to the
996+
client.
997+
998+
Drivers MAY expose API for default providers for the following scenarios when applicable in their language's SDK:
999+
1000+
1. The default SDK credential provider.
1001+
2. A custom credential provider chain.
1002+
3. A single credential provider of any available SDK options provided by the SDK.
1003+
1004+
##### Credential Fetching Order
1005+
9901006
The order in which Drivers MUST search for credentials is:
9911007

9921008
1. The URI
9931009
2. Environment variables
994-
3. Using `AssumeRoleWithWebIdentity` if `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` are set.
995-
4. The ECS endpoint if `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` is set. Otherwise, the EC2 endpoint.
1010+
3. A custom AWS credential provider if the driver supports it.
1011+
4. Using `AssumeRoleWithWebIdentity` if `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` are set.
1012+
5. The ECS endpoint if `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` is set. Otherwise, the EC2 endpoint.
9961013

9971014
> [!NOTE]
9981015
> See *Should drivers support accessing Amazon EC2 instance metadata in Amazon ECS* in [Q & A](#q-and-a)
@@ -1306,6 +1323,12 @@ in the MONGODB-OIDC specification, including sections or blocks that specificall
13061323
check MUST be performed after SRV record resolution, if applicable. This property is only required for drivers
13071324
that support the [Human Authentication Flow](#human-authentication-flow).
13081325
1326+
- AWS_CREDENTIAL_PROVIDER
1327+
1328+
A function or object from the AWS SDK that can be used to return AWS credentials. Drivers MAY allow the user to
1329+
specify the callback using a `MongoClient` configuration instead of a mechanism property, depending on what is
1330+
idiomatic for the driver.
1331+
13091332
<span id="built-in-provider-integrations"/>
13101333
13111334
#### Built-in OIDC Environment Integrations
@@ -2134,6 +2157,8 @@ practice to avoid this. (See
21342157

21352158
## Changelog
21362159

2160+
- 2025-01-29: Add support for custom AWS credential providers.
2161+
21372162
- 2024-10-02: Add Kubernetes built-in OIDC provider integration.
21382163

21392164
- 2024-08-19: Clarify Reauthentication and Speculative Authentication combination behavior.

source/auth/tests/mongodb-aws.md

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ SecretAccessKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
2121
Token=AQoDYXdzEJr...<remainder of security token>
2222
```
2323

24+
If the driver supports user provided custom AWS credential providers, then the driver MUST also test the above scenarios
25+
2-6 with a user provided `AWS_CREDENTIAL_PROVIDER` auth mechanism property. This value MUST be the default credential
26+
provider from the AWS SDK. If the default provider does not cover all scenarios above, those not covered MAY be skipped.
27+
In these tests the driver MUST also assert that the user provided credential provider was called at least once in each
28+
test.
29+
30+
If the driver supports a custom AWS credential provider, it MUST verify the custom provider was used when testing. This
31+
may be via a custom function or object that wraps the calls to the custom provider and asserts that it was called at
32+
least once.
33+
2434
## Regular credentials
2535

2636
Drivers MUST be able to authenticate by providing a valid access key id and secret access key pair as the username and

source/client-side-encryption/client-side-encryption.md

+50-5
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ class AutoEncryptionOpts {
380380
// without the MongoDB Enterprise Advanced licensed crypt_shared library.
381381
bypassQueryAnalysis: Optional<Boolean>; // Default false.
382382
keyExpirationMS: Optional<Uint64>; // Default 60000. 0 means "never expire".
383+
credentialProviders: Optional<CredentialProviders>;
383384
}
384385
```
385386

@@ -475,6 +476,44 @@ See
475476
<span id="GCPKMSOptions"></span> <span id="AWSKMSOptions"></span> <span id="KMSProvider"></span>
476477
<span id="KMSProviders"></span> <span id="AzureAccessToken"></span> <span id="kmsproviders"></span>
477478

479+
#### credentialProviders
480+
481+
The `credentialProviders` property may be specified on [ClientEncryptionOpts](#ClientEncryptionOpts) or
482+
[AutoEncryptionOpts](#AutoEncryptionOpts). Current support is for AWS only, but is designed to be able to accommodate
483+
additional providers in the future. If a custom credential provider is present, it MUST be used instead of the default
484+
flow for fetching automatic credentials and if the `kmsProviders` are not configured for automatic credential fetching
485+
an error MUST be thrown.
486+
487+
```typescript
488+
interface CredentialProviders {
489+
aws?: AWSCredentialProvider
490+
}
491+
492+
// The type of the AWS credential provider is dictated by the AWS SDK's credential provider for the specific
493+
// language.
494+
type AWSCredentialProvider = Function | Object;
495+
```
496+
497+
The following shows an example object of `CredentialProviders` for Node.js:
498+
499+
```typescript
500+
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
501+
502+
const client = new MongoClient(process.env.MONGODB_URI, {
503+
autoEncryption: {
504+
keyVaultNamespace: 'keyvault.datakeys',
505+
kmsProviders: {
506+
// Set to empty map to use `credentialProviders`.
507+
aws: {}
508+
},
509+
credentialProviders: {
510+
// Acquire credentials for AWS:
511+
aws: fromNodeProviderChain()
512+
}
513+
}
514+
}
515+
```
516+
478517
#### kmsProviders
479518
480519
The `kmsProviders` property may be specified on [ClientEncryptionOpts](#ClientEncryptionOpts) or
@@ -593,11 +632,14 @@ Once requested, drivers MUST create a new [KMSProviders](#kmsproviders) $P$ acco
593632
[ClientEncryptionOpts](#ClientEncryptionOpts) or [AutoEncryptionOpts](#AutoEncryptionOpts).
594633
2. Initialize $P$ to an empty [KMSProviders](#kmsproviders) object.
595634
3. If $K$ contains an `aws` property, and that property is an empty map:
596-
1. Attempt to obtain credentials $C$ from the environment using similar logic as is detailed in
597-
[the obtaining-AWS-credentials section from the Driver Authentication specification](../auth/auth.md#obtaining-credentials),
598-
but ignoring the case of loading the credentials from a URI
599-
2. If credentials $C$ were successfully loaded, create a new [AWSKMSOptions](#AWSKMSOptions) map from $C$ and insert
600-
that map onto $P$ as the `aws` property.
635+
1. If a custom credential provider is supplied via the `credentialProviders.aws` applicable encryption option, use
636+
that to fetch the credentials from AWS.
637+
2. Otherwise:
638+
1. Attempt to obtain credentials $C$ from the environment using similar logic as is detailed in
639+
[the obtaining-AWS-credentials section from the Driver Authentication specification](../auth/auth.md#obtaining-credentials),
640+
but ignoring the case of loading the credentials from a URI
641+
2. If credentials $C$ were successfully loaded, create a new [AWSKMSOptions](#AWSKMSOptions) map from $C$ and
642+
insert that map onto $P$ as the `aws` property.
601643
4. If $K$ contains an `gcp` property, and that property is an empty map:
602644
1. Attempt to obtain credentials $C$ from the environment logic as is detailed in
603645
[Obtaining GCP Credentials](#obtaining-gcp-credentials).
@@ -1051,6 +1093,7 @@ interface ClientEncryptionOpts {
10511093
keyVaultClient: MongoClient;
10521094
keyVaultNamespace: String;
10531095
kmsProviders: KMSProviders;
1096+
credentialProviders: CredentialProviders;
10541097
tlsOptions?: KMSProvidersTLSOptions; // Maps KMS provider to TLS options.
10551098
keyExpirationMS: Optional<Uint64>; // Default 60000. 0 means "never expire".
10561099
};
@@ -2420,6 +2463,8 @@ explicit session parameter as described in the [Drivers Sessions Specification](
24202463

24212464
## Changelog
24222465

2466+
- 2024-02-19: Add custom options AWS credential provider.
2467+
24232468
- 2024-10-09: Add retry prose test.
24242469

24252470
- 2024-07-29: Document range as stable.

source/client-side-encryption/tests/README.md

+88
Original file line numberDiff line numberDiff line change
@@ -3683,3 +3683,91 @@ Run an aggregate operation on `db.csfle` with the following pipeline:
36833683
```
36843684

36853685
Expect an exception to be thrown with a message containing the substring `Upgrade`.
3686+
3687+
### 26. Custom AWS Credentials
3688+
3689+
These tests require valid AWS credentials for the remote KMS provider via the secrets manager (FLE_AWS_KEY and
3690+
FLE_AWS_SECRET). These tests MUST NOT run inside an AWS environment that has the same credentials set in order to
3691+
properly ensure the tests would fail using on-demand credentials.
3692+
3693+
#### Case 1: ClientEncryption with `credentialProviders` and incorrect `kmsProviders`
3694+
3695+
Create a MongoClient named `setupClient`.
3696+
3697+
Create a [ClientEncryption](../client-side-encryption.md#clientencryption) object with the following options:
3698+
3699+
```typescript
3700+
class ClientEncryptionOpts {
3701+
keyVaultClient: <setupClient>,
3702+
keyVaultNamespace: "keyvault.datakeys",
3703+
kmsProviders: { "aws": { "accessKeyId": <set from secrets manager>, "secretAccessKey": <set from secrets manager> } },
3704+
credentialProviders: { "aws": <default provider from AWS SDK> }
3705+
}
3706+
```
3707+
3708+
Assert that an error is thrown.
3709+
3710+
#### Case 2: ClientEncryption with `credentialProviders` works
3711+
3712+
Create a MongoClient named `setupClient`.
3713+
3714+
Create a [ClientEncryption](../client-side-encryption.md#clientencryption) object with the following options:
3715+
3716+
```typescript
3717+
class ClientEncryptionOpts {
3718+
keyVaultClient: <setupClient>,
3719+
keyVaultNamespace: "keyvault.datakeys",
3720+
kmsProviders: { "aws": {} },
3721+
credentialProviders: { "aws": <object/function that returns valid credentials from the secrets manager> }
3722+
}
3723+
```
3724+
3725+
Use the client encryption to create a datakey using the "aws" KMS provider. This should successfully load and use the
3726+
AWS credentials that were provided by the secrets manager for the remote provider. Assert the datakey was created and
3727+
that the custom credential provider was called at least once.
3728+
3729+
An example of this in Node.js:
3730+
3731+
```typescript
3732+
import { ClientEncryption, MongoClient } from 'mongodb';
3733+
3734+
let calledCount = 0;
3735+
const masterKey = {
3736+
region: '<aws region>',
3737+
key: '<key for arn>'
3738+
};
3739+
const keyVaultClient = new MongoClient(process.env.MONGODB_URI);
3740+
const options = {
3741+
keyVaultNamespace: 'keyvault.datakeys',
3742+
kmsProviders: { aws: {} },
3743+
credentialProviders: {
3744+
aws: async () => {
3745+
calledCount++;
3746+
return {
3747+
accessKeyId: process.env.FLE_AWS_KEY,
3748+
secretAccessKey: process.env.FLE_AWS_SECRET
3749+
};
3750+
}
3751+
}
3752+
};
3753+
const clientEncryption = new ClientEncryption(keyVaultClient, options);
3754+
const dk = await clientEncryption.createDataKey('aws', { masterKey });
3755+
expect(dk).to.be.a(Binary);
3756+
expect(calledCount).to.be.greaterThan(0);
3757+
```
3758+
3759+
#### Case 3: `AutoEncryptionOpts` with `credentialProviders` and incorrect `kmsProviders`
3760+
3761+
Create a `MongoClient` object with the following options:
3762+
3763+
```typescript
3764+
class AutoEncryptionOpts {
3765+
autoEncryption: {
3766+
keyVaultNamespace: "keyvault.datakeys",
3767+
kmsProviders: { "aws": { "accessKeyId": <set from secrets manager>, "secretAccessKey": <set from secrets manager> } },
3768+
credentialProviders: { "aws": <default provider from AWS SDK> }
3769+
}
3770+
}
3771+
```
3772+
3773+
Assert that an error is thrown.

0 commit comments

Comments
 (0)