Feature Request: AWS STS Session Token Support for Backups
Summary
Add support for AWS STS temporary credentials (specifically AWS_SESSION_TOKEN) in both scheduled backups and PITR binlog collector components.
Current Behavior
Both backup components currently have issues with AWS STS temporary credentials:
Scheduled Backups
The containerOptions.args.xbcloud feature appears to support passing --s3-session-token, but variable substitution does not work. When configuring:
containerOptions:
env:
- name: AWS_SESSION_TOKEN
valueFrom:
secretKeyRef:
name: backup-creds
key: AWS_SESSION_TOKEN
args:
xbcloud:
- "--s3-session-token=${AWS_SESSION_TOKEN}"
The backup script receives the literal string --s3-session-token=${AWS_SESSION_TOKEN} instead of the expanded token value. This is because:
- The operator passes
containerOptions.args.xbcloudto the environment variableXBCLOUD_EXTRA_ARGS - The backup script at
/backup/lib/pxc/backup.shline 10 does:XBCLOUD_ARGS="--curl-retriable-errors=7 $XBCLOUD_EXTRA_ARGS" - Shell variable references inside
$XBCLOUD_EXTRA_ARGSare not expanded because they’re already strings, not shell code
The result is xbcloud receiving a literal ${AWS_SESSION_TOKEN} string:
xbcloud put ... '--s3-session-token=${AWS_SESSION_TOKEN}' ...
xbcloud: Probe failed. Please check your credentials and endpoint settings.
PITR Binlog Collector
The PITR binlog collector uses the minio-go SDK directly with a hardcoded empty session token in pkg/pxc/backup/storage/storage.go:
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), // Empty session token
Use Case
AWS STS temporary credentials are commonly used to:
- Follow AWS security best practices by avoiding long-lived static credentials
- Implement fine-grained, time-limited access control
- Enable cross-account S3 access via AssumeRole
- Comply with organizational security policies that mandate temporary credentials
Without session token support, users must either:
- Disable backups entirely or accept that they will fail with STS credentials
- Use static long-lived credentials (not always possible due to security requirements)
Technical Analysis
Root Cause 1: Scheduled Backups (xbcloud variable expansion)
The backup shell scripts don’t expand environment variable references passed via XBCLOUD_EXTRA_ARGS. The script at /backup/lib/pxc/backup.sh simply concatenates the string without evaluation:
XBCLOUD_ARGS="--curl-retriable-errors=7 $XBCLOUD_EXTRA_ARGS"
When XBCLOUD_EXTRA_ARGS contains --s3-session-token=${AWS_SESSION_TOKEN}, the ${AWS_SESSION_TOKEN} is treated as a literal string because:
- It’s already a string value in the environment variable
- Shell expansion only happens once when the line is executed
- Nested variable references in strings are not automatically expanded
Files requiring changes:
/backup/lib/pxc/backup.sh- Addevalor useenvsubstto expand variables inXBCLOUD_EXTRA_ARGS/backup/run_backup.sh- Same fix needed/backup/recovery-cloud.sh- Same fix needed
Proposed fix for backup scripts:
# Option 1: Use eval (simple but requires careful escaping)
XBCLOUD_EXTRA_ARGS=$(eval echo "$XBCLOUD_EXTRA_ARGS")
XBCLOUD_ARGS="--curl-retriable-errors=7 $XBCLOUD_EXTRA_ARGS"
# Option 2: Use envsubst (safer)
XBCLOUD_EXTRA_ARGS=$(echo "$XBCLOUD_EXTRA_ARGS" | envsubst)
XBCLOUD_ARGS="--curl-retriable-errors=7 $XBCLOUD_EXTRA_ARGS"
Root Cause 2: PITR Binlog Collector (minio-go SDK)
The issue is in pkg/pxc/backup/storage/storage.go at line 100-101:
minioClient, err := minio.New(strings.TrimRight(endpoint, "/"), &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), // Empty session token
Secure: useSSL,
Region: region,
Transport: transport,
})
The third parameter to credentials.NewStaticV4() is the session token, which is hardcoded to an empty string "".
PITR Code Flow
- PITR Deployment is created by
pkg/pxc/app/binlogcollector/binlog-collector.go - Environment variables are set from the credentials secret in
getStorageEnvs()(lines 241-336) - The collector binary reads config from environment variables in
cmd/pitr/collector/collector.go(lines 104-110) - Storage client is created in
collector.New()callingstorage.NewS3()(line 147) storage.NewS3()creates the minio client with hardcoded empty session token
Files Requiring Changes for PITR
-
pkg/pxc/backup/storage/storage.go- Add session token parameter toNewS3()function -
cmd/pitr/collector/collector.go- AddSessionTokenfield toBackupS3config struct -
pkg/pxc/app/binlogcollector/binlog-collector.go- AddAWS_SESSION_TOKENenvironment variable to the PITR deployment -
api/v1/pxc_types.go(optional) - AddsessionTokenfield toBackupStorageS3Specif a dedicated secret key reference is preferred
Proposed Solution
Part 1: Fix Scheduled Backups (xbcloud variable expansion)
Update the backup shell scripts to expand environment variables in XBCLOUD_EXTRA_ARGS:
In /backup/lib/pxc/backup.sh, /backup/run_backup.sh, and /backup/recovery-cloud.sh:
# Before using XBCLOUD_EXTRA_ARGS, expand any variable references
if [ -n "$XBCLOUD_EXTRA_ARGS" ]; then
XBCLOUD_EXTRA_ARGS=$(echo "$XBCLOUD_EXTRA_ARGS" | envsubst)
fi
XBCLOUD_ARGS="--curl-retriable-errors=7 $XBCLOUD_EXTRA_ARGS"
This allows users to configure:
containerOptions:
env:
- name: AWS_SESSION_TOKEN
valueFrom:
secretKeyRef:
name: backup-creds
key: AWS_SESSION_TOKEN
args:
xbcloud:
- "--s3-session-token=${AWS_SESSION_TOKEN}"
Part 2: Fix PITR Binlog Collector
Add support for an optional AWS_SESSION_TOKEN key in the existing credentials secret. If present, it will be used; if absent, behavior remains unchanged (backwards compatible).
Change 1: pkg/pxc/backup/storage/storage.go
// Modify function signature to accept sessionToken
func NewS3(
ctx context.Context,
endpoint,
accessKeyID,
secretAccessKey,
sessionToken, // NEW PARAMETER
bucketName,
prefix,
region string,
verifyTLS bool,
caBundle []byte,
) (Storage, error) {
// ... existing code ...
minioClient, err := minio.New(strings.TrimRight(endpoint, "/"), &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, sessionToken), // USE sessionToken
Secure: useSSL,
Region: region,
Transport: transport,
})
// ... rest of function ...
}
Change 2: cmd/pitr/collector/collector.go
type BackupS3 struct {
Endpoint string `env:"ENDPOINT" envDefault:"s3.amazonaws.com"`
AccessKeyID string `env:"ACCESS_KEY_ID,required"`
AccessKey string `env:"SECRET_ACCESS_KEY,required"`
SessionToken string `env:"AWS_SESSION_TOKEN"` // NEW FIELD (optional)
BucketURL string `env:"S3_BUCKET_URL,required"`
Region string `env:"DEFAULT_REGION,required"`
}
And update the call to storage.NewS3():
s, err = storage.NewS3(
ctx,
c.BackupStorageS3.Endpoint,
c.BackupStorageS3.AccessKeyID,
c.BackupStorageS3.AccessKey,
c.BackupStorageS3.SessionToken, // NEW ARGUMENT
bucketArr[0],
prefix,
c.BackupStorageS3.Region,
c.VerifyTLS,
caBundle,
)
Change 3: pkg/pxc/app/binlogcollector/binlog-collector.go
In getStorageEnvs() function, add the session token environment variable:
case api.BackupStorageS3:
if storage.S3 == nil {
return nil, errors.New("s3 storage is not specified")
}
envs = []corev1.EnvVar{
{
Name: "SECRET_ACCESS_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: app.SecretKeySelector(storage.S3.CredentialsSecret, "AWS_SECRET_ACCESS_KEY"),
},
},
{
Name: "ACCESS_KEY_ID",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: app.SecretKeySelector(storage.S3.CredentialsSecret, "AWS_ACCESS_KEY_ID"),
},
},
// NEW: Add session token (optional key)
{
Name: "AWS_SESSION_TOKEN",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: storage.S3.CredentialsSecret,
},
Key: "AWS_SESSION_TOKEN",
Optional: ptr.To(true), // Optional - won't fail if key doesn't exist
},
},
},
// ... rest of existing env vars ...
}
Change 4: cmd/pitr/recoverer/recoverer.go
Apply the same changes to the recoverer for consistency:
type BackupS3 struct {
Endpoint string `env:"ENDPOINT" envDefault:"s3.amazonaws.com"`
AccessKeyID string `env:"ACCESS_KEY_ID,required"`
AccessKey string `env:"SECRET_ACCESS_KEY,required"`
SessionToken string `env:"AWS_SESSION_TOKEN"` // NEW FIELD
BackupDest string `env:"S3_BUCKET_URL,required"`
Region string `env:"DEFAULT_REGION,required"`
}
type BinlogS3 struct {
Endpoint string `env:"BINLOG_S3_ENDPOINT" envDefault:"s3.amazonaws.com"`
AccessKeyID string `env:"BINLOG_S3_ACCESS_KEY_ID,required"`
AccessKey string `env:"BINLOG_S3_SECRET_ACCESS_KEY,required"`
SessionToken string `env:"BINLOG_S3_SESSION_TOKEN"` // NEW FIELD
Region string `env:"BINLOG_S3_REGION,required"`
BucketURL string `env:"BINLOG_S3_BUCKET_URL,required"`
}
And update the calls to storage.NewS3() accordingly.
Backwards Compatibility
This solution maintains full backwards compatibility:
- Existing deployments without
AWS_SESSION_TOKENin their secrets will continue to work unchanged - The session token parameter defaults to empty string if not provided
- No changes required to existing CRs unless users want to enable STS support
Testing Recommendations
- Unit tests: Add tests for
storage.NewS3()with and without session token - Integration test with STS credentials:
- Create credentials using
aws sts assume-role - Store all three values (access key, secret key, session token) in Kubernetes secret
- Verify PITR uploads succeed
- Verify PITR recovery works
- Create credentials using
- Backwards compatibility test: Verify existing deployments without session token continue to function
Additional Context
Related: CloudNativePG Implementation
For reference, the CloudNativePG Barman plugin handles this well:
- Supports
sessionTokenin the ObjectStore CRD - Reads credentials fresh from Kubernetes secrets on each operation
- Full STS support out of the box
See: Hello from Barman Cloud CNPG-I plugin | Barman Cloud CNPG-I plugin
Environment
- PXC Operator version: 1.18.0 (also affects earlier versions)
- Kubernetes: Any
- Cloud provider: AWS (or S3-compatible with STS support)
Summary
Adding AWS STS session token support to both scheduled backups and PITR would:
- Align with AWS security best practices for temporary credentials
- Enable use of STS AssumeRole for S3 backup storage
- Require minimal code changes:
- Scheduled backups: Add
envsubstexpansion in 3 shell scripts - PITR: Add session token parameter across 4 Go files
- Scheduled backups: Add
- Maintain full backwards compatibility
Thank you for considering this feature request.