DEV Community

Tingwei
Tingwei

Posted on

Hands-on Guide to Sealed Secrets: Deploying .NET 6 Apps Securely in KIND

Repository

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

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 
Enter fullscreen mode Exit fullscreen mode
  • kubeseal (client-side utility)
go install \ github.com/bitnami-labs/sealed-secrets/cmd/kubeseal@main 
Enter fullscreen mode Exit fullscreen mode

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' 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Running cloud-provider-kind

# Run As administrator cloud-provider-kind 
Enter fullscreen mode Exit fullscreen mode

Setting up mkcert

  • Install the cert/key
# on Windows, I used choco choco install mkcert mkcert -install mkcert localhost 127.0.0.1 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

localhost-tls-secret is the tls secret for Traefik

  • Remove mkcert cert/key
mkcert -uninstall rm -rf "$(mkcert -CAROOT)" 
Enter fullscreen mode Exit fullscreen mode

Configuring Traefik with TLS

  • Deploy Traefik
helm install -f traefik-values-localhost.yaml \ traefik traefik/traefik 
Enter fullscreen mode Exit fullscreen mode
  • Update localhost tls from external ip of Traefik
mkcert localhost 127.0.0.1 172.19.0.6 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode
  • 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 
Enter fullscreen mode Exit fullscreen mode
  • 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 
Enter fullscreen mode Exit fullscreen mode

port = 10001 (whatever you want)
ConnectionStrings__PgCluster is from the key "pg-cluster" in the secret "ms-dev-secret"

Testing

ms swagger

Done!

Top comments (0)