Support AWS KMS as a tenant credential key provider
- Dominant language
- TypeScript
- Stars
- 156k
- Forks
- 24.6k
- Avg merge
- 22h 9m
- Merged PRs (30d)
- 610
Description
### Self Checks
- [X] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [X] I have searched for existing issues, including closed ones.
- [X] I confirm that I am using English to submit this report.
- [X] Please do not modify this template :) and fill in all the required fields.
### 1. Is this request related to a challenge you're experiencing? Tell me about your story.
`KEY_PROVIDER_TYPE` currently accepts `local` and `azure-keyvault`. With `local`, the RSA private key that decrypts every tenant credential is written to the `STORAGE_TYPE` backend, so on a self-hosted deployment it sits on the same disk as the ciphertext it protects:
```
storage/privkeys//private.pem (0644)
```
Anyone who can read that volume — a host compromise, a copied EBS snapshot, an unencrypted backup — gets both halves at once. That is also the first thing an enterprise security review asks about, and "next to the application" is a hard answer to defend.
#39933 solved this for Azure by keeping the key in Key Vault. AWS deployments have no equivalent, even though the original request (#32328) asked for AWS Secrets Manager, Azure Key Vault and HashiCorp Vault together, and only Azure shipped.
### 2. Additional context or comments
I would like to add an `aws-kms` provider. `BaseKeyProvider` already has everything needed, and `boto3` is already a dependency (used by the S3 storage backend), so no new dependency is introduced.
Proposed design, which deliberately does **not** mirror the Azure provider one-for-one:
- **One symmetric KMS key for all tenants, separated by encryption context** rather than a key per tenant. Per-tenant keys bill per key and would require `kms:CreateKey` at runtime, a permission operators are reluctant to grant an application. A `dify:tenant_id` encryption context still binds each wrapped data key to its tenant cryptographically (KMS refuses a mismatched context), appears in CloudTrail, and can be enforced in IAM via the `kms:EncryptionContext:dify:tenant_id` condition key. Dify would need only `kms:GenerateDataKey` and `kms:Decrypt`.
- **No key version pinned in the envelope.** A KMS CiphertextBlob already names the backing key that produced it and KMS retains superseded backing keys, so automatic rotation keeps old credentials decryptable with nothing to pin — the concern `AzureKeyVaultKeyProvider` documents at length does not arise. (Automatic rotation is also only offered for symmetric keys, which is a further argument against copying the per-tenant asymmetric model.)
- Envelope layout and error handling kept identical to the Azure provider, including translating botocore failures into `ValueError` so `core/provider_manager.py` and `services/model_load_balancing_service.py` keep behaving as they do today.
New settings would be `AWS_KMS_KEY_ID` (required), `AWS_KMS_REGION` and `AWS_KMS_ENDPOINT_URL` (both optional). Credentials resolve through the default boto3 chain, so no long-lived secret has to be handed to Dify to reach the key that protects the other secrets.
I have an implementation ready with unit tests that mock the AWS SDK (no network calls, matching the approach in `test_azure_keyvault_key_provider.py`); `make lint`, the import-linter contracts and the key provider tests all pass. Happy to open the PR, or to adjust the design first if a per-tenant key or a Secrets Manager based approach is preferred.
@wylswz since you built the key provider abstraction in #39933 — does this direction look right to you?
Contributor guide
Research direction
Start with the existing BaseKeyProvider and AzureKeyVaultKeyProvider, then read core/provider_manager.py, services/model_load_balancing_service.py, and test_azure_keyvault_key_provider.py. Add the AWS KMS provider using the stated settings, envelope behavior, encryption context, and botocore error handling; done means mocked unit tests, key provider tests, import-linter contracts, and make lint pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, python
- Domain
- backend, cloud, security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100