Skip to content

Commit 5ce8bea

Browse files
feat: Retire CA certificates (#650)
* feat: Add retirement duration for CA certificates * docs: Document the CA certificate retirement * chore: Update changelog * test: Add comment to `expect` * chore: Fix rustdoc warnings * chore: Address review comments * chore: Explain the calculation of safe_max_cert_lifetime
1 parent ed2a413 commit 5ce8bea

File tree

10 files changed

+390
-27
lines changed

10 files changed

+390
-27
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ All notable changes to this project will be documented in this file.
1212
- `EOS_DISABLED` (`--eos-disabled`) to disable the EoS checker completely.
1313
- Support exporting the TrustStore CA certificate information to Secrets or ConfigMaps ([#597]).
1414
- New helm value for `priorityClassName` ([#641]).
15+
- CA certificates are retired one hour (configurable via
16+
`autoTls.ca.caCertificateRetirementDuration`) before they expire ([#650]).
1517

1618
### Changed
1719

@@ -30,6 +32,8 @@ All notable changes to this project will be documented in this file.
3032
- Bump csi-node-driver-registrar to `v2.15.0` ([#642]).
3133
- Bump csi-provisioner to `v5.3.0` ([#643]).
3234
- OLM deployer: patch the new Deployment object too and other changes to align with the new operator structure ([#648]).
35+
- BREAKING: Expired and retired CA certificates are no longer published in Volumes and TrustStores
36+
([#650]).
3337

3438
[#597]: https://github.com/stackabletech/secret-operator/pull/597
3539
[#636]: https://github.com/stackabletech/secret-operator/pull/636
@@ -39,6 +43,7 @@ All notable changes to this project will be documented in this file.
3943
[#644]: https://github.com/stackabletech/secret-operator/pull/644
4044
[#645]: https://github.com/stackabletech/secret-operator/pull/645
4145
[#648]: https://github.com/stackabletech/secret-operator/pull/648
46+
[#650]: https://github.com/stackabletech/secret-operator/pull/650
4247

4348
## [25.7.0] - 2025-07-23
4449

Cargo.nix

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ socket2 = { version = "0.6", features = ["all"] }
4040
strum = { version = "0.27", features = ["derive"] }
4141
sys-mount = { version = "3.0", default-features = false }
4242
tempfile = "3.12"
43-
time = { version = "0.3", features = ["parsing"] }
43+
time = { version = "0.3", features = ["macros", "parsing"] }
4444
tokio = { version = "1.40", features = ["full"] }
4545
tokio-stream = { version = "0.1", features = ["net"] }
4646
tonic = "0.14"

docs/modules/secret-operator/pages/secretclass.adoc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,18 @@ In case an operator sets a higher lifetime, a tracking issue must be created to
8888
Users can use xref:concepts:overrides.adoc#pod-overrides[podOverrides] to extend the certificate lifetime by adding volume annotations.
8989
Native support for customizing certificate lifetimes in Stacklet CRDs might be added in the future.
9090

91+
[#ca-rotation]
9192
==== Certificate Authority rotation
9293

9394
Certificate authorities also have a limited lifetime, and need to be rotated before they expire to avoid cluster disruption.
9495

95-
If configured to provision its own CA (`autoTls.ca.autoGenerate`), the Secret Operator will create CA certificates that are valid for 365 days (≃ 1 year, configurable via `autoTls.ca.caCertificateLifetime`), and initiate rotation once less than half of that time remains.
96+
If configured to provision its own CA (`autoTls.ca.autoGenerate`), the Secret Operator will create CA certificates that are valid for 365 days (≃ 1 year, configurable via `autoTls.ca.caCertificateLifetime`).
97+
The CA certificate is retired one hour before its expiration (configurable via `autoTls.ca.caCertificateRetirementDuration`), to avoid that an almost expired certificate must be deployed, which causes problems in some products, e.g. OpenSearch.
98+
Once less than half of the active lifetime (= lifetime - retirement duration) remains, the rotation is initiated.
9699

97100
To avoid disruption and let the new CA propagate through the cluster, the Secret Operator will prefer using the oldest CA that will last for the entire lifetime of the issued certificate.
98101

99-
NOTE: Expired CA certificates will currently not be deleted automatically.
100-
They should be cleaned up manually.
102+
NOTE: Expired and retired CA certificates will not be deployed.
101103

102104
==== Reference
103105

@@ -112,6 +114,7 @@ spec:
112114
namespace: default
113115
autoGenerate: true
114116
caCertificateLifetime: 700d
117+
caCertificateRetirementDuration: 1d
115118
keyGeneration:
116119
rsa:
117120
length: 4096
@@ -131,6 +134,7 @@ spec:
131134
and `ca.key` respectively.
132135
`autoTls.ca.autoGenerate`:: Whether the certificate authority should be provisioned and managed by the Secret Operator.
133136
`autoTls.ca.caCertificateLifetime` :: The lifetime of the certificate authority's root certificate.
137+
`autoTls.ca.caCertificateRetirementDuration` :: Duration at the end of the CA certificate lifetime where no signed certificate will exist.
134138
`autoTls.ca.keyGeneration`:: Configures how keys should be generated.
135139
`autoTls.ca.keyGeneration.rsa`:: Declares that keys should be generated using the RSA algorithm.
136140
`autoTls.ca.keyGeneration.rsa.length`:: The amount of bits used for generating the RSA key pair. Currently, `2048`, `3072` and `4096` are supported. Defaults to `2048` bits.

docs/modules/secret-operator/pages/truststore.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ include::example$truststore-tls.yaml[]
2020
This will create a ConfigMap (or `Secret` based on `targetKind`) named `truststore-pem` containing a `ca.crt` with the trust root certificates.
2121
It can then either be mounted into a Pod or retrieved and used from outside of Kubernetes.
2222

23+
Expired or retired (see xref:secretclass.adoc#ca-rotation[Certificate Authority rotation]) certificates will not be published, because they should not be needed and some products, e.g. OpenSearch, have problems if they are present at startup.
24+
2325
NOTE: Make sure to have a procedure for updating the retrieved certificates.
2426
The Secret Operator will automatically rotate the xref:secretclass.adoc#backend-autotls[autoTls] certificate authority as needed, but all trust roots will require some form of update occasionally.

extra/crds.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ spec:
114114
If `autoGenerate: true` then the Secret Operator will prepare a new CA certificate the old CA approaches expiration.
115115
If `autoGenerate: false` then the Secret Operator will log a warning instead.
116116
type: string
117+
caCertificateRetirementDuration:
118+
default: 1h
119+
description: |-
120+
Duration at the end of the CA certificate lifetime where no signed certificate will exist.
121+
122+
Retired (or expired) CA certificates will not be published and will not be used for
123+
signing leaf certificates.
124+
type: string
117125
keyGeneration:
118126
default:
119127
rsa:
@@ -165,6 +173,20 @@ spec:
165173
In case consumers request a longer lifetime than allowed by this setting,
166174
the lifetime will be the minimum of both, so this setting takes precedence.
167175
The default value is 15 days.
176+
177+
The maximum lifetime must be less than a quarter of the active CA certificate lifetime
178+
where the active CA certificate lifetime is `ca.ca_certificate_lifetime -
179+
ca.ca_certificate_retirement_duration` to ensure that two subjects always have a common
180+
CA certificate in their trust stores – assuming that CAs are rotated at half of their
181+
active lifetimes.
182+
183+
For instance, if a pod is created right before half of the active CA lifetime has
184+
passed, then it is signed by this CA but it does not know yet the new CA certificate
185+
which is created right afterwards. If another pod is created so that its certificate
186+
lifetime ends right after the first active CA lifetime then it is signed by the new CA.
187+
The `max_certificate_lifetime` must be chosen so that these two pods have no
188+
overlapping lifetimes, otherwise the first pod would see the second one signed by an
189+
unknown CA certificate. This can be achieved by the mentioned formula.
168190
type: string
169191
required:
170192
- ca
@@ -519,6 +541,14 @@ spec:
519541
If `autoGenerate: true` then the Secret Operator will prepare a new CA certificate the old CA approaches expiration.
520542
If `autoGenerate: false` then the Secret Operator will log a warning instead.
521543
type: string
544+
caCertificateRetirementDuration:
545+
default: 1h
546+
description: |-
547+
Duration at the end of the CA certificate lifetime where no signed certificate will exist.
548+
549+
Retired (or expired) CA certificates will not be published and will not be used for
550+
signing leaf certificates.
551+
type: string
522552
keyGeneration:
523553
default:
524554
rsa:
@@ -570,6 +600,20 @@ spec:
570600
In case consumers request a longer lifetime than allowed by this setting,
571601
the lifetime will be the minimum of both, so this setting takes precedence.
572602
The default value is 15 days.
603+
604+
The maximum lifetime must be less than a quarter of the active CA certificate lifetime
605+
where the active CA certificate lifetime is `ca.ca_certificate_lifetime -
606+
ca.ca_certificate_retirement_duration` to ensure that two subjects always have a common
607+
CA certificate in their trust stores – assuming that CAs are rotated at half of their
608+
active lifetimes.
609+
610+
For instance, if a pod is created right before half of the active CA lifetime has
611+
passed, then it is signed by this CA but it does not know yet the new CA certificate
612+
which is created right afterwards. If another pod is created so that its certificate
613+
lifetime ends right after the first active CA lifetime then it is signed by the new CA.
614+
The `max_certificate_lifetime` must be chosen so that these two pods have no
615+
overlapping lifetimes, otherwise the first pod would see the second one signed by an
616+
unknown CA certificate. This can be achieved by the mentioned formula.
573617
type: string
574618
required:
575619
- ca

0 commit comments

Comments
 (0)