Objective
This guide demonstrates encrypting Kubernetes secrets with Sealed Secrets, using a PostgreSQL connection string as an example, and setting up a local Traefik TLS secret with mkcert.
Prerequisites
- KIND
- cloud-provider-kind
- Sealed Secrets
- kubectl
- Helm
- Traefik
- mkcert
- .NET 6
Installing Sealed Secrets
- Sealed Secrets controller (Cluster-Side Controller)
# by Helm
helm repo add sealed-secrets \
https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets -n kube-system \
--set-string fullnameOverride=sealed-secrets-controller \
sealed-secrets/sealed-secrets
- kubeseal (client-side utility)
go install \
github.com/bitnami-labs/sealed-secrets/cmd/kubeseal@main
Creating the sealed secret with Sealed Secrets
- Create the Kubernetes secret
kubectl create secret generic ms-dev-secret \
--dry-run=client -o yaml > appsettings-secret.Development.yaml \
--from-literal=pg-cluster='Host=your_host_ip;Port=5432;Database=your_database;Username=your_username;Password=your_password'
pg-cluster is the name of the key in the secret.
ms-dev-secret is the name of the secret.
- Create the sealed secret from the secret
kubeseal --scope cluster-wide \
< appsettings-secret.Development.yaml -o yaml \
> appsettings-sealed-secret.Development.yaml
appsettings-sealed-secret.Development.yaml is the sealed secret.
--scope
- How to decrypt the sealed secret to the secret
# First, find the sealed secret controller
# Running kubectl get secrets -A will show secrets starting with sealed-secrets-
# For example, sealed-secrets-abc
kubectl get secrets -A
# Secondly, get the private key "sealed-secret-key.pem"
kubectl get secret -n kube-system sealed-secrets-abc \
-o jsonpath='{.data.tls\.key}' | base64 --decode \
> sealed-secret-key.pem
# Finally, decrypt the sealed secret "appsettings-sealed-secret.Development.yaml"
# to the secret "appsettings-secret.Development.yaml"
kubeseal --recovery-unseal \
--recovery-private-key sealed-secret-key.pem \
< appsettings-sealed-secret.Development.yaml \
-o yaml > appsettings-secret.Development.yaml
# memo: decode base64 string
echo [base64_string] | base64 --decode
Running cloud-provider-kind
# Run As administrator
cloud-provider-kind
Setting up mkcert
- Install the cert/key
# on Windows, I used choco
choco install mkcert
mkcert -install
mkcert localhost 127.0.0.1
localhost.pem is the mkcert cert
localhost-key.pem is the mkcert key
Creating the Kubernetes tls secret for Traefik
kubectl create secret tls localhost-tls-secret \
--cert=localhost.pem \
--key=localhost-key.pem \
--namespace=default
localhost-tls-secret is the tls secret for Traefik
- Remove mkcert cert/key
mkcert -uninstall
rm -rf "$(mkcert -CAROOT)"
Configuring Traefik with TLS
- Deploy Traefik
helm install -f traefik-values-localhost.yaml \
traefik traefik/traefik
- Update localhost tls from external ip of Traefik
mkcert localhost 127.0.0.1 172.19.0.6
172.19.0.6 is the Traefik external ip given by cloud-provider-kind
remember to rename new key and new cert as "localhost.pem" and "localhost-key.pem"
- traefik-values-localhost.yaml
ports:
web:
redirectTo:
port: websecure
tlsStore:
default:
defaultCertificate:
secretName: localhost-tls-secret
Deploying the .NET 6 App with Sealed Secrets
# deploy sealed secret first
kubectl apply -f appsettings-sealed-secret.Development.yaml
# deploy the app
kubectl apply -f deploy.yaml
- appsettings-sealed-secret.Development.yaml
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: "true"
creationTimestamp: null
name: ms-dev-secret
namespace: default
spec:
encryptedData:
pg-cluster: AgAjyf765/UaOa80g1Z+30zEQ4Sgu5uLU+9EwKEQ5yiZC+8lczxJERGpq1oMmzdL2jLe9sYT7jREGynPDcczX8Y5tU+ZH4ADiuUgbgjpOCrfOXT68AF2LEMu6JFhu3bqMhLbYL1yUMTxQmf6YP/kvPxZ3kTyWaiQfOuTTwgKwGRT50mOrbYJaMMJxH6HMe4V8kPQ6qbpg9FSU1S6DzHCI+pf0HKg8cuCLUjxOXNL7Bc7X+PqGG3nP/BlF4WVaGvYpVCxC/4IakPZIwSI5mUWRpSabFl1f+rP2OmuEuChm3l9XzYTZyXUAu8f9c/jR4FmnK4DVv0MOMeVhsSFf/eS64l2HC6/evKscL2AIvw2HWX/bpTJeeNfA0neSqNYn4zMMTs2nSFsQTh0Qd5XE/zdprYY74B6UlbcNZHiOAeKiHlo8bL44ulCcZBTWviBejd7dB/ot07l0lfo0aa1F2O/WKT6/gdGNnxULx7weZbwDKjb970OmtJpmS4HKiwf3vnlOoF6tGsPzd7Bj3mXEFnMPFKdnZ/nwsKc8+lVRFBQ5CaWZziXD5T5uOQB/lSUFoY7miCpGEM49INUbfPpZKZd4aW1ZPFJ1bszlkBL2F7gkl3i+vVtqFHqv3DIF5RG/Vk537dppLEsjA3qK0vGjJJzYTCBppyij86PRcI7EjGeDd63YTuHP59U/RKOfCfLdEntuABjWN124N3rpmQB60Tdr84AWVmQG8SUD1dBSii6cGtqNVN8iLqZqAn1cfGQhV0nG56Ciyd+/hTAVIxTAGqvKMXO/OJWTuzZ9X8p0Ry5WgYDWMgwV+Du7gUJ6Bb+kq2TTTOXGKiUXKFu3wl0vj2WpBmZiYV10DNb9k2jlSVx6JA=
template:
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: "true"
creationTimestamp: null
name: ms-dev-secret
namespace: default
- deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ms
labels:
app: ms
spec:
replicas: 1
selector:
matchLabels:
app: ms
template:
metadata:
labels:
app: ms
spec:
containers:
- name: ms-app
image: ghcr.io/tingwei628/ms/ms:latest
ports:
- containerPort: 10001
env:
- name: DOTNET_ENVIRONMENT
value: Development
- name: ConnectionStrings__PgCluster
valueFrom:
secretKeyRef:
name: ms-dev-secret
key: pg-cluster
- name: ASPNETCORE_URLS
value: http://+:10001
livenessProbe:
httpGet:
path: /health
port: 10001
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 10001
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: ms-service
labels:
app: ms
spec:
selector:
app: ms
ports:
- port: 10001
targetPort: 10001
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ms-ingress
namespace: default
labels:
app: ms
spec:
entryPoints:
- websecure
routes:
- match: Host(`172.19.0.6`) && PathPrefix(`/ms`)
kind: Rule
middlewares:
- name: strip-ms-prefix
services:
- name: ms-service
port: 10001
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-ms-prefix
labels:
app: ms
spec:
stripPrefix:
prefixes:
- /ms
port = 10001 (whatever you want)
ConnectionStrings__PgCluster is from the key "pg-cluster" in the secret "ms-dev-secret"
Testing
Done!
Top comments (0)