Skip to content

fix(sops): inject AWS credentials for KMS decryption via awsclicompat#992

Merged
yxxhero merged 1 commit into
helmfile:mainfrom
aditmeno:fix/sops-aws-kms-credential-injection
Feb 17, 2026
Merged

fix(sops): inject AWS credentials for KMS decryption via awsclicompat#992
yxxhero merged 1 commit into
helmfile:mainfrom
aditmeno:fix/sops-aws-kms-credential-injection

Conversation

@aditmeno
Copy link
Copy Markdown
Contributor

Summary

Fixes #466 — SOPS provider fails to decrypt files encrypted with AWS KMS:

Error getting data key: 0 successful groups required, got 0

Root cause: decrypt.File()/decrypt.Data() is an opaque API that internally creates keyservice.LocalClientkeyservice.Serverkms.MasterKey without injecting AWS credentials. The MasterKey then calls config.LoadDefaultConfig() and may force an AwsProfile from the encrypted file's metadata that doesn't exist on the decrypting machine.

Fix: Replace the opaque decrypt API with a custom decryption pipeline that injects AWS credentials via a kmsAwareServer (a custom keyservice.KeyServiceServer). This brings the SOPS provider in line with every other AWS provider in vals (awssecrets, ssm, awskms, s3), all of which use awsclicompat.NewConfig().

What changed

pkg/providers/sops/sops.go

  • Added Region, Profile, RoleARN, AWSLogLevel fields to the provider struct
  • Updated New() to accept awsLogLevel and read region/profile/role_arn from config
  • Added kmsAwareServer — implements keyservice.KeyServiceServer:
    • KMS keys: converts protobuf KmsKeykms.MasterKey, injects credentials via kms.NewCredentialsProvider(cp).ApplyToMasterKey(), then encrypts/decrypts
    • All other key types (PGP, Age, GCP KMS, Azure, Vault): delegates to embedded keyservice.Server
  • Added kmsKeyToMasterKey() helper (replicates unexported function from keyservice/server.go)
  • Rewrote decrypt() to: read file → detect format → parse encrypted tree → build credentials via awsclicompat.NewConfig() → get data key via custom key service → decrypt → verify MAC → emit plaintext

vals.go, pkg/stringprovider/, pkg/stringmapprovider/

  • Pass awsLogLevel to sops.New() at all three call sites

pkg/providers/sops/sops_test.go

  • TestKmsKeyToMasterKey — verifies all field conversions (Arn, Role, Context, AwsProfile)
  • TestKmsKeyToMasterKeyContextPointerIndependence — verifies encryption context map values don't alias the loop variable
  • TestKmsAwareServerDelegatesNonKms — verifies non-KMS keys (Age, PGP) delegate to the default server
  • TestNewProviderReadsAWSConfig — verifies region/profile/role_arn parsed from config
  • TestNewProviderDefaultKeyType — verifies default key_type is "filepath"

Backward compatibility

  • No AWS params configured: awsclicompat uses defaults (env vars, shared config) — same as all other AWS providers
  • Non-KMS SOPS files: PGP, Age, etc. work identically — custom server delegates to default keyservice.Server
  • awsclicompat.NewConfig() fails: falls back to nil credentials (original SOPS default behavior)
  • format and key_type config params: unchanged

New ref+ URI parameters

ref+sops://path/to/file.yaml?region=us-east-1&profile=myprofile&role_arn=arn:aws:iam::123:role/myrole#/key

Test plan

  • go build ./... compiles cleanly
  • go test ./pkg/providers/sops/... — all 8 tests pass
  • go test ./... — full suite passes (only pre-existing k8s test failures due to missing kind-cluster)
  • Manual test with a SOPS-encrypted YAML file using KMS

@aditmeno aditmeno force-pushed the fix/sops-aws-kms-credential-injection branch 2 times, most recently from c71980c to 51ebdc5 Compare February 16, 2026 01:51
@aditmeno aditmeno requested a review from Copilot February 16, 2026 01:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 kmsAwareServer that 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, and role_arn query 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()

Comment thread pkg/providers/sops/sops.go
Comment thread pkg/providers/sops/sops.go
@aditmeno aditmeno force-pushed the fix/sops-aws-kms-credential-injection branch from 51ebdc5 to 5a775b8 Compare February 16, 2026 02:16
@aditmeno aditmeno requested a review from Copilot February 16, 2026 02:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread pkg/providers/sops/sops.go
Comment thread pkg/providers/sops/sops_test.go
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>
@aditmeno aditmeno force-pushed the fix/sops-aws-kms-credential-injection branch from 5a775b8 to 525d362 Compare February 16, 2026 02:26
@aditmeno aditmeno requested a review from Copilot February 16, 2026 02:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@yxxhero yxxhero merged commit 9484184 into helmfile:main Feb 17, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.37.3 is unable to decrypt using sops provider encrypted via KMS, 0.37.2 works perfectly.

3 participants