-
Notifications
You must be signed in to change notification settings - Fork 245
DRIVERS-2731 support named KMS providers #1492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Additional properties are now allowed to support specifying named KMS providers
To test automatic encryption for named KMS
AWS and local are not the only KMS providers supported. Remove sentence. Specifics of KMS providers are described in the `kmsProviders` section.
KMSProviderName was previously one of the strings "aws", "azure", "gcp", "local", or "kmip". The term "KMS provider" consists of the same set of strings, but also includes an optional name suffix. Use KMSProvider to match the term and avoid ambiguity with "KMS provider name".
Note that "aws" is a `KMS provider type`, not a `KMS provider`.
May help readers unfamiliar with typescript interfaces.
Named KMS providers are required to be non-empty. libmongocrypt will return the error: `empty name`
"^azure:[a-zA-Z0-9_]+?$"?: AzureKMSOptions; | ||
"^gcp:[a-zA-Z0-9_]+?$"?: GCPKMSOptions; | ||
"^local:[a-zA-Z0-9_]+?$"?: LocalKMSOptions; | ||
"^kmip:[a-zA-Z0-9_]+?$"?: KMIPKMSOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
source/client-side-encryption/etc/test-templates/namedKMS.yml.template
Outdated
Show resolved
Hide resolved
"additionalProperties": false, | ||
"patternProperties": { | ||
"^aws:[a-zA-Z0-9_]+?$": { | ||
"$ref": "#/definitions/kmsProviders/$defs/AWSKMSOptions" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the named providers don't support placeholders, I think this may warrant different definitions. You can probably revert to inlined definitions for "aws", "gcp" and "azure".
You may not even need $defs
for the "kmip" and "local" providers. If the "kmip" and "local" providers are identical, you can just tweak just their regex patterns to make the entire suffix optional (e.g. ^local(:[a-zA-Z0-9_]+)?$
) so it will apply to both cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the named providers don't support placeholders, I think this may warrant different definitions. You can probably revert to inlined definitions for "aws", "gcp" and "azure".
I think the named and unnamed can use the same definition. The aws:name2
KMS provider uses placeholders, since it is expected to be filled in with credentials for a different AWS account.
you can just tweak just their regex patterns to make the entire suffix optional (e.g.
^local(:[a-zA-Z0-9_]+)?$
) so it will apply to both cases.
I like that idea. Since the named providers also support placeholders, the named and unnamed properties have been combined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the named providers also support placeholders, the named and unnamed properties have been combined
Ah, I mixed up support for placeholders with automatic credentials. The automatic credentials are what the named providers don't support.
"type": "object", | ||
"additionalProperties": false, | ||
"patternProperties": { | ||
"^aws(:[a-zA-Z0-9_]+)?$": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted that the named providers don't support automatic credentials, but I don't think we need to address that in the schema. If anything, allowing an empty object would be helpful if we want to test that a driver raises a runtime error.
@@ -545,6 +599,11 @@ When such a state is detected, libmongocrypt_ will call back to the driver by | |||
entering the ``MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS`` state, upon which the | |||
driver should fill in the KMS options automatically. | |||
|
|||
Automatic credentials are only supported for the KMS provider types ``aws``, | |||
``gcp``, and ``azure``. KMS providers containing a name (e.g. ``aws:myname``) do | |||
not support automatic credentials. Attempting to configure a KMS provider with a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Named KMS providers do not support "Automatic Credentials".
It would be very simple to add support for this in Python, why do we have this limitation?
And by this I mean that all providers of the same type share one set of on demand creds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On-demand credentials are primarily intended to support passing credentials assigned in an environment. Supporting on-demand credentials for more than one KMS provider of the same type is not expected to be useful. Supporting on-demand KMS credentials would require added work in drivers inspecting the KMS providers (example in Java) when
obtaining credentials.
Seems fine to me since we can always add support later if needed. Could we include this rationale in the spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added rationale.
"aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } | ||
"azure:name1": { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } | ||
"gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } | ||
"kmip:name1": { endpoint: { $$placeholder: 1 } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To test this the driver test needs to also set the KMS TLS Options for "kmip:name1". Are we supposed to set that up automatically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. From the PR description:
The
name1
KMS providers may be configured exactly as the unnamed KMS providers. I.e.aws:name1
is configured the same asaws
.
The C driver implementation uses the same function to parse: https://github.com/mongodb/mongo-c-driver/pull/1509/files#diff-155d86e55a8ee5230e233a43f28e041a3e0c9c24ebf2403763c2220388fa2a10R1201
"local:name1": { key: { $$placeholder: 1 } } | ||
# Use a different local master key for `local:name2`. | ||
"local:name2": { key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } | ||
# Use a different AWS account to test wrapping between different AWS accounts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this use a different AWS account if aws:name2 uses the same placeholders as aws:name1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test runner is expected to use different credentials for the aws:name2
provider.
See the PR description for information on the different AWS account:
Test credentials for
aws:name2
are available in AWS Secrets Manager underdrivers/client-side-encryption
. Theaws:name2
account credentials start withAWSNAME2_
. See https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets for more background on how the secrets are managed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks this makes sense now. Could you update the CSE test readme to mention the aws:name2 credentials and the kmip:name1 KMS TLS options mentioned in the PR description?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
upsert: { $$unsetOrMatches: false } | ||
writeConcern: { w: majority } | ||
|
||
- description: "rewrap from aws:name1 to aws:name2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails in python with:
[2024/01/26 09:02:28.906] test/unified_format.py:1280: in _clientEncryptionOperation_rewrapManyDataKey
[2024/01/26 09:02:28.906] data = target.rewrap_many_data_key(*args, **kwargs)
[2024/01/26 09:02:28.906] pymongo/encryption.py:1057: in rewrap_many_data_key
[2024/01/26 09:02:28.906] with _wrap_encryption_errors():
[2024/01/26 09:02:28.906] /opt/python/3.10/lib/python3.10/contextlib.py:153: in __exit__
[2024/01/26 09:02:28.906] self.gen.throw(typ, value, traceback)
[2024/01/26 09:02:28.906] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[2024/01/26 09:02:28.906] @contextlib.contextmanager
[2024/01/26 09:02:28.906] def _wrap_encryption_errors() -> Iterator[None]:
[2024/01/26 09:02:28.906] """Context manager to wrap encryption related errors."""
[2024/01/26 09:02:28.906] try:
[2024/01/26 09:02:28.906] yield
[2024/01/26 09:02:28.906] except BSONError:
[2024/01/26 09:02:28.906] # BSON encoding/decoding errors are unrelated to encryption so
[2024/01/26 09:02:28.906] # we should propagate them unchanged.
[2024/01/26 09:02:28.906] raise
[2024/01/26 09:02:28.906] except Exception as exc:
[2024/01/26 09:02:28.906] > raise EncryptionError(exc) from None
[2024/01/26 09:02:28.906] E pymongo.errors.EncryptionError: Error in KMS response. HTTP status=400. Response body=
[2024/01/26 09:02:28.906] E {"__type":"AccessDeniedException","Message":"User: arn:aws:iam::579766882180:user/BUILD-7242 is not authorized to perform: kms:Encrypt on this resource because the resource does not exist in this Region, no resource-based policies allow access, or a resource-based policy explicitly denies access"}
Do you know what the issue could be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect this is due to aws:name2
being configured with the credentials from aws:name1
. The aws:name1
account deliberately cannot access the key in the aws:name2
account to ensure rewrapping uses separate accounts.
See the PR description for steps to get the aws:name2
credentials:
Test credentials for
aws:name2
are available in AWS Secrets Manager underdrivers/client-side-encryption
. Theaws:name2
account credentials start withAWSNAME2_
. See https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets for more background on how the secrets are managed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks this makes sense now.
…d `aws:name2` and `aws:name1` are different AWS accounts.
"privateKey": <set from environment>, | ||
"endpoint": "127.0.0.1:9002" | ||
}, | ||
"kmip:no_client_cert" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a colon after the name here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
"privateKey": <set from environment>, | ||
"endpoint": "127.0.0.1:9002" | ||
}, | ||
"kmip:with_tls" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a colon after the name here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
|
||
Test credentials to KMS are located in "drivers/client-side-encryption". | ||
|
||
Test credentials to create environments are available in "drivers/gcpkms" and "drivers/azurekms". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have these credentials in the existing "drivers/csfle" vault. Should we remove these other vaults in favor of "drivers/csfle"?
Note I just added FLE_AWS_KEY2 and FLE_AWS_SECRET2 to "drivers/csfle" today (for aws:name2).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC: @blink1073
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I used drivers/csfle
in mongodb-labs/drivers-evergreen-tools#390
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use drivers/csfle
and scheduled drivers/client-side-encryption
for deletion. Thank you for adding the secrets.
Should we remove these other vaults in favor of "drivers/csfle"?
I propose keeping drivers/gcpkms
and drivers/azurekms
separate. Those vaults are in use by Python and Go. I expect the Azure and GCP prose tests are run in a separate task group to manage the lifetime of the created VM. I do not see much value in combining the credentials.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with keeping them separate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Tests pass in Python.
Summary
This PR includes tests and documentation for the new feature: named KMS providers.
mongodb/mongo-c-driver#1509 is a reference implementation in the C driver.
Named KMS providers requires libmongocrypt with MONGOCRYPT-605. libmongocrypt binaries to test are available on the upload-all task.
Background
Previously supported KMS providers were only:
aws
,azure
,gcp
,kmip
, andlocal
. The KMS provider is now expanded to support name suffixes. (e.g.local:myname
).Named KMS providers enables more than one of each KMS provider type to be configured. Example: libmongocrypt can now support more than one
local
KMS provider configured:Named KMS providers is further described in DBX Scope: Support Named KMS Providers.
Tests
The following specification tests are added:
namedKMS.yml
tests automatic encryption.namedKMS-rewrapManyDataKey.yml
tests key rewrapping. This was noted as a motivation in DRIVERS-2731.namedKMS-explicit.yml
tests explicit encryption. This introduces use of theencrypt
anddecrypt
operations.namedKMS-createDataKey.yml
tests creating data keys with named KMS providers.The Unified Test Format schema 1.18 is added to allow
additionalProperties
patternProperties
inkmsProviders
.Prose Test 11 (
KMS TLS Options Tests
) is extended to test named KMS providers. This is motivated by necessary changes in the C driver to support named KMS providers in theKMSProvidersTLSOptions
.Tests refer to additional KMS providers:
local:name1
,aws:name1
,gcp:name1
,azure:name1
, andkmip:name1
.The
name1
KMS providers may be configured exactly as the unnamed KMS providers. I.e.aws:name1
is configured the same asaws
.To test configuring two KMS providers of the same type referring to distinct credentials, two more test KMS providers are defined:
local:name2
andaws:name2
.This PR proposes not adding
azure:name2
,gcp:name2
,kmip:name2
with distinct credentials. I expect there is little gained value in testing all external KMS providers. And it requires managing more accounts.Test credentials for
aws:name2
are available in AWS Secrets Manager underdrivers/csfle
. Theaws:name2
account credentials are inFLE_AWS_KEY2
andFLE_AWS_SECRET2
. See https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets for more background on how the secrets are managed.Please complete the following before merging:
[] Test these changes against all server versions and topologies (including standalone, replica set, shardedC does not run Client-Side Encryption tests with sharded or serverlessclusters, and serverless).