Skip to content

fix: stop misclassifying plain S3 credentials as base64 (#2245) - #955

Open
PutlaTheophila wants to merge 3 commits into
openeverest:mainfrom
PutlaTheophila:fix/2245-drop-base64-heuristic
Open

fix: stop misclassifying plain S3 credentials as base64 (#2245)#955
PutlaTheophila wants to merge 3 commits into
openeverest:mainfrom
PutlaTheophila:fix/2245-drop-base64-heuristic

Conversation

@PutlaTheophila

@PutlaTheophila PutlaTheophila commented May 19, 2026

Copy link
Copy Markdown

Summary

The DataImportJob mutating webhook used utils.IsBase64Encoded to decide whether to store inline AccessKeyID / SecretAccessKey into the credentials Secret as Data (raw bytes, treated by the API server as already-base64) or as StringData (plain text). The heuristic is

return len(s)%4 == 0 && len(s) > 0 && err == nil

base64.StdEncoding.DecodeString silently accepts any ASCII string whose length is a multiple of 4 with no internal =. Real AWS access keys like

AKIAIOSFODNN7EXAMPLEAAAAAAAAAAAA   (length 32)

are therefore misclassified as base64 and stored as the decoded byte sequence. The DataImport job then receives a corrupted AWS_ACCESS_KEY_ID env var, S3 returns InvalidAccessKeyId, and the DB sits in importing with no surface-level error.

Fix

  • Drop the heuristic in mutateS3CredentialsSecret. Always write user-supplied credentials to secret.StringData — the API server encodes them on the write path, so plain strings round-trip unchanged.
  • utils.IsBase64Encoded is retained because internal/webhook/enginefeatures.everest/v1alpha1/splithorizondnsconfig_webhook.go still calls it for CA cert/key validation. Changing that webhook's contract is out of scope here.

Tests

  • TestDataImportJobDefaulter now uses 32-char plain-alphanumeric keys (the regression class) and asserts the values land in StringData verbatim.
  • TestDatabaseClusterDefaulter updated for the same assertion — it exercises the same handleS3CredentialsSecret path through the DatabaseCluster defaulter.
  • TestIsBase64Encoded removed (the function is no longer called from this package).

Related

Companion PR in the API server adds a synchronous read-only S3 probe at DatabaseCluster admission so the broader "credentials silently fail" class of bug is caught at create time: openeverest/openeverest#2279 (Fixes openeverest/openeverest#2229).

Together the two PRs make the inline-credentials path correct end-to-end: the operator stores them as plain strings, and the API server validates them against S3 before the resource is admitted.

Test plan

  • go build ./... clean
  • go vet ./internal/webhook/everest/v1alpha1/... ./utils/... ./internal/webhook/enginefeatures.everest/v1alpha1/... clean
  • go test ./internal/webhook/everest/v1alpha1/... ./utils/... ./internal/webhook/enginefeatures.everest/v1alpha1/... pass
  • DCO sign-off

Fixes openeverest/openeverest#2245.

The DataImportJob mutating webhook previously used the
utils.IsBase64Encoded heuristic to decide whether to store the inline
AccessKeyID / SecretAccessKey into the credentials Secret as raw bytes
(Data, treated as already-base64-encoded by the API server) or as plain
strings (StringData). The heuristic is

    len(s)%4 == 0 && len(s) > 0 && err == nil

where err comes from base64.StdEncoding.DecodeString. Any plain ASCII
string whose length is a multiple of 4 with no internal "=" passes
DecodeString silently, so a real AWS access key like

    AKIAIOSFODNN7EXAMPLEAAAAAAAAAAAA   (length 32)

is misclassified as base64 and stored as the decoded byte sequence.
The DataImport job then receives a corrupted AWS_ACCESS_KEY_ID env
var and the import either fails with "InvalidAccessKeyId" or hangs in
"importing" state, with no surface-level error to the user.

Drop the heuristic in mutateS3CredentialsSecret and always write the
user-supplied credentials to secret.StringData. The API server encodes
StringData on the write path, so plain credentials round-trip unchanged.

Tests:
- TestDataImportJobDefaulter now uses 32-char plain-alphanumeric keys
  (the regression class) and asserts the values land in StringData
  verbatim.
- TestDatabaseClusterDefaulter updated for the same assertion since it
  exercises the same handleS3CredentialsSecret path through the
  DatabaseCluster defaulter.
- TestIsBase64Encoded is removed since the function is no longer used
  here (it remains in utils for SplitHorizonDNSConfig cert validation,
  which is out of scope for this fix).

The sibling openeverest PR adds a synchronous read-only S3 probe at
DatabaseCluster admission so the broader "credentials silently fail"
class of bug (openeverest/openeverest#2229) is caught up front.

Fixes openeverest/openeverest#2245

Signed-off-by: PutlaTheophila <putlatheophila123@gmail.com>

@chilagrow chilagrow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for your contribution! One tiny comment.

Comment on lines +81 to +82
// Credentials land in StringData verbatim. Writing to Data with these
// values would have stored a corrupted (base64-decoded) byte sequence.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's remove this comment, for the readers of the code this comment sounds confusing without context.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hi

PutlaTheophila added a commit to PutlaTheophila/openeverest-operator that referenced this pull request Jun 3, 2026
@PutlaTheophila

Copy link
Copy Markdown
Author

Thanks for the review @chilagrow! Removed the confusing StringData/Data comment in a646b73.

@chilagrow chilagrow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you sign-off your commit please? CI is failing

Addresses review feedback from @chilagrow on PR openeverest#955.

Signed-off-by: PutlaTheophila <putlatheophila123@gmail.com>
…4-heuristic

Signed-off-by: PutlaTheophila <putlatheophila123@gmail.com>
@PutlaTheophila
PutlaTheophila force-pushed the fix/2245-drop-base64-heuristic branch from c0395d0 to 4e67ec5 Compare June 3, 2026 12:42
@PutlaTheophila

Copy link
Copy Markdown
Author

Thanks @chilagrow — both addressed:

  • Comment removed (6edb5f9).
  • Sign-off added; DCO is green.

@chilagrow chilagrow added the test/e2e-data-importer Run data importer E2E suite. label Jun 4, 2026

@chilagrow chilagrow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for your contribution! I run e2e test for data importer a few times and they are failing, could you look at it please https://github.com/openeverest/openeverest-operator/actions/runs/26921958578/job/79426452666?pr=955?

Note, we run these tests manually upon change of the relevant code, so it might have got outdated 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test/e2e-data-importer Run data importer E2E suite.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IsBase64Encoded heuristic breaks inline S3 credentials No checks done when providing S3 details.

3 participants