Skip to content

How to rotate and renew expired Contour secrets (contourcert and envoycert)

The purpose is to provide a detailed guide on how to rotate and renew expired secrets (contourcert and envoycert) under projectcountour namespace. It includes a step-by-step workaround, prerequisites, and verification steps.

Symptoms:

  • Envoy (pods with name starting with "projectcontour-envoy-") pods under projectcontour namespace show restarts.

Example

kubectl get pods -n proejctcontour

NAMESPACE        NAME                                     READY  STATUS    RESTARTS  AGE  
projectcontour   projectcontour-contour-c476b465b-b5bvn    1/1   Running    1        16h  
projectcontour   projectcontour-contour-c476b465b-jkhsd    1/1   Running    1        16h                    
projectcontour   projectcontour-envoy-wegsv                1/2   Running    161      16h          
projectcontour   projectcontour-envoy-sd7xc                1/2   Running    161      16h      
projectcontour   projectcontour-envoy-u9hac                1/2   Running    162      16h
  • The secrets (contourcert and envoycert) in projectcontour namespace are expired.

Example

kubectl  get secret -n projectcontour envoycert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -dates
notBefore=Jul 24 19:11:31 2023 GMT
notAfter=Jul 26 19:11:31 2024 GMT

kubectl  get secret -n projectcontour contourcert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -dates
notBefore=Jul 24 19:11:31 2023 GMT
notAfter=Jul 26 19:11:31 2024 GMT
  • The log for envoy pods in the projectcontour namespace (pods with name starting with "projectcontour-envoy-") reports TLS errors, such as:

      kubectl  logs -n projectcontour <envoy pod name>
    

  • [2024-07-26 16:32:01.575][1][warning][config] [bazel-out/k8-opt/bin/source/common/config/_virtual_includes/grpc_stream_lib/common/config/grpc_stream.h:101] StreamListeners gRPC config stream closed: 14, upstream connect error or disconnect/reset before headers. reset reason: connection failure, transport failure reason: TLS error: 268435581:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED

Cause

  • The secrets (contourcert and envoycert) are used for internal gRPC communication between Contour and Envoy.

Update the contour and under all the resources defined in "contour_certgen_job.yaml" which has following tags:

"helm.sh/chart: contour-<version>"
"image: docker.io/bitnami/contour:<image_version>"
You can get the current version by running "kubectl describe" on contour deployment/pods.

Run the following commands from manager node to rotate/renew the expired contourcert/envoycert (you can run these commands on the control plane node, there is no need to pass kubeconfig option in that case):

1. Take backup:
    kubectl get secret -n projectcontour contourcert -o yaml > contourcert-backup.yaml
  kubectl get secret -n projectcontour envoycert -o yaml > envoycert-backup.yaml

2. Delete current contourcert and envoycert:
   kubectl delete secret -n projectcontour envoycert contourcert

3. Generate new contourcert and envoycert :
    kubectl apply -f <path>/contour_certgen_job.yaml -n projectcontour

4. Verify that the new contourcert/envoycert was generated:
    kubectl get secret -n projectcontour

5. Verify that the validity period of new secrets is 10 years:
  kubectl get secret -n projectcontour envoycert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -dates
  kubectl get secret -n projectcontour contourcert -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -dates

6. Restart all contour pods:
   kubectl patch deployment projectcontour-contour -n projectcontour -p '{"spec": {"template": {"metadata": {"labels":{"test": "restart"} } } } }' --type=merge

7. Restart all envoy pods:
  kubectl patch daemonset projectcontour-envoy -n projectcontour -p '{"spec": {"template": {"metadata": {"labels":{"test": "restart"} } } } }' --type=merge

8. Delete projectcontour-contour-certgen job if it is present:
  kubectl get job projectcontour-contour-certgen -n projectcontour

    If the output of above command shows the projectcontour-contour-certgen job, then run below command to delete it: 
  kubectl delete job projectcontour-contour-certgen -n projectcontour

9. Verify all contour/envoy pods are in running state and not restarting:
  kubectl get pods -n projectcontour

Manifest

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
  name: projectcontour-contour-certgen-psp
  namespace: zerone-projectcontour
spec:
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  fsGroup:
    rule: RunAsAny
  hostIPC: true
  hostNetwork: true
  hostPID: true
  hostPorts:
  - max: 65535
    min: 0
  privileged: true
  runAsUser:
    rule: RunAsAny
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  volumes:
  - '*'
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: projectcontour-contour-certgen
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: projectcontour-contour-certgen-psp
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
rules:
- apiGroups:
  - policy
  resourceNames:
  - projectcontour-contour-certgen-psp
  resources:
  - podsecuritypolicies
  verbs:
  - use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: projectcontour-contour-certgen-psp
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: projectcontour-contour-certgen-psp
subjects:
  - kind: ServiceAccount
    name: projectcontour-contour-certgen
    namespace: zerone-projectcontour
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: projectcontour-contour-certgen
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
rules:
  - apiGroups:
      - ""
    resources:
      - secrets
    verbs:
      - create
      - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: projectcontour-contour-certgen
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: projectcontour-contour-certgen
subjects:
  - kind: ServiceAccount
    name: projectcontour-contour-certgen
---
apiVersion: batch/v1
kind: Job
metadata:
  name: projectcontour-contour-certgen
  namespace: zerone-projectcontour
  annotations:
    "helm.sh/hook": "pre-install,pre-upgrade"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
  labels:
    app.kubernetes.io/name: contour
    helm.sh/chart: contour-10.2.2
    app.kubernetes.io/instance: projectcontour
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: contour-certgen
spec:
  ttlSecondsAfterFinished: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: contour
        helm.sh/chart: contour-10.2.2
        app.kubernetes.io/instance: projectcontour
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: contour-certgen
    spec:

      containers:
        - name: contour
          image: docker.io/bitnami/contour:1.23.3-debian-11-r0
          imagePullPolicy: IfNotPresent
          command:
            - contour
          args:
            - certgen
            - --kube
            - --incluster
            - --overwrite
            - --secrets-format=compact
            - --namespace=$(CONTOUR_NAMESPACE)
            - --certificate-lifetime=3650
          env:
            - name: CONTOUR_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          resources:
            limits:
              memory: 256Mi
            requests:
              cpu: 40m
              memory: 32Mi
      restartPolicy: Never
      serviceAccountName: projectcontour-contour-certgen
      securityContext:
        runAsUser: 1001
        runAsGroup: 1001
        fsGroup:
        runAsNonRoot: true
  parallelism: 1
  completions: 1
  backoffLimit: 1