Skip to content

fix(sts): rotate TLS certificates - #222

Merged
GatewayJ merged 3 commits into
rustfs:mainfrom
GatewayJ:agent/fix-1091-rotate-sts-tls
Aug 9, 2026
Merged

fix(sts): rotate TLS certificates#222
GatewayJ merged 3 commits into
rustfs:mainfrom
GatewayJ:agent/fix-1091-rotate-sts-tls

Conversation

@GatewayJ

@GatewayJ GatewayJ commented Aug 5, 2026

Copy link
Copy Markdown
Member

Type of Change

  • New Feature
  • Bug Fix
  • Documentation
  • Performance Improvement
  • Test/CI
  • Refactor
  • Other: N/A

Related Issues

Closes rustfs/backlog#1091

Summary of Changes

  • Keep operator STS TLS enabled while defaulting certificate ownership to externally managed sts-tls material (sts.tls.auto=false).
  • Fail startup with actionable guidance when TLS is enabled but externally managed tls.crt, tls.key, and ca.crt are missing.
  • Keep Kind/E2E development manifests explicitly opted in to Operator-generated certificates with OPERATOR_STS_TLS_AUTO=true.
  • When automatic management is explicitly enabled, issue STS CA and server certificates with a one-year validity period and rotate them 30 days before expiry.
  • Poll the TLS Secret every five minutes, hot-load valid material for new connections, retain the last valid configuration on refresh failures, and handle concurrent replica replacement conflicts.
  • Reject expired or not-yet-valid replacement material, expose leaf and CA expiry timestamp metrics, and document the external and automatic ownership models.

Checklist

  • I have read and followed the CONTRIBUTING.md guidelines
  • Passed make pre-commit (fmt-check + clippy + test + console-lint + console-fmt-check)
  • Added/updated necessary tests
  • Documentation updated (if needed)
  • CHANGELOG.md updated under [Unreleased] (N/A)
  • CI/CD passed (pending)

Impact

  • Breaking change (CRD/API compatibility)
  • Requires doc/config/deployment update
  • Other impact: Installations that keep STS TLS enabled and do not explicitly opt in to automatic generation must pre-create sts-tls. Existing externally managed material is hot-loaded after valid Secret updates. Kind/E2E development remains opted in to automatic generation.

Verification

make pre-commit

Additional Notes

A live Kubernetes rotation was not run locally. Unit and manifest tests cover certificate validity, early renewal, legacy policy detection, external-certificate ownership defaults, missing-Secret guidance, development auto-generation, expired material rejection, and metrics.


Thank you for your contribution! Please ensure your PR follows the community standards (CODE_OF_CONDUCT.md) and sign the CLA if this is your first contribution.

@GatewayJ
GatewayJ marked this pull request as ready for review August 8, 2026 11:51
@GatewayJ

GatewayJ commented Aug 8, 2026

Copy link
Copy Markdown
Member Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cfa517ab4f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/sts/tls.rs
namespace = %config.namespace,
"rotating managed operator STS TLS certificate"
);
replace_generated_secret(api, config, &secret).await

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve client trust across automatic renewal

When a managed certificate enters the 30-day renewal window, this replacement generates a new self-signed CA as well as a new leaf and immediately hot-loads the leaf. Clients that copied or cached the previous ca.crt—especially workloads in other namespaces, since the repository provides no CA distribution or overlap mechanism—will reject every subsequent STS connection until their trust store is manually updated. Preserve a longer-lived CA while rotating leaf certificates, or distribute/overlap the new trust anchor before switching the server certificate.

Useful? React with 👍 / 👎.

Comment thread src/sts/tls.rs Outdated
Comment on lines +572 to +573
let certificate = rustls_pemfile::certs(&mut Cursor::new(pem))
.next()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate the full CA bundle rather than its first entry

When an externally managed ca.crt contains multiple PEM certificates during a CA rollover, this .next() inspects only the first entry. If that old entry is expired while a later CA is valid and signs the active leaf, validated_material_from_secret rejects the entire Secret, preventing startup or hot-loading an otherwise usable replacement. Inspect the complete bundle and validate the applicable issuing chain instead of treating the first certificate as the sole CA.

Useful? React with 👍 / 👎.

Comment thread src/metrics.rs Outdated
Comment on lines +475 to +476
.sts_tls_certificate_expiry_timestamp_seconds
.load(Ordering::Relaxed) as f64,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Suppress expiry gauges when STS TLS is inactive

When STS or its TLS support is disabled while the metrics endpoint remains enabled, neither expiry setter runs, so these default-initialized atomics are still rendered as Unix timestamp 0. An alert that follows the new documentation and checks whether the expiry timestamp is approaching or already passed will therefore fire continuously even though no STS certificate exists. Omit these series until TLS material has been loaded, or expose an explicit enabled/validity signal that alerts can use.

Useful? React with 👍 / 👎.

Comment thread src/sts/tls.rs
Comment on lines +386 to +389
let annotations = BTreeMap::from([(
POLICY_VERSION_ANNOTATION.to_string(),
POLICY_VERSION.to_string(),
)]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve existing annotations during managed replacement

When users or cluster automation add annotations to the Operator-managed Secret—for example, to mirror ca.crt into workload namespaces—the legacy migration and every scheduled renewal replace the Secret with an object whose annotation map contains only this policy marker. Because replace_generated_secret preserves only resourceVersion, the first rotation silently removes those integration annotations and can stop subsequent Secret propagation. Merge the existing annotations and then overwrite only the policy-version key.

Useful? React with 👍 / 👎.

@GatewayJ
GatewayJ added this pull request to the merge queue Aug 9, 2026
Merged via the queue into rustfs:main with commit 7565eba Aug 9, 2026
3 checks passed
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