-
Type: New Feature
-
Resolution: Unresolved
-
Priority: Unknown
-
None
-
Affects Version/s: None
-
Component/s: None
Lambda role chaining IAM authentification.
Currently it is not possible to use role chaining to authenticate via IAM without connection / auth issues. But this is a common ScreenOrientation.
Our use case:
Lamnbda has execution role that allows function to assume role MONDO_DB_READ. This roe is registered as an Atlas user so the lambda fn can authenticate with our cluster. We need role chaining (instead of using the execution role directly for auth) for security compliance reasons.
Issue:
Roles assumed via STS (role chaining) are only valid for an hour. If we provide the credentials obtained via STS directly to a MongoClient, the initial connection works, but after an hour (we have busy lambdas with execution contexts lasting longer than that), the connection to our Mongo cluster suddenly fails due to expired credentials. The MongoDB node driver makes it impossible to use other AWS credential providers than fromNodeProviderChain.
Solution:
Use the AWS sdk to get a credential provider. The SDK handles credential rotation. MongoDB node driver does support this (and uses it internally). World could be a happy place.
But:
The Node driver uses a specific credential provider from the AWS sdk called fromNodeProviderChain (https://github.com/mongodb/node-mongodb-native/blob/643a87553b5314abb44bcdc342a8bb8cb51d2052/src/cmap/auth/aws_temporary_credentials.ts#L99C49-L99C70)
This does not work for our case, as we would need the fromTemporaryCredentials provider.
So we monkey-patched the mongodb lib like this:
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; async function patchMongoAuth(): Promise<void> { const { AWSSDKCredentialProvider } = await import( // @ts-ignore 'mongodb/lib/cmap/auth/aws_temporary_credentials.js' ); AWSSDKCredentialProvider.prototype.getCredentials = async function (): Promise<{ AccessKeyId: string; Expiration?: Date; SecretAccessKey: string; Token?: string; }> { const provider = fromTemporaryCredentials({ clientConfig: { region: 'eu-central-1' }, params: { RoleArn: 'our-role-arn', DurationSeconds: 3600 }, }); const { accessKeyId, expiration, secretAccessKey, sessionToken } = await provider(); return { AccessKeyId: accessKeyId, Expiration: expiration, SecretAccessKey: secretAccessKey, Token: sessionToken, }; }; }
This works, but not in a lambda environment, since the Node driver set username and password by default to the AWS credentials stored in the environemnt variables. This happens in the `MongoCredentials` class constructor: MongoCredentials
But when the username is set then AWSSDKCredentialProvider.getCredentials is never called. But in a lambda environemnt the `AWS_ACCESS_KEY_ID` is always set.
This means, we now need to monkey patch the auth method as well and modify the auth context:
async function patchMongoAuth(): Promise<void> { const { AWSSDKCredentialProvider } = await import( // @ts-ignore 'mongodb/lib/cmap/auth/aws_temporary_credentials.js' ); const { MongoDBAWS } = await import( // @ts-ignore 'mongodb/lib/cmap/auth/mongodb_aws.js' ); const originalAuth = MongoDBAWS.prototype.auth; MongoDBAWS.prototype.auth = async function (authContext: any) { authContext.credentials = { source: '$external', mechanism: 'MONGODB-AWS', mechanismProperties: {}, }; return originalAuth.call(this, authContext); }; AWSSDKCredentialProvider.prototype.getCredentials = async function (): Promise<{ AccessKeyId: string; Expiration?: Date; SecretAccessKey: string; Token?: string; }> { const provider = fromTemporaryCredentials({ clientConfig: { region: 'eu-central-1' }, params: { RoleArn: 'our-role-arn', DurationSeconds: 3600 }, }); const { accessKeyId, expiration, secretAccessKey, sessionToken } = await provider(); return { AccessKeyId: accessKeyId, Expiration: expiration, SecretAccessKey: secretAccessKey, Token: sessionToken, }; }; }
Please
Could you make the AWS credential provider configurable? Or provide a different solution for this use case? I think it is rather common. If you have hundreds of lambdas, it is not feasible nor advisable from a security perspective to register each execution role in Atlas.
- depends on
-
NODE-6141 Allow users to provide custom AWS configuration
- Backlog