Skip to content

fix(repositoryserver): use a valid assume-role duration for S3 credential env - #4131

Open
anxkhn wants to merge 1 commit into
kanisterio:masterfrom
anxkhn:fix/reposerver-assume-role-duration
Open

anxkhn wants to merge 1 commit into
kanisterio:masterfrom
anxkhn:fix/reposerver-assume-role-duration

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 15, 2026

Copy link
Copy Markdown

Change Overview

RepoServerHandler.setCredDataFromSecretInPod builds the credential environment
variables for the kopia repository-server pod. It passed
time.Duration(time.Now().Second()) as the AWS assume-role session duration:

envVars, err := storage.GenerateEnvSpecFromCredentialSecret(storageCredSecret, time.Duration(time.Now().Second()))

time.Time.Second() returns the wall-clock seconds field as an int in [0, 59],
and time.Duration interprets that int as nanoseconds. So the value handed to
the AWS AssumeRole / AssumeRoleWithWebIdentity provider is only 0-59ns and it
changes every second.

That duration flows through
GenerateEnvSpecFromCredentialSecret -> getEnvSpecForAWSCredentialSecret ->
secrets.ExtractAWSCredentials; for a role-based secret it is stored as its
.String() form and parsed back by durationFromString, then reaches the SDK's
stscreds providers. Those providers compute the STS request as
DurationSeconds = int32(duration / time.Second)
(assume_role_provider.go, web_identity_provider.go), which truncates any
sub-second duration to 0. AWS STS rejects DurationSeconds below its documented
900s (15m) minimum for AssumeRole / AssumeRoleWithWebIdentity.

Net effect: whenever the storage credential secret uses an IAM role (config role /
web identity), repository-server credential env generation fails for roughly 59 of
every 60 wall-clock seconds. It only "works" in the single second where
Second() == 0, because a 0 duration falls through to the provider's 15m default.
Static access-key secrets are unaffected, since the duration is unused for them.

This change passes aws.AssumeRoleDurationDefault (60m) instead, which is exactly
the value already used at the other ExtractAWSCredentials call sites
(pkg/restic/restic.go, pkg/validate/validate.go, pkg/location/location.go).
That removes the wall-clock dependence and yields a valid STS session duration. The
now-unused time import is dropped from handler.go.

  • 🚧 Work in Progress
  • 🌈 Refactoring (no functional changes, no api changes)
  • 🐹 Trivial/Minor
  • 🐛 Bugfix
  • 🌻 Feature
  • 🗺️ Documentation
  • 🤖 Test
  • 🏗️ Build

Issues

Ref #

Test Plan

  • ⚡ Unit test

Added TestAssumeRoleDurationRoundTrip in pkg/aws/aws_test.go. It drives the real
durationFromString parser and mirrors the SDK's
DurationSeconds = int32(duration / time.Second) derivation to assert that:

  • AssumeRoleDurationDefault resolves to a DurationSeconds >= the STS 900s minimum,
  • an unset/empty duration falls back to the default and is likewise valid,
  • a sub-second duration (1ns / 37ns / 59ns) round-trips to DurationSeconds = 0,
    documenting the exact behaviour this fix avoids.

Commands run locally:

go build ./...
go test ./pkg/aws/... ./pkg/kopia/command/storage/... ./pkg/secrets/...
go test ./pkg/aws/ -check.f TestAssumeRoleDurationRoundTrip -v
go test -c -o /dev/null ./pkg/controllers/repositoryserver/
golangci-lint run --config=.golangci.yml ./pkg/controllers/repositoryserver/... ./pkg/aws/...

All pass; golangci-lint reports 0 issues; gofmt clean.

The role path in the handler itself ends in a live STS Retrieve, so it is not
exercisable offline without AWS credentials; the regression guard is therefore placed
at the deepest pure-Go seam (durationFromString plus the SDK's DurationSeconds
formula).

@anxkhn
anxkhn force-pushed the fix/reposerver-assume-role-duration branch from d8c249d to 5ac6de8 Compare August 12, 2026 05:24
Add a regression test asserting the resolved assume-role duration converts to a
DurationSeconds of at least the STS 900s minimum, and documenting that a
sub-second duration truncates to 0.

The original repository-server call site that passed
time.Duration(time.Now().Second()) was removed upstream with the
repositoryserver CRD, so this keeps the duration contract coverage.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn
anxkhn force-pushed the fix/reposerver-assume-role-duration branch from 5ac6de8 to 5cbd2fe Compare August 20, 2026 12:05

This branch has not been deployed

No deployments
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.

1 participant