fix(sops): inject AWS credentials for KMS decryption via awsclicompat#992
Merged
yxxhero merged 1 commit intoFeb 17, 2026
Merged
Conversation
c71980c to
51ebdc5
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes issue #466 where the SOPS provider fails to decrypt files encrypted with AWS KMS, returning the error "Error getting data key: 0 successful groups required, got 0". The root cause was that SOPS's opaque decrypt.File()/decrypt.Data() APIs don't allow injection of AWS credentials, causing decryption failures when the encrypted file's metadata references an AWS profile that doesn't exist on the decrypting machine.
Changes:
- Replaced opaque SOPS decrypt APIs with a custom decryption pipeline that injects AWS credentials via
awsclicompat.NewConfig(), bringing consistency with other AWS providers (awssecrets, ssm, awskms, s3) - Added
kmsAwareServerthat intercepts KMS key operations to inject credentials while delegating all other key types (PGP, Age, GCP KMS, Azure, Vault) to the default SOPS keyservice - Added support for
region,profile, androle_arnquery parameters in SOPS ref+ URIs - Comprehensive test coverage including unit tests for the new conversion logic, delegation behavior, pointer independence, and configuration parsing
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
pkg/providers/sops/sops.go |
Core implementation: added AWS config fields, kmsAwareServer for credential injection, kmsKeyToMasterKey helper, and rewrote decrypt() to use custom decryption pipeline |
pkg/providers/sops/sops_test.go |
Added 5 new unit tests covering kmsKeyToMasterKey conversion, pointer independence, non-KMS delegation, and AWS config parsing |
vals.go |
Pass awsLogLevel parameter to sops.New() |
pkg/stringprovider/stringprovider.go |
Pass awsLogLevel parameter to sops.New() |
pkg/stringmapprovider/stringmapprovider.go |
Pass awsLogLevel parameter to sops.New() |
51ebdc5 to
5a775b8
Compare
The SOPS provider used the opaque decrypt.File()/decrypt.Data() API
which constructs KMS MasterKey objects without injecting any AWS
credentials. This caused decryption failures ("0 successful groups
required, got 0") when the encrypted file's metadata references an
AwsProfile that doesn't exist on the decrypting machine.
Replace the opaque decrypt calls with a custom decryption pipeline that
uses a kmsAwareServer (implementing keyservice.KeyServiceServer) to
intercept KMS key operations and inject credentials built via
awsclicompat.NewConfig() — the same credential chain used by all other
AWS providers (awssecrets, ssm, awskms, s3).
Non-KMS key types (PGP, Age, GCP KMS, Azure, Vault) delegate to the
default keyservice.Server unchanged.
Fixes helmfile#466
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
5a775b8 to
525d362
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #466 — SOPS provider fails to decrypt files encrypted with AWS KMS:
Root cause:
decrypt.File()/decrypt.Data()is an opaque API that internally createskeyservice.LocalClient→keyservice.Server→kms.MasterKeywithout injecting AWS credentials. TheMasterKeythen callsconfig.LoadDefaultConfig()and may force anAwsProfilefrom the encrypted file's metadata that doesn't exist on the decrypting machine.Fix: Replace the opaque
decryptAPI with a custom decryption pipeline that injects AWS credentials via akmsAwareServer(a customkeyservice.KeyServiceServer). This brings the SOPS provider in line with every other AWS provider in vals (awssecrets, ssm, awskms, s3), all of which useawsclicompat.NewConfig().What changed
pkg/providers/sops/sops.goRegion,Profile,RoleARN,AWSLogLevelfields to the provider structNew()to acceptawsLogLeveland readregion/profile/role_arnfrom configkmsAwareServer— implementskeyservice.KeyServiceServer:KmsKey→kms.MasterKey, injects credentials viakms.NewCredentialsProvider(cp).ApplyToMasterKey(), then encrypts/decryptskeyservice.ServerkmsKeyToMasterKey()helper (replicates unexported function fromkeyservice/server.go)decrypt()to: read file → detect format → parse encrypted tree → build credentials viaawsclicompat.NewConfig()→ get data key via custom key service → decrypt → verify MAC → emit plaintextvals.go,pkg/stringprovider/,pkg/stringmapprovider/awsLogLeveltosops.New()at all three call sitespkg/providers/sops/sops_test.goTestKmsKeyToMasterKey— verifies all field conversions (Arn, Role, Context, AwsProfile)TestKmsKeyToMasterKeyContextPointerIndependence— verifies encryption context map values don't alias the loop variableTestKmsAwareServerDelegatesNonKms— verifies non-KMS keys (Age, PGP) delegate to the default serverTestNewProviderReadsAWSConfig— verifies region/profile/role_arn parsed from configTestNewProviderDefaultKeyType— verifies default key_type is "filepath"Backward compatibility
awsclicompatuses defaults (env vars, shared config) — same as all other AWS providerskeyservice.Serverawsclicompat.NewConfig()fails: falls back to nil credentials (original SOPS default behavior)formatandkey_typeconfig params: unchangedNew ref+ URI parameters
Test plan
go build ./...compiles cleanlygo test ./pkg/providers/sops/...— all 8 tests passgo test ./...— full suite passes (only pre-existing k8s test failures due to missing kind-cluster)