Cert-Manager Integration
Cert-Manager is a common tool to manage certificates in Kubernetes, especially when backed by an external Certificate Authority (CA) such as Let's Encrypt.
The Stackable Secret Operator does not currently support managing Cert-Manager certificates directly, but it can be configured to consume certificates generated by it.
Caveats
Cert-Manager is designed to manage relatively long-lived certificates that are stored in Kubernetes Secrets. By contrast, the Stackable Secret Operator is designed to generate temporary short-lived certificates.
This has a couple of repercussions:
-
Longer-lived certificates mean that a leaked certificate has potential to be abused for longer.
-
Application teams may have access to read Secrets in their respective applications' Namespaces.
Where possible, we recommend using the autoTls
backend instead.
Configuring Cert-Manager
We recommend using the autoTls backend instead for self-signed PKIs. We use Cert-Manager’s CA issuer here to show the broader concepts.
|
To do this, you will first need to teach Cert-Manager how to create your certificates.
In a production setup this will likely use an external CA such as ACME or OpenBao/Vault. However, to make this guide self-contained, Cert-Manager will create a self-signed CA certificate instead.
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: secret-operator-demonstration (1)
spec:
ca:
secretName: secret-operator-demonstration-ca
# Create a self-signed CA for secret-operator-demonstration to use
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: secret-operator-demonstration-ca
spec:
secretName: secret-operator-demonstration-ca
isCA: true
commonName: Stackable Secret Operator/Cert-Manager Demonstration CA
issuerRef:
kind: Issuer
name: secret-operator-demonstration-ca
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: secret-operator-demonstration-ca
spec:
selfSigned: {}
1 | This is the Issuer that our created certificates will reference later |
Creating a SecretClass
The Stackable Secret Operator needs to know how to find the certificates created by Cert-Manager. We do this by creating
a SecretClass
using the k8sSearch
backend, which can find arbitrary
Kubernetes Secret objects that have the correct labels.
---
apiVersion: secrets.stackable.tech/v1alpha1
kind: SecretClass
metadata:
name: tls-cert-manager (1)
spec:
backend:
k8sSearch:
searchNamespace:
pod: {} (2)
1 | Both certificates and Pods will reference this name, to ensure that the correct certificates are found |
2 | This informs the Secret Operator that certificates will be found in the same namespace as the Pod using it |
Requesting a certificate
You can now use Cert-Manager to provision your first certificate. Use labels to inform the Stackable Secret Operator about which scopes the certificate fulfills. Which scopes must be provisioned is going to depend on the design of the workload. This guide assumes the service scope.
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-app-tls (1)
spec:
secretName: my-app-tls (2)
secretTemplate:
labels:
secrets.stackable.tech/class: tls-cert-manager (3)
secrets.stackable.tech/service: my-app (4)
dnsNames:
- my-app (5)
issuerRef:
kind: Issuer
name: secret-operator-demonstration (6)
1 | The Certificate name is irrelevant for the Stackable Secret Operator’s, but must be unique (within the Namespace) |
2 | The Secret name must also be unique within the Namespace |
3 | This tells the Stackable Secret Operator that this secret corresponds to the SecretClass created before |
4 | This secret fulfils the service scope for my-app |
5 | The list of DNS names that this certificate should apply to. |
6 | The Cert-Manager Issuer that should sign this certificate, as created before |
Using the certificate
Finally, we can create and expose a Pod that consumes the certificate!
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: tls
mountPath: /tls
- name: config
mountPath: /etc/nginx/conf.d
ports:
- name: https
containerPort: 443
volumes:
- name: tls (1)
ephemeral:
volumeClaimTemplate:
metadata:
annotations:
secrets.stackable.tech/class: tls-cert-manager (2)
secrets.stackable.tech/scope: service=my-app (3)
spec:
storageClassName: secrets.stackable.tech
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "1"
- name: config
configMap:
name: my-app
--- (4)
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app
data:
default.conf: |
server {
listen 443 ssl;
ssl_certificate /tls/tls.crt;
ssl_certificate_key /tls/tls.key;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
--- (5)
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- name: https
port: 443
1 | A secret volume is created, where the certificate will be exposed to the app |
2 | The volume references the SecretClass defined before |
3 | The app is designated the scope service=my-app , matching the certificate’s scope |
4 | nginx is configured to use the mounted certificate |
5 | nginx is exposed as a Kubernetes Service |