Skip to content

Fix AWS SDK debug logging by making it configurable (issue #2270)#2290

Merged
yxxhero merged 3 commits into
helmfile:mainfrom
aditmeno:fix/aws-sdk-debug-logging-configurable
Nov 24, 2025
Merged

Fix AWS SDK debug logging by making it configurable (issue #2270)#2290
yxxhero merged 3 commits into
helmfile:mainfrom
aditmeno:fix/aws-sdk-debug-logging-configurable

Conversation

@aditmeno

@aditmeno aditmeno commented Nov 24, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR properly fixes issue #2270 where AWS SDK debug logs continue to appear in helmfile output despite the fix in PR #2288.

Two-part solution: This PR works in conjunction with vals PR #893 to provide flexible, configurable AWS SDK logging with secure defaults.


⚠️ Dependency

This PR depends on: helmfile/vals#893

Current status:

  • ✅ vals PR helmfile hangs on large diff with --enable-live-output #893 implements clean SetDefaultLogLevel() package-level function
  • ✅ Helmfile references vals commit 847469e1d13b via replace directive
  • ✅ All tests passing with new vals version
  • ⏳ Once vals PR merges, will update to official release version

After vals PR merge:

go get github.com/helmfile/vals@latest
go mod tidy
# Remove replace directive from go.mod

Problem Statement

As reported in PR #2288 comment, users still see AWS SDK debug logs in helmfile 1.2.1:

❯ helmfile diff -l cluster=staging
SDK 2025/11/24 07:54:06 DEBUG Request
PUT /latest/api/token HTTP/1.1
Host: 169.254.169.254

This exposes sensitive information including:

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

Root Cause Analysis

The original fix in PR #2288 added LogOutput: io.Discard to vals.Options, but this only suppressed vals' own logging, not the AWS SDK's debug output.

Why the issue persisted:

  1. AWS SDK v2 uses a separate logging system:

    • Controlled by AWS_SDK_GO_LOG_LEVEL environment variable
    • Configured via WithClientLogMode option in AWS config
    • Completely independent from vals' LogOutput
  2. AWS SDK clients in remote.go needed attention:

    • Three config.LoadDefaultConfig() calls had no log suppression
    • These clients interact with S3, exposing credentials in logs
  3. Vals library defaulted to verbose logging:

    • vals hardcoded aws.LogRetries | aws.LogRequest as default
    • No programmatic way to change this behavior
    • Leaked credentials on every AWS API call

Solution

Two-Part Fix

Part 1: Vals Library Enhancement (PR #893)

Clean implementation without environment variable modification:

  • ✅ Added Options.AWSLogLevel field for programmatic control
  • ✅ Created package-level SetDefaultLogLevel() function in awsclicompat package
  • ✅ Enhanced parseAWSLogLevel() to check package-level variable between env var and parameter
  • ✅ Changed default from aws.LogRetries | aws.LogRequest to 0 (no logging)
  • ✅ Added preset levels: "off", "minimal", "standard", "verbose"
  • ✅ Maintains environment variable precedence (AWS_SDK_GO_LOG_LEVEL always wins)

Key implementation details:

// vals.go - Calls SetDefaultLogLevel() when Options.AWSLogLevel is provided
func New(opts Options) (*Runtime, error) {
    // Configure AWS SDK logging if AWSLogLevel is provided
    // This provides programmatic control without modifying environment variables
    if opts.AWSLogLevel != "" {
        awsclicompat.SetDefaultLogLevel(opts.AWSLogLevel)
    }
    // ...
}

// pkg/awsclicompat/session.go - Package-level configuration
var defaultLogLevel string

func SetDefaultLogLevel(level string) {
    defaultLogLevel = level
}

func parseAWSLogLevel(paramDefault string) aws.ClientLogMode {
    // Priority: AWS_SDK_GO_LOG_LEVEL > defaultLogLevel > paramDefault > secure default
    logLevel := strings.TrimSpace(os.Getenv("AWS_SDK_GO_LOG_LEVEL"))
    
    if logLevel == "" && defaultLogLevel != "" {
        logLevel = defaultLogLevel
    }
    
    if logLevel == "" {
        logLevel = paramDefault
    }
    
    if logLevel == "" {
        return 0  // Secure default
    }
    // ... preset handling
}

Part 2: Helmfile Changes (This PR)

1. Added Flexible Configuration (pkg/envvar/const.go)

// HELMFILE_AWS_SDK_LOG_LEVEL controls AWS SDK logging level
// Valid values: "off" (default), "minimal", "standard", "verbose", or custom
AWSSDKLogLevel = "HELMFILE_AWS_SDK_LOG_LEVEL";

Preset levels:

  • "off" (default) - No AWS SDK logging (secure, prevents credential leakage)
  • "minimal" - Log retries only (minimal debugging)
  • "standard" - Log retries + requests (previous default behavior)
  • "verbose" - Log everything (requests, responses, bodies, signing)
  • Custom - Comma-separated: "request,response,signing"

2. Enhanced Vals Configuration (pkg/plugins/vals.go)

// Read log level from HELMFILE_AWS_SDK_LOG_LEVEL
logLevel := strings.TrimSpace(os.Getenv(envvar.AWSSDKLogLevel))

// Default to "off" for security if not specified
if logLevel == "" {
    logLevel = "off"
}

// Pass to vals library using new Options.AWSLogLevel field
// vals will call awsclicompat.SetDefaultLogLevel() internally
opts.AWSLogLevel = logLevel

// Also suppress vals' own logging when set to "off"
if logLevel == "off" {
    opts.LogOutput = io.Discard
}

3. Conditional AWS SDK Log Suppression (pkg/remote/remote.go)

Made WithClientLogMode(0) conditional in all 3 AWS config locations:

  • S3Getter.Get() - S3 chart downloads
  • S3Getter.S3FileExists() - S3 file checks (region config)
  • S3Getter.S3FileExists() - S3 file checks (default config)
// Only suppress if explicitly set to "off" (default)
if awsSDKLogLevel == "off" {
    configOpts = append(configOpts, config.WithClientLogMode(0))
}
// For other values, AWS SDK respects AWS_SDK_GO_LOG_LEVEL env var

4. Comprehensive Unit Tests

Added extensive test coverage to verify AWS SDK logging behavior:

pkg/plugins/vals_test.go:

  • TestAWSSDKLogLevelConfiguration - Tests all preset levels + custom

    • Verifies default ("off"), explicit off, minimal, standard, verbose, custom
    • Validates LogOutput = io.Discard only when level is "off"
    • 6 test cases covering all configuration scenarios
  • TestEnvironmentVariableReading - Tests env var parsing

    • Empty defaults to "off"
    • Whitespace trimming
    • Value preservation
    • 3 test cases

pkg/remote/remote_test.go:

  • TestAWSSDKLogLevelInit - Tests init() logic
    • Default behavior (empty → "off")
    • All preset values (off, minimal, standard, verbose)
    • Whitespace trimming
    • 6 test cases

Test Results:

✅ pkg/plugins: PASS (all 3 test suites, 9 new test cases)
✅ pkg/remote: PASS (all test suites, 6 new test cases)
✅ Linting: 0 issues (golangci-lint)

Usage Examples

🔒 Production (Default - Secure)

helmfile diff
# ✅ No AWS SDK debug output
# ✅ No vals debug output
# ✅ Credentials protected

🔍 Minimal Debugging (Retries Only)

HELMFILE_AWS_SDK_LOG_LEVEL=minimal helmfile diff
# Shows only retry attempts
# Minimal overhead, good for troubleshooting connectivity

📊 Standard Debugging (Previous Default)

HELMFILE_AWS_SDK_LOG_LEVEL=standard helmfile diff
# Shows retries + request URLs
# Good for debugging auth/permissions issues

🔬 Verbose Debugging (Full Details)

HELMFILE_AWS_SDK_LOG_LEVEL=verbose helmfile diff
# Shows everything: requests, responses, bodies, signing
# Use for deep debugging only (exposes credentials!)

🎯 Custom Granular Control

HELMFILE_AWS_SDK_LOG_LEVEL=request,response helmfile diff
# Fine-grained control over specific log components

🌐 Environment Variable Priority

# AWS_SDK_GO_LOG_LEVEL always overrides HELMFILE_AWS_SDK_LOG_LEVEL
AWS_SDK_GO_LOG_LEVEL=verbose helmfile diff
# This takes precedence over any helmfile setting

Configuration Priority

The logging configuration follows this priority order (highest to lowest):

  1. AWS_SDK_GO_LOG_LEVEL environment variable (highest priority)
  2. HELMFILE_AWS_SDK_LOG_LEVEL environment variable → Options.AWSLogLevelawsclicompat.SetDefaultLogLevel()
  3. Secure default ("off" - no logging)

This ensures:

  • Users can override helmfile's settings when needed
  • AWS SDK's standard environment variable always works
  • Secure by default when nothing is configured
  • Clean implementation without global env var modification

Approach

This PR takes a two-tier approach:

  1. Fix the root cause in vals library (PR helmfile hangs on large diff with --enable-live-output #893)

    • Adds programmatic control over AWS SDK logging using clean package-level function
    • Uses SetDefaultLogLevel() instead of modifying global environment variables
    • Changes default to secure (no logging)
    • Benefits all vals users, not just helmfile
  2. Leverage vals improvements in helmfile (this PR)

    • Uses new Options.AWSLogLevel field
    • Adds flexible configuration via environment variable
    • Addresses AWS SDK usage in remote S3 operations
    • Provides comprehensive test coverage

This approach ensures the fix is thorough and maintainable, with secure defaults that prevent credential leakage while still allowing users to enable debugging when needed.


Security Impact

🔒 Before (helmfile ≤ 1.2.1)

  • AWS SDK logged by default: aws.LogRetries | aws.LogRequest
  • Credentials exposed in logs:
    SDK 2025/11/24 07:54:06 DEBUG Request
    PUT /latest/api/token HTTP/1.1
    Host: 169.254.169.254
    X-aws-ec2-metadata-token: REDACTED_BUT_VISIBLE_IN_LOGS
    

🔐 After (This PR)

  • Default: No AWS SDK logging (0)
  • Credentials protected unless user explicitly enables debugging
  • Opt-in security model: Users must consciously enable sensitive logging
  • Clean implementation: No global environment variable modification

Security principles:

  • Secure by default - No logging unless needed
  • Principle of least privilege - Minimal logging even when enabled
  • Defense in depth - Prevents accidental credential exposure
  • Explicit opt-in - Users understand security implications when enabling
  • Clean architecture - Package-level configuration, no global state pollution

Testing

All code compiles, passes linting, and comprehensive test coverage:

Unit Tests:

✅ go test ./pkg/plugins/... -v
   - TestValsInstance: PASS
   - TestAWSSDKLogLevelConfiguration: PASS (6 test cases)
   - TestEnvironmentVariableReading: PASS (3 test cases)

✅ go test ./pkg/remote/... -v  
   - TestAWSSDKLogLevelInit: PASS (6 test cases)
   - All existing remote tests: PASS

✅ golangci-lint run ./pkg/plugins/... ./pkg/remote/... ./pkg/envvar/...
   - 0 issues

Test Coverage:

  • New test cases: 15 (across 3 test functions)
  • Code paths tested:
    • Default behavior (no env var)
    • All preset levels (off, minimal, standard, verbose)
    • Custom log level values
    • Whitespace trimming
    • LogOutput configuration
    • Environment variable precedence

Files Changed

Helmfile Changes (This PR)

File Lines Description
go.mod +2 Replace directive for vals PR #893 (commit 847469e1d13b)
go.sum +2 Updated checksums for vals dependency
pkg/envvar/const.go +12 Added HELMFILE_AWS_SDK_LOG_LEVEL constant
pkg/plugins/vals.go +25, -5 Flexible log level configuration with presets
pkg/plugins/vals_test.go +154 Added comprehensive unit tests (9 test cases)
pkg/remote/remote.go +18, -6 Conditional AWS SDK log suppression (3 locations)
pkg/remote/remote_test.go +64 Added init() logic tests (6 test cases)

Total: 7 files changed, 271 insertions(+), 17 deletions(-)

Vals Changes (PR #893)

File Lines Description
vals.go +8, -1 Added AWSLogLevel field to Options struct, calls SetDefaultLogLevel()
pkg/awsclicompat/session.go +58, -14 Added package-level defaultLogLevel variable and SetDefaultLogLevel() function, enhanced parseAWSLogLevel() with preset support
pkg/awsclicompat/session_test.go +33, -9 Updated tests for secure default + added preset tests

Total: 3 files changed, 99 insertions(+), 24 deletions(-)


Migration Path

For Vals Dependency Update (After PR #893 Merges)

# In helmfile repository
# Remove replace directive from go.mod
go get github.com/helmfile/vals@latest
go mod tidy
go build ./cmd/helmfile

For Users Upgrading Helmfile

No action required! The default is secure (no logging).

If you need debugging:

# Minimal debugging (recommended for troubleshooting)
HELMFILE_AWS_SDK_LOG_LEVEL=minimal helmfile diff

# Standard debugging (previous default behavior)
HELMFILE_AWS_SDK_LOG_LEVEL=standard helmfile diff

# Verbose debugging (for deep investigation)
HELMFILE_AWS_SDK_LOG_LEVEL=verbose helmfile diff

Related Issues & PRs


Checklist

  • Code changes are focused and minimal
  • Comprehensive unit tests added (15 new test cases)
  • All tests pass (pkg/plugins, pkg/remote)
  • All linting passes (golangci-lint)
  • No breaking changes introduced
  • Secure by default (no logging)
  • Flexible configuration (4 presets + custom)
  • Follows existing code patterns
  • Clean git history with signed commits
  • Security implications documented
  • Backward compatible via "standard" preset
  • Comprehensive documentation in comments
  • Dependency clearly documented (vals PR helmfile hangs on large diff with --enable-live-output #893)
  • Environment variable precedence maintained
  • All AWS SDK client creation points addressed
  • Test coverage for all configuration scenarios
  • Clean implementation without global environment variable modification
  • Vals reference updated to commit 847469e1d13b

@aditmeno aditmeno marked this pull request as draft November 24, 2025 01:38
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch from 912638f to dddf315 Compare November 24, 2025 01:46
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch 2 times, most recently from 6bac643 to a1dd54a Compare November 24, 2025 02:18
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch from a1dd54a to d099d65 Compare November 24, 2025 02:36
aditmeno added a commit to aditmeno/vals that referenced this pull request Nov 24, 2025
This commit adds flexible AWS SDK logging configuration to vals while
changing the default to no logging for security, preventing credential
leakage.

Key changes:
1. Added Options.AWSLogLevel field for programmatic control
2. Implemented clean SetDefaultLogLevel() package-level function
3. Enhanced parseAWSLogLevel() with preset support
4. Changed default from aws.LogRetries | aws.LogRequest to 0 (no logging)
5. Added preset levels: "off", "minimal", "standard", "verbose"
6. Maintains AWS_SDK_GO_LOG_LEVEL environment variable precedence

Implementation approach:
- Uses package-level variable instead of modifying global environment
- Provides clean SetDefaultLogLevel() function for configuration
- No side effects on process-wide state
- Thread-safe initialization during vals.New()

Breaking change: Default behavior changes from logging to not logging.
Security rationale: Credentials should never leak unless explicitly
enabled for debugging.

Migration path:
- Environment variable: export AWS_SDK_GO_LOG_LEVEL=standard
- Code update: vals.New(vals.Options{AWSLogLevel: "standard"})

Related to: helmfile/helmfile#2270
Enables: helmfile/helmfile#2290

Signed-off-by: Aditya Menon <[email protected]>
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch from d099d65 to 5fd2745 Compare November 24, 2025 02:37
aditmeno added a commit to aditmeno/vals that referenced this pull request Nov 24, 2025
This commit adds flexible AWS SDK logging configuration to vals while
changing the default to no logging for security, preventing credential
leakage.

Key changes:
1. Added Options.AWSLogLevel field for programmatic control
2. Implemented clean SetDefaultLogLevel() package-level function
3. Enhanced parseAWSLogLevel() with preset support
4. Changed default from aws.LogRetries | aws.LogRequest to 0 (no logging)
5. Added preset levels: "off", "minimal", "standard", "verbose"
6. Maintains AWS_SDK_GO_LOG_LEVEL environment variable precedence

Implementation approach:
- Uses package-level variable instead of modifying global environment
- Provides clean SetDefaultLogLevel() function for configuration
- No side effects on process-wide state
- Thread-safe initialization during vals.New()

Breaking change: Default behavior changes from logging to not logging.
Security rationale: Credentials should never leak unless explicitly
enabled for debugging.

Migration path:
- Environment variable: export AWS_SDK_GO_LOG_LEVEL=standard
- Code update: vals.New(vals.Options{AWSLogLevel: "standard"})

Related to: helmfile/helmfile#2270
Enables: helmfile/helmfile#2290

Signed-off-by: Aditya Menon <[email protected]>
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch from 5fd2745 to 23c2e90 Compare November 24, 2025 02:40
aditmeno added a commit to aditmeno/vals that referenced this pull request Nov 24, 2025
This commit adds flexible AWS SDK logging configuration to vals while
changing the default to no logging for security, preventing credential
leakage.

Key changes:
1. Added Options.AWSLogLevel field for programmatic control
2. Implemented clean SetDefaultLogLevel() package-level function
3. Enhanced parseAWSLogLevel() with preset support
4. Changed default from aws.LogRetries | aws.LogRequest to 0 (no logging)
5. Added preset levels: "off", "minimal", "standard", "verbose"
6. Maintains AWS_SDK_GO_LOG_LEVEL environment variable precedence

Implementation approach:
- Uses package-level variable instead of modifying global environment
- Provides clean SetDefaultLogLevel() function for configuration
- No side effects on process-wide state
- Thread-safe initialization during vals.New()

Breaking change: Default behavior changes from logging to not logging.
Security rationale: Credentials should never leak unless explicitly
enabled for debugging.

Migration path:
- Environment variable: export AWS_SDK_GO_LOG_LEVEL=standard
- Code update: vals.New(vals.Options{AWSLogLevel: "standard"})

Related to: helmfile/helmfile#2270
Enables: helmfile/helmfile#2290

Signed-off-by: Aditya Menon <[email protected]>
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 <[email protected]>
@aditmeno aditmeno force-pushed the fix/aws-sdk-debug-logging-configurable branch from 23c2e90 to 637dbdf Compare November 24, 2025 02:45
@aditmeno

Copy link
Copy Markdown
Contributor Author

@paulbehrisch Could you verify if the helmfile built from this PR achieves the same result?

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 <[email protected]>
@aditmeno aditmeno marked this pull request as ready for review November 24, 2025 05:09
@yxxhero

yxxhero commented Nov 24, 2025

Copy link
Copy Markdown
Member

@aditmeno vals is ready.

Update from vals fork (aditmeno/vals) to official release v0.42.6.
Remove replace directive now that vals PR helmfile#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 <[email protected]>
@aditmeno

Copy link
Copy Markdown
Contributor Author

@yxxhero PR is ready for review

@yxxhero yxxhero merged commit 83b4a8f into helmfile:main Nov 24, 2025
16 checks passed
@aditmeno aditmeno deleted the fix/aws-sdk-debug-logging-configurable branch November 24, 2025 10:31
Copilot AI added a commit that referenced this pull request Nov 28, 2025
…iles

This fixes an issue where template expressions like {{ env "VAR" }},
{{ readFile "file" }}, or {{ fetchSecretValue "ref+..." }} in inline
values passed to sub-helmfiles were not being rendered when the parent
helmfile used a .yaml extension instead of .yaml.gotmpl.

The fix adds template rendering for inline map values in
LoadEnvironmentValues, ensuring that template expressions are
properly evaluated in the context of the parent helmfile's directory.

Fixes #2290

Co-authored-by: yxxhero <[email protected]>
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.

2 participants