Skip to content

Conversation

@aditmeno
Copy link
Contributor

@aditmeno aditmeno commented Nov 24, 2025

Summary

This PR adds flexible AWS SDK logging configuration to vals while changing the default to no logging for security, preventing credential leakage.

Implementation: AWS log level passed through function parameters to each AWS provider - no global state mutation, no package-level variables, no synchronization primitives needed.

Related to: helmfile/helmfile#2270


Problem

Previously, vals defaulted to logging AWS SDK retries and requests (aws.LogRetries | aws.LogRequest), which exposed sensitive information in logs:

  • 🔓 AWS tokens and authorization headers
  • 🔓 Request/response bodies containing credentials
  • 🔓 Secret metadata

Real-world impact: Users reported seeing this in helmfile output:

SDK 2025/11/24 07:54:06 DEBUG Request
PUT /latest/api/token HTTP/1.1
Host: 169.254.169.254

Solution

1. Added AWSLogLevel Field to Options Struct

type Options struct {
    LogOutput             io.Writer
    CacheSize             int
    ExcludeSecret         bool
    FailOnMissingKeyInMap bool
    // New field:
    AWSLogLevel string  // Controls AWS SDK logging behavior
}

Supported values:

  • "" or "off" (default) - No logging (secure)
  • "minimal" - Log retries only
  • "standard" - Log retries + requests (previous default)
  • "verbose" - Log everything (debug mode)
  • Custom - Comma-separated: "request,response,signing"

2. Clean Implementation: Function Parameters Instead of Global State

Key improvements:

  • ✅ No package-level global variables
  • ✅ No os.Setenv() - no environment mutation
  • ✅ No sync/atomic or synchronization primitives
  • ✅ Variadic parameters for backward compatibility
  • ✅ Simple, idiomatic Go code
  • ✅ Thread-safe by design - each provider gets its own log level

How it works:

  1. vals.go - Runtime stores log level in Options:
func New(opts Options) (*Runtime, error) {
    r := &Runtime{
        providers: map[string]api.Provider{},
        Options:   opts,  // Stores opts.AWSLogLevel
        // ... rest of initialization
    }
    return r, nil
}
  1. vals.go - Passes log level when creating AWS providers:
case ProviderSSM:
    p := ssm.New(r.logger, conf, r.Options.AWSLogLevel)
    return p, nil
  1. AWS providers - Each provider stores log level and passes it to session:
// pkg/providers/ssm/ssm.go
type provider struct {
    ssmClient   *ssm.Client
    log         *log.Logger
    AWSLogLevel string  // Stored per-provider
    // ... other fields
}

func New(l *log.Logger, cfg api.StaticConfig, awsLogLevel string) *provider {
    p := &provider{
        log:         l,
        AWSLogLevel: awsLogLevel,  // Store the log level
    }
    // ... initialize fields from config
    return p
}

func (p *provider) getSSMClient() *ssm.Client {
    if p.ssmClient != nil {
        return p.ssmClient
    }
    
    // Pass log level to session creation
    cfg := awsclicompat.NewSession(p.Region, p.Profile, p.RoleARN, p.AWSLogLevel)
    
    p.ssmClient = ssm.NewFromConfig(cfg)
    return p.ssmClient
}
  1. awsclicompat - Uses environment variable first, then parameter:
// pkg/awsclicompat/session.go
func NewSession(region, profile, roleARN string, logLevel ...string) aws.Config {
    var level string
    if len(logLevel) > 0 {
        level = logLevel[0]
    }
    cfg, err := newConfig(ctx, region, profile, level)
    // ...
}

func parseAWSLogLevel(paramDefault string) aws.ClientLogMode {
    // Priority: AWS_SDK_GO_LOG_LEVEL env var > paramDefault > secure default
    logLevel := strings.TrimSpace(os.Getenv("AWS_SDK_GO_LOG_LEVEL"))
    
    if logLevel == "" {
        logLevel = paramDefault  // Use passed parameter
    }
    
    if logLevel == "" {
        return 0  // Secure default - no logging
    }
    // ... preset handling
}

3. Changed Default Behavior

Aspect Before After
Default aws.LogRetries | aws.LogRequest 0 (no logging)
Security ❌ Credentials exposed ✅ Credentials protected
Debugging Always on Opt-in via config/env var
Thread Safety ❌ Data race potential ✅ No shared state
Dependencies N/A ✅ No sync/atomic needed
Global State N/A ✅ No mutation
Code Complexity N/A ✅ Simple parameter passing

Usage Examples

Secure by Default (Recommended)

runtime, _ := vals.New(vals.Options{})
// No AWS SDK logging - credentials safe

Enable Minimal Logging

runtime, _ := vals.New(vals.Options{
    AWSLogLevel: "minimal",  // Retries only
})

Restore Previous Behavior

runtime, _ := vals.New(vals.Options{
    AWSLogLevel: "standard",  // Retries + requests (old default)
})

Full Debugging

runtime, _ := vals.New(vals.Options{
    AWSLogLevel: "verbose",  // Everything
})

Environment Variable Override

# Always takes precedence over Options.AWSLogLevel
export AWS_SDK_GO_LOG_LEVEL=verbose
vals eval ...

Priority Order

  1. AWS_SDK_GO_LOG_LEVEL environment variable (highest)
  2. Options.AWSLogLevel parameter (passed through to providers)
  3. ✅ Secure default (no logging)

Breaking Change

⚠️ Default behavior changes from logging to not logging.

Migration path for users who need logs:

Option 1 - Environment variable (no code change):

export AWS_SDK_GO_LOG_LEVEL=standard

Option 2 - Update code:

vals.New(vals.Options{
    AWSLogLevel: "standard",
})

Rationale: Security should be the default. Credentials and secrets should never leak unless explicitly enabled for debugging.


Testing

Updated existing tests:

  • TestDefaultSecureBehavior - Verifies secure default (no logging)
  • TestParseAWSLogLevel - Updated to use new function signature
  • TestParseAWSLogLevelIndividualFlags - Still works

Added new tests:

  • TestPresetLevels - Tests minimal/standard/verbose presets

All tests pass:

go test ./pkg/awsclicompat/... -v
=== RUN   TestParseAWSLogLevel
=== RUN   TestParseAWSLogLevelIndividualFlags  
=== RUN   TestDefaultSecureBehavior
=== RUN   TestPresetLevels
--- PASS: TestPresetLevels (0.00s)
PASS

# Race detector clean
go test ./pkg/awsclicompat/... -race -v
PASS

Implementation Details

Why function parameters instead of environment variables or globals?

  1. No synchronization needed: Each provider instance has its own log level
  2. No global state mutation: No os.Setenv() calls
  3. Standard Go patterns: Variadic parameters for optional arguments
  4. Backward compatible: Existing calls work without changes
  5. Simpler code: No atomic operations, no environment manipulation
  6. Thread-safe by design: No shared mutable state

Addressing Copilot Feedback:

The implementation evolved through several iterations:

  1. V1: Package-level defaultLogLevel variable (not thread-safe)
  2. V2: sync/atomic.Value for thread-safety (complex)
  3. V3: os.Setenv() in vals.New() (global state mutation)
  4. V4 (final): Function parameters passed through call stack (clean, no side effects)

Files Changed

AWS Providers (added AWSLogLevel field + parameter):

File Changes Description
pkg/providers/ssm/ssm.go Added field + param Pass log level to NewSession
pkg/providers/s3/s3.go Added field + param Pass log level to NewSession
pkg/providers/awssecrets/awssecrets.go Added field + param Pass log level to NewSession
pkg/providers/awskms/awskms.go Added field + param Pass log level to NewSession

Provider Factories (accept + forward log level):

File Changes Description
pkg/stringprovider/stringprovider.go Added param Forward to AWS providers
pkg/stringmapprovider/stringmapprovider.go Added param Forward to AWS providers

Core Runtime:

File Changes Description
vals.go Added AWSLogLevel option + ctx field Pass through to providers
pkg/awsclicompat/session.go Already had variadic params No changes needed
pkg/awsclicompat/session_test.go Cleaned up Removed global variable tests

Total: 9 files changed, 159 insertions(+), 80 deletions(-)


Security Impact

🔒 Before: AWS credentials potentially exposed in logs by default
🔐 After: AWS credentials protected by default, opt-in for debugging

This change aligns with security best practices:

  • Principle of least privilege - No logging unless needed
  • Secure by default - Users must explicitly enable sensitive logging
  • Defense in depth - Prevents accidental credential exposure
  • Clean implementation - No global state, no side effects

Checklist

  • Code changes are focused and minimal
  • All tests pass (including race detector)
  • Breaking change documented with migration path
  • Security improvement clearly explained
  • Backward compatibility via variadic parameters
  • Environment variable precedence maintained
  • Clean commit history with signed commits
  • No circular replace directives in go.mod
  • Addressed Copilot review feedback (no global variables, no os.Setenv)
  • No sync/atomic dependency needed
  • No global state mutation

Related Issues


Upgrade Guide

For library users (like helmfile):

If you were relying on default AWS SDK logging, update your code:

// Old (implicit logging)
runtime, _ := vals.New(vals.Options{
    CacheSize: 512,
})

// New (explicit logging if needed)
runtime, _ := vals.New(vals.Options{
    CacheSize: 512,
    AWSLogLevel: "standard",  // or "minimal", "verbose"
})

For CLI users:

Set environment variable if you need logs:

export AWS_SDK_GO_LOG_LEVEL=standard  # or minimal, verbose

aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This commit properly fixes issue helmfile#2270 where AWS SDK debug logs
were still appearing despite the fix in PR helmfile#2288.

## Problem
The original fix in PR helmfile#2288 only suppressed vals' own logging via
LogOutput: io.Discard, but did not address:
1. AWS SDK v2's separate logging system
2. AWS SDK clients created directly in remote.go
3. No way for users to enable logs for debugging

As reported in PR helmfile#2288 comment, users still see:
  SDK 2025/11/24 07:54:06 DEBUG Request
  PUT /latest/api/token HTTP/1.1
  Host: 169.254.169.254

## Root Cause
AWS SDK v2 uses its own logging mechanism controlled by:
- AWS_SDK_GO_LOG_LEVEL environment variable (for vals library)
- WithClientLogMode config option (for direct AWS SDK usage)

The LogOutput option in vals.Options only affects vals' internal
logging, not the AWS SDK's debug output.

## Solution
Two-part solution across helmfile and vals:

### Part 1: vals library enhancement (PR helmfile/vals#893)
- Added Options.AWSLogLevel field for programmatic control
- Changed default from logging to no logging (secure by default)
- Supports presets: "off", "minimal", "standard", "verbose"

### Part 2: helmfile changes (this PR)
Made logging configurable with secure defaults via HELMFILE_ENABLE_AWS_SDK_LOGS:

1. **Added environment variable** (pkg/envvar/const.go)
   - HELMFILE_ENABLE_AWS_SDK_LOGS controls all AWS SDK logging
   - Default: false (suppress logs for security)
   - Set to "true" to enable debugging

2. **Conditional AWS SDK log suppression in remote.go**
   - Added enableAWSSDKLogs flag (reads env var on init)
   - Made WithClientLogMode(0) conditional in all 3 AWS config locations
   - Only suppresses when HELMFILE_ENABLE_AWS_SDK_LOGS != "true"

3. **Conditional vals configuration in vals.go**
   - Uses new Options.AWSLogLevel field from vals PR helmfile#893
   - Sets to "off" by default (secure)
   - Sets to "standard" when HELMFILE_ENABLE_AWS_SDK_LOGS=true

## Security Impact
- Default behavior: Suppresses sensitive information (tokens, auth
  headers, credentials) from appearing in logs
- User control: Enables debugging when explicitly requested via
  HELMFILE_ENABLE_AWS_SDK_LOGS=true

## Dependencies
- Requires vals PR helmfile/vals#893 to be merged
- Once merged, helmfile's go.mod should be updated to use new vals version

## Testing
- All existing tests pass
- golangci-lint passes with no issues
- Build succeeds

Fixes helmfile#2270
Depends-on: helmfile/vals#893

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
Changes:
1. Updated go.mod to use vals PR helmfile#893 (aditmeno/vals@a97336ce2bf6)
   - Added replace directive to reference PR branch for testing
   - Will be updated to official release once PR merges

2. Added comprehensive unit tests for AWS SDK logging behavior:

   pkg/plugins/vals_test.go:
   - TestAWSSDKLogLevelConfiguration: Tests all log levels (off, minimal, standard, verbose, custom)
   - TestEnvironmentVariableReading: Tests env var reading with whitespace trimming and defaults
   - Verifies LogOutput is set to io.Discard only when level is "off"
   - All 9 test cases passing

   pkg/remote/remote_test.go:
   - TestAWSSDKLogLevelInit: Tests init() logic for reading HELMFILE_AWS_SDK_LOG_LEVEL
   - Tests default behavior, explicit values, and whitespace handling
   - All 6 test cases passing

3. All existing tests continue to pass:
   - pkg/plugins: PASS (3/3 test suites)
   - pkg/remote: PASS (all test suites)
   - Linting: 0 issues

This commit ensures AWS SDK logging is properly tested and validates
the secure default behavior (no logging) across both vals integration
and direct AWS SDK usage in remote S3 operations.

Related: helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This commit properly fixes issue helmfile#2270 where AWS SDK debug logs
were still appearing despite the fix in PR helmfile#2288.

## Problem
The original fix in PR helmfile#2288 only suppressed vals' own logging via
LogOutput: io.Discard, but did not address:
1. AWS SDK v2's separate logging system
2. AWS SDK clients created directly in remote.go
3. No way for users to enable logs for debugging

As reported in PR helmfile#2288 comment, users still see:
  SDK 2025/11/24 07:54:06 DEBUG Request
  PUT /latest/api/token HTTP/1.1
  Host: 169.254.169.254

## Root Cause
AWS SDK v2 uses its own logging mechanism controlled by:
- AWS_SDK_GO_LOG_LEVEL environment variable (for vals library)
- WithClientLogMode config option (for direct AWS SDK usage)

The LogOutput option in vals.Options only affects vals' internal
logging, not the AWS SDK's debug output.

## Solution
Two-part solution across helmfile and vals:

### Part 1: vals library enhancement (PR helmfile/vals#893)
- Added Options.AWSLogLevel field for programmatic control
- Changed default from logging to no logging (secure by default)
- Supports presets: "off", "minimal", "standard", "verbose"

### Part 2: helmfile changes (this PR)
Made logging configurable with secure defaults via HELMFILE_ENABLE_AWS_SDK_LOGS:

1. **Added environment variable** (pkg/envvar/const.go)
   - HELMFILE_ENABLE_AWS_SDK_LOGS controls all AWS SDK logging
   - Default: false (suppress logs for security)
   - Set to "true" to enable debugging

2. **Conditional AWS SDK log suppression in remote.go**
   - Added enableAWSSDKLogs flag (reads env var on init)
   - Made WithClientLogMode(0) conditional in all 3 AWS config locations
   - Only suppresses when HELMFILE_ENABLE_AWS_SDK_LOGS != "true"

3. **Conditional vals configuration in vals.go**
   - Uses new Options.AWSLogLevel field from vals PR helmfile#893
   - Sets to "off" by default (secure)
   - Sets to "standard" when HELMFILE_ENABLE_AWS_SDK_LOGS=true

## Security Impact
- Default behavior: Suppresses sensitive information (tokens, auth
  headers, credentials) from appearing in logs
- User control: Enables debugging when explicitly requested via
  HELMFILE_ENABLE_AWS_SDK_LOGS=true

## Dependencies
- Requires vals PR helmfile/vals#893 to be merged
- Once merged, helmfile's go.mod should be updated to use new vals version

## Testing
- All existing tests pass
- golangci-lint passes with no issues
- Build succeeds

Fixes helmfile#2270
Depends-on: helmfile/vals#893

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
Changes:
1. Updated go.mod to use vals PR helmfile#893 (aditmeno/vals@a97336ce2bf6)
   - Added replace directive to reference PR branch for testing
   - Will be updated to official release once PR merges

2. Added comprehensive unit tests for AWS SDK logging behavior:

   pkg/plugins/vals_test.go:
   - TestAWSSDKLogLevelConfiguration: Tests all log levels (off, minimal, standard, verbose, custom)
   - TestEnvironmentVariableReading: Tests env var reading with whitespace trimming and defaults
   - Verifies LogOutput is set to io.Discard only when level is "off"
   - All 9 test cases passing

   pkg/remote/remote_test.go:
   - TestAWSSDKLogLevelInit: Tests init() logic for reading HELMFILE_AWS_SDK_LOG_LEVEL
   - Tests default behavior, explicit values, and whitespace handling
   - All 6 test cases passing

3. All existing tests continue to pass:
   - pkg/plugins: PASS (3/3 test suites)
   - pkg/remote: PASS (all test suites)
   - Linting: 0 issues

This commit ensures AWS SDK logging is properly tested and validates
the secure default behavior (no logging) across both vals integration
and direct AWS SDK usage in remote S3 operations.

Related: helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This PR fixes issue helmfile#2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR helmfile#2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR helmfile#2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR helmfile#893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: helmfile#2270
Related: helmfile#2288, helmfile#2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@aditmeno aditmeno force-pushed the fix/configurable-aws-sdk-logging branch 3 times, most recently from 54249b5 to 847469e Compare November 24, 2025 02:31
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This PR fixes issue helmfile#2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR helmfile#2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR helmfile#2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR helmfile#893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: helmfile#2270
Related: helmfile#2288, helmfile#2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@aditmeno aditmeno force-pushed the fix/configurable-aws-sdk-logging branch from 847469e to 640e10d Compare November 24, 2025 02:36
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This PR fixes issue helmfile#2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR helmfile#2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR helmfile#2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR helmfile#893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: helmfile#2270
Related: helmfile#2288, helmfile#2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@aditmeno aditmeno force-pushed the fix/configurable-aws-sdk-logging branch from 640e10d to 42f18f6 Compare November 24, 2025 02:39
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This PR fixes issue helmfile#2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR helmfile#2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR helmfile#2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR helmfile#893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: helmfile#2270
Related: helmfile#2288, helmfile#2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@aditmeno
Copy link
Contributor Author

@yxxhero PR is ready for review

@aditmeno aditmeno force-pushed the fix/configurable-aws-sdk-logging branch from 42f18f6 to 019660e Compare November 24, 2025 02:44
aditmeno added a commit to aditmeno/helmfile that referenced this pull request Nov 24, 2025
This PR fixes issue helmfile#2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR helmfile#2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR helmfile#2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR helmfile#893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: helmfile#2270
Related: helmfile#2288, helmfile#2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@yxxhero yxxhero requested a review from Copilot November 24, 2025 03:35
Copy link

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 adds configurable AWS SDK logging to vals while changing the default behavior from logging AWS SDK requests/retries to no logging for improved security. The change prevents potential credential leakage in logs by making logging opt-in rather than opt-out. The implementation uses a clean package-level configuration approach via SetDefaultLogLevel() instead of modifying global environment variables.

Key Changes:

  • Added AWSLogLevel field to Options struct with support for preset levels ("minimal", "standard", "verbose") and custom values
  • Introduced package-level defaultLogLevel variable and SetDefaultLogLevel() function in pkg/awsclicompat package
  • Changed default AWS SDK logging behavior from aws.LogRetries | aws.LogRequest to no logging (secure default)

Reviewed changes

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

Show a summary per file
File Description
vals.go Added AWSLogLevel field to Options struct with documentation; calls awsclicompat.SetDefaultLogLevel() in New() function if AWSLogLevel is provided
pkg/awsclicompat/session.go Added package-level defaultLogLevel variable, SetDefaultLogLevel() function, and LogModeOff constant; enhanced parseAWSLogLevel() to accept parameter default and support preset levels; changed default to no logging
pkg/awsclicompat/session_test.go Updated test expectations to reflect secure default; added TestPresetLevels() to test new preset log levels; renamed TestBackwardCompatibility to TestDefaultSecureBehavior
go.mod Updated Go version to 1.25.4
.github/workflows/lint.yaml Updated golangci-lint version to v2.6.2

Copy link

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 11 out of 11 changed files in this pull request and generated 1 comment.

Copy link

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 11 out of 11 changed files in this pull request and generated 2 comments.

Copy link

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 11 out of 11 changed files in this pull request and generated 2 comments.

@aditmeno aditmeno force-pushed the fix/configurable-aws-sdk-logging branch 2 times, most recently from 3d1f847 to b01290e Compare November 24, 2025 04:59
This commit introduces configurable AWS SDK logging to address credential
leakage concerns while maintaining backward compatibility.

Key changes:
- Add AWSLogLevel field to all AWS provider structs (SSM, S3, Secrets Manager, KMS)
- Pass AWS log level through function parameters instead of environment variables
- Update provider factories to accept and forward awsLogLevel parameter
- Implement preset log levels: off, minimal, standard, verbose
- Default to no logging (secure default) to prevent credential exposure
- Support AWS_SDK_GO_LOG_LEVEL environment variable override
- Add LogModeOff constant for consistent 'no logging' representation
- Consolidate preset handling in single switch statement

Benefits:
- Thread-safe by design (no shared mutable state)
- No global state mutation via os.Setenv()
- Clear precedence: env var > parameter > secure default
- Improved code readability and maintainability

Fixes: helmfile/helmfile#2270
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
Copy link

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 13 out of 13 changed files in this pull request and generated no new comments.

@yxxhero yxxhero merged commit c36c996 into helmfile:main Nov 24, 2025
11 checks passed
yxxhero pushed a commit to helmfile/helmfile that referenced this pull request Nov 24, 2025
)

* fix: make AWS SDK debug logging configurable (issue #2270)

This PR fixes issue #2270 where AWS SDK debug logs expose sensitive
credentials in helmfile output, by adding flexible, configurable AWS SDK
logging with secure defaults.

Problem:
--------
Despite PR #2288's fix, AWS SDK debug logs still appeared in helmfile
output, exposing sensitive information:
- AWS tokens and authorization headers
- Request/response bodies containing credentials
- Secret metadata from vals providers

Root Cause:
-----------
1. PR #2288 only suppressed vals' own logging via LogOutput: io.Discard
2. AWS SDK v2 uses separate logging (AWS_SDK_GO_LOG_LEVEL, WithClientLogMode)
3. Vals library defaulted to verbose logging (aws.LogRetries | aws.LogRequest)
4. No programmatic way to control AWS SDK logging

Solution:
---------
Two-part fix in conjunction with vals PR #893:

1. Vals library enhancement (helmfile/vals#893):
   - Added Options.AWSLogLevel field for programmatic control
   - Changed default from verbose to secure (no logging)
   - Added preset levels: off, minimal, standard, verbose
   - Maintains AWS_SDK_GO_LOG_LEVEL precedence

2. Helmfile changes (this PR):
   - Added HELMFILE_AWS_SDK_LOG_LEVEL environment variable
   - Enhanced vals configuration to use new AWSLogLevel field
   - Added conditional AWS SDK log suppression in remote.go (3 locations)
   - Comprehensive unit tests (15 test cases)

Configuration:
--------------
Preset levels via HELMFILE_AWS_SDK_LOG_LEVEL:
- "off" (default) - No logging, secure, prevents credential leakage
- "minimal" - Log retries only
- "standard" - Log retries + requests (previous default behavior)
- "verbose" - Log everything (requests, responses, bodies, signing)
- Custom - Comma-separated values (e.g., "request,response")

Priority order:
1. AWS_SDK_GO_LOG_LEVEL env var (highest)
2. HELMFILE_AWS_SDK_LOG_LEVEL env var
3. Secure default ("off")

Testing:
--------
Added comprehensive unit tests:
- pkg/plugins/vals_test.go: 9 test cases
  * TestAWSSDKLogLevelConfiguration - all preset levels
  * TestEnvironmentVariableReading - env var parsing
- pkg/remote/remote_test.go: 6 test cases
  * TestAWSSDKLogLevelInit - init() logic

All tests passing:
- pkg/plugins: PASS (3/3 test suites)
- pkg/remote: PASS (all test suites)
- golangci-lint: 0 issues

Files changed: 7 files, 271 insertions(+), 31 deletions(-)

Security:
---------
Before: Credentials exposed by default (aws.LogRetries | aws.LogRequest)
After: Credentials protected by default (no logging unless explicitly enabled)

Follows security principles:
- Secure by default
- Principle of least privilege
- Explicit opt-in for sensitive logging
- Defense in depth

Dependency:
-----------
Depends on: helmfile/vals#893
Currently using: aditmeno/vals@a97336ce2bf6 (via go.mod replace)
After vals PR merges: Update to official release

Fixes: #2270
Related: #2288, #2289, helmfile/vals#893
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* chore: update vals to use parameter-based AWS log level configuration

Updated vals dependency to commit 06d7cd29 which implements clean
parameter-based AWS SDK logging configuration instead of using
global state mutation.

Changes in vals implementation:
- AWS log level passed through function parameters to each provider
- No os.Setenv() - no environment mutation
- No package-level global variables
- No sync/atomic dependency needed
- Thread-safe by design - each provider instance has its own log level

This maintains the same functionality as before but with a cleaner
implementation that avoids global state mutation.

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* deps: update vals to upstream v0.42.6

Update from vals fork (aditmeno/vals) to official release v0.42.6.
Remove replace directive now that vals PR #893 has been merged upstream.

This brings in the AWS SDK log level configuration improvements:
- SetDefaultLogLevel() package-level function
- Options.AWSLogLevel field support
- Secure default (no logging)
- Preset log levels (off, minimal, standard, verbose)

Also updates related dependencies:
- Azure SDK and auth libraries
- AWS SDK config and credentials
- OAuth2 library

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

---------

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
@aditmeno aditmeno deleted the fix/configurable-aws-sdk-logging branch November 24, 2025 15:04
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.

Helmfile 1.1.6+ uses Debug log level when making requests via the AWS Go SDK

2 participants