Skip to content

Commit 3512603

Browse files
Add a backend that requests Cert-Manager certificates (#482)
* Initial prototype for cert-manager integration See #474 * Use PVC name as cert-manager secret, rather than pod name * Move cert-manager integration into a separate backend * Reorganize * Cert-Manager: only set node label if volume has node scope * CRD docs * Fix CSI always being considered dirty * Reference docs * Update cert-manager guide to use cert-manager backend * Fix rustdoc warnings * Regenerate CRDs * Allow overriding Cert-Manager lifetimes * Changelog * Make pvc_name optional * Update rust/operator-binary/src/backend/cert_manager.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Update rust/operator-binary/src/backend/mod.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Update rust/operator-binary/src/backend/mod.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Update tests/templates/kuttl/cert-manager-tls/consumer.yaml Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Update rust/operator-binary/src/crd.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Align cert-manager CRDs better with upstream * Regenerate CRD * Quote scopes in errors * Add context for certificate object errors * Make Clippy happy * cargo fmt --------- Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com>
1 parent 74ce5da commit 3512603

File tree

32 files changed

+617
-62
lines changed

32 files changed

+617
-62
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
77
### Added
88

99
- Active Directory's `samAccountName` generation can now be customized ([#454]).
10+
- Added experimental cert-manager backend ([#482]).
1011

1112
### Fixed
1213

@@ -18,6 +19,7 @@ All notable changes to this project will be documented in this file.
1819
- Remove custom `h2` patch, as Kubernetes 1.26 has fixed the invalid data from Kubernetes' side. Starting with 24.11 we only support at least 1.27 (as it's needed by OpenShift 4.14) ([#495]).
1920

2021
[#454]: https://github.com/stackabletech/secret-operator/pull/454
22+
[#482]: https://github.com/stackabletech/secret-operator/pull/482
2123
[#490]: https://github.com/stackabletech/secret-operator/pull/490
2224
[#495]: https://github.com/stackabletech/secret-operator/pull/495
2325
[#497]: https://github.com/stackabletech/secret-operator/pull/497

deploy/helm/secret-operator/crds/crds.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ spec:
3131
- k8sSearch
3232
- required:
3333
- autoTls
34+
- required:
35+
- experimentalCertManager
3436
- required:
3537
- kerberosKeytab
3638
properties:
@@ -79,6 +81,43 @@ spec:
7981
required:
8082
- ca
8183
type: object
84+
experimentalCertManager:
85+
description: |-
86+
The [`experimentalCertManager` backend][1] injects a TLS certificate issued by [cert-manager](https://cert-manager.io/).
87+
88+
A new certificate will be requested the first time it is used by a Pod, it will be reused after that (subject to cert-manager renewal rules).
89+
90+
[1]: https://docs.stackable.tech/home/nightly/secret-operator/secretclass#backend-certmanager
91+
properties:
92+
defaultCertificateLifetime:
93+
default: 1d
94+
description: |-
95+
The default lifetime of certificates.
96+
97+
Defaults to 1 day. This may need to be increased for external issuers that impose rate limits (such as Let's Encrypt).
98+
type: string
99+
issuer:
100+
description: A reference to the cert-manager issuer that the certificates should be requested from.
101+
properties:
102+
kind:
103+
description: |-
104+
The kind of the issuer, Issuer or ClusterIssuer.
105+
106+
If Issuer then it must be in the same namespace as the Pods using it.
107+
enum:
108+
- Issuer
109+
- ClusterIssuer
110+
type: string
111+
name:
112+
description: The name of the issuer.
113+
type: string
114+
required:
115+
- kind
116+
- name
117+
type: object
118+
required:
119+
- issuer
120+
type: object
82121
k8sSearch:
83122
description: The [`k8sSearch` backend](https://docs.stackable.tech/home/nightly/secret-operator/secretclass#backend-k8ssearch) can be used to mount Secrets across namespaces into Pods.
84123
properties:

deploy/helm/secret-operator/templates/roles.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ volumes:
3434
- projected
3535
- hostPath
3636
- emptyDir
37-
{{ end }}
37+
{{ end }}
3838
---
3939
apiVersion: rbac.authorization.k8s.io/v1
4040
kind: ClusterRole
@@ -105,6 +105,14 @@ rules:
105105
- podlisteners
106106
verbs:
107107
- get
108+
- apiGroups:
109+
- cert-manager.io
110+
resources:
111+
- certificates
112+
verbs:
113+
- get
114+
- patch
115+
- create
108116
{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }}
109117
- apiGroups:
110118
- security.openshift.io
@@ -114,4 +122,4 @@ rules:
114122
- securitycontextconstraints
115123
verbs:
116124
- use
117-
{{ end }}
125+
{{ end }}

docs/modules/secret-operator/examples/cert-manager/certificate.yaml

Lines changed: 0 additions & 16 deletions
This file was deleted.

docs/modules/secret-operator/examples/cert-manager/pod.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ spec:
3131
metadata:
3232
annotations:
3333
secrets.stackable.tech/class: tls-cert-manager # <2>
34-
secrets.stackable.tech/scope: service=my-app # <3>
34+
secrets.stackable.tech/scope: node,service=my-app # <3>
3535
spec:
3636
storageClassName: secrets.stackable.tech
3737
accessModes:

docs/modules/secret-operator/examples/cert-manager/secretclass.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ metadata:
55
name: tls-cert-manager # <1>
66
spec:
77
backend:
8-
k8sSearch:
9-
searchNamespace:
10-
pod: {} # <2>
8+
experimentalCertManager:
9+
issuer:
10+
kind: Issuer # <2>
11+
name: secret-operator-demonstration # <3>

docs/modules/secret-operator/pages/cert-manager.adoc

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
= Cert-Manager Integration
22

3+
WARNING: The Cert-Manager backend is experimental, and subject to change.
4+
35
https://cert-manager.io/[Cert-Manager] is a common tool to manage certificates in Kubernetes, especially when backed by an external
46
Certificate Authority (CA) such as https://letsencrypt.org/[Let\'s Encrypt].
57
6-
The Stackable Secret Operator does not currently support managing Cert-Manager certificates directly, but it can be configured to consume certificates generated by it.
8+
The Stackable Secret Operator supports requesting certificates from Cert-Manager.
79
810
[#caveats]
911
== Caveats
@@ -37,46 +39,28 @@ include::example$cert-manager/issuer.yaml[]
3739
[#secretclass]
3840
== Creating a SecretClass
3941

40-
The Stackable Secret Operator needs to know how to find the certificates created by Cert-Manager. We do this by creating
41-
a xref:secretclass.adoc[] using the xref:secretclass.adoc#backend-k8ssearch[`k8sSearch` backend], which can find arbitrary
42-
Kubernetes Secret objects that have the correct labels.
42+
The Stackable Secret Operator needs to know how to request the certificates from Cert-Manager. We do this by creating
43+
a xref:secretclass.adoc[] using the xref:secretclass.adoc#backend-certmanager[`experimentalCertManager` backend].
4344

4445
[source,yaml]
4546
----
4647
include::example$cert-manager/secretclass.yaml[]
4748
----
4849
<1> Both certificates and Pods will reference this name, to ensure that the correct certificates are found
49-
<2> This informs the Secret Operator that certificates will be found in the same namespace as the Pod using it
50-
51-
[#certificate]
52-
== Requesting a certificate
53-
54-
You can now use Cert-Manager to provision your first certificate. Use labels to inform the Stackable Secret Operator
55-
about which xref:scope.adoc[scopes] the certificate fulfills. Which scopes must be provisioned is going to depend
56-
on the design of the workload. This guide assumes the xref:scope.adoc#service[service] scope.
57-
58-
[source,yaml]
59-
----
60-
include::example$cert-manager/certificate.yaml[]
61-
----
62-
<1> The Certificate name is irrelevant for the Stackable Secret Operator's, but must be unique (within the Namespace)
63-
<2> The Secret name must also be unique within the Namespace
64-
<3> This tells the Stackable Secret Operator that this secret corresponds to the SecretClass created xref:#secretclass[before]
65-
<4> This secret fulfils the xref:scope.adoc#service[service] scope for `my-app`
66-
<5> The list of DNS names that this certificate should apply to.
67-
<6> The Cert-Manager Issuer that should sign this certificate, as created xref:#issuer[before]
50+
<2> This guide uses a namespaced Issuer, rather than a cluster-scoped ClusterIssuer
51+
<3> The Cert-Manager Issuer that should sign these certificates, as created xref:#issuer[before]
6852

6953
[#pod]
7054
== Using the certificate
7155

72-
Finally, we can create and expose a Pod that consumes the certificate!
56+
Finally, we can create and expose a Pod that requests and uses the certificate!
7357

7458
[source,yaml]
7559
----
7660
include::example$cert-manager/pod.yaml[]
7761
----
7862
<1> A secret xref:volume.adoc[volume] is created, where the certificate will be exposed to the app
7963
<2> The volume references the SecretClass defined xref:#secretclass[before]
80-
<3> The app is designated the scope xref:scope#service[`service=my-app`], matching the xref:#certificate[certificate's scope]
64+
<3> The app requires the certificate to be valid for the scopes xref:scope.adoc#node[`node`] and xref:scope.adoc#service[`service=my-app`]
8165
<4> nginx is configured to use the mounted certificate
8266
<5> nginx is exposed as a Kubernetes Service

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,40 @@ spec:
9494
`autoTls.ca.caCertificateLifetime` :: The lifetime of the certificate authority's root certificate.
9595
`autoTls.maxCertificateLifetime`:: Maximum lifetime the created certificates are allowed to have. In case consumers request a longer lifetime than allowed by this setting, the lifetime will be the minimum of both.
9696

97+
[#backend-certmanager]
98+
=== `experimentalCertManager`
99+
100+
*Format*: xref:#format-tls-pem[]
101+
102+
Injects a TLS certificate issued by https://cert-manager.io/[Cert-Manager].
103+
104+
WARNING: This backend is experimental, and subject to change.
105+
106+
NOTE: This backend requires https://cert-manager.io/[Cert-Manager] to already be installed and configured.
107+
108+
A new certificate will be requested the first time it is used by a Pod, it will be reused after that (subject to Cert-Manager's renewal rules).
109+
110+
Node-scoped requests will cause a Pod to become "sticky" to the Node that it was first scheduled to (like xref:#backend-k8ssearch[], but unlike xref:#backend-autotls[]).
111+
112+
==== Reference
113+
114+
[source,yaml]
115+
----
116+
spec:
117+
backend:
118+
experimentalCertManager:
119+
issuer:
120+
kind: Issuer
121+
name: secret-operator-demonstration
122+
defaultCertificateLifetime: 1d
123+
----
124+
125+
`experimentalCertManager`:: Declares that the `experimentalCertManager` backend is used.
126+
`experimentalCertManager.issuer`:: The reference to the Cert-Manager issuer that should issue the certificates.
127+
`experimentalCertManager.issuer.kind`:: The kind of the Cert-Manager issuer, either Issuer or ClusterIssuer. Note that Issuer must be in the same namespace as the Pod requesting the secret.
128+
`experimentalCertManager.issuer.name`:: The name of the Issuer or ClusterIssuer to be used.
129+
`experimentalCertManager.defaultCertificateLifetime`:: The default duration of the certificates. This may need to be increased for backends that impose stricter rate limits, such as https://letsencrypt.org/[Let's Encrypt].
130+
97131
[#backend-kerberoskeytab]
98132
=== `kerberosKeytab`
99133

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ shortened by a random amount between 0 and 4.8 hours, leaving a certificate that
9797

9898
Jittering may be disabled by setting the jitter factor to 0.
9999

100+
=== `secrets.stackable.tech/backend.cert-manager.cert.lifetime`
101+
102+
*Required*: false
103+
104+
*Default value*: `1d` (configured by xref:secretclass.adoc#backend-certmanager[the backend])
105+
106+
*Backends*: xref:secretclass.adoc#backend-autotls[]
107+
108+
The lifetime of the created certificate.
109+
110+
The format is documented in xref:concepts:duration.adoc[].
111+
100112
=== `secrets.stackable.tech/kerberos.service.names`
101113

102114
*Required*: false

rust/operator-binary/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn main() {
66
let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR is required"));
77
tonic_build::configure()
88
.file_descriptor_set_path(out_dir.join("file_descriptor_set.bin"))
9-
.compile(&["csi.proto"], &["vendor/csi"])
9+
.compile(&["vendor/csi/csi.proto"], &["vendor/csi"])
1010
.unwrap();
1111
built::write_built_file().unwrap();
1212
}

0 commit comments

Comments
 (0)