Background and Problem
My Node v18 server application is deployed on AWS Elastic Beanstalk on an EC2 instance running Amazon Linux 2023. I've set up my .ebextensions
to set up the Mongo Enterprise repository, adapted from here, and I've installed the mongo-enterprise-cryptd
binary from that repo in order to support the mongo-client-encryption
node library that my app server needs to validate the fields I'm explicitly encrypting and implicitly decrypting.
My initialization code sets the KMS provider to AWS, and sets its value to a blank object {}
, which prompts AWS to look for an IAM role that injects the environment values at runtime, as described in the "Important" section just below here.
Since Mongo has not yet released the core C encryption library libmongocrypt
as a package for Amazon Linux 2023 (afaik as of Dec 2023; Here's the MongoDB JIRA ticket I created), I've compiled it from source on my EC2 instance. After this I installed and started the mongocryptd
process, then installed the mongo-client-encryption
node driver. This all appears to work as my app does not complain about any packages being missing.
During my application's initialization of the encryption keystore database, I call ClientEncryption.createDataKey()
with the KMS options described in the docs, and I one of my catch blocks throws this error:
/var/app/current/my-server/src/encryption.ts:167
throw new Error('Unable to create Data Encryption Key (DEK)::' + err);
^
Error: Unable to create Data Encryption Key (DEK)::MongoCryptError: Unexpected field: 'expiration'
at call (/var/app/current/my-server/src/encryption.ts:167:15)
at tryCatch (/var/app/current/my-server/src/encryption.ts:2:1)
at Generator._invoke (/var/app/current/my-server/src/encryption.ts:2:1)
at Generator.throw (/var/app/current/my-server/src/encryption.ts:2:1)
at asyncGeneratorStep (/var/app/current/my-server/src/encryption.ts:2:1)
at _throw (/var/app/current/my-server/src/encryption.ts:2:1)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
This part: MongoCryptError: Unexpected field: 'expiration'
is the salient bit.
I do not define an encryption schema at any point in my code since I'm using explicit encryption. I do not provide a field in the KMS or connection options that is called 'expiration'. I do not have a field anywhere in my mongoose
schemas that is called expiration
.
Questions:
- Is Amazon injecting this field when the Mongo node driver fetches the KMS credentials?
- If not, where is this field coming from?
- How can I resolve this or bypass checks to fields I do not care about?
Attempted:
- Attempted to call the
ClientEncryption.createDataKey()
method with "appropriate" configuration options
Expected:
- Data encryption key to be created and code to continue executing
Actual:
- Received an error referencing a field key that does not exist anywhere in my code base:
MongoCryptError: Unexpected field: 'expiration'
Cause: Missing Environment Variables in AWS Elastic Beanstalk (EB)
Background
The cryptic error:
MongoCryptError: Unexpected field: 'expiration'
originates from thelibmongocrypt
library when it has received an object that has different keys than it expects. It then re-throws the error wrapped as a NodeJS MongoCryptError, making the source of this error hard to find.The word "expiration" eventually led me to the MONGODB_AWS specification and this resolved MongoDB JIRA ticket that explains how the client-side field-level encryption (CSFLE) implementation wasn't aligned with the rest of the codebase in terms of authentication processes.
In my case,
libmongocrypt
was throwing an authentication error. It wasn't able to find the AWS credentials needed to authenticate with the AWS KMS service in the EB env vars:AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
.Why this was confusing for me
According the Mongo docs here, in the "Important!" box, the wording is:
To me, this meant I could remove the credentials in the env vars because I assumed that there was a mechanism in place to authenticate somehow using the IAM Role I had set up and attached to my EC2 instance with appropriate access to my KMS resource.
But this isn't the case: you need to have both
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
defined in your environment to avoid this error.