LCMD db logoLCMD[db]

TLS Certificate

Guide for renewing the DigiCert TLS certificate for lcmd-app.epfl.ch

The TLS certificate for lcmd-app.epfl.ch is issued by DigiCert (Basic OV) and must be renewed manually. The certificate and private key are stored as a SOPS-encrypted Kubernetes secret (lcmd-app-tls) and terminated by Traefik at the ingress.

Certificate details

FieldValue
Common Namelcmd-app.epfl.ch
IssuerDigiCert Inc
Account ID1611318
OrganizationEPFL
Contactlcmd-app@groupes.epfl.ch

Renewal process

Request the certificate

Ask the EPFL IT security team (currently Florent Rauth — rauth@epfl.ch) to renew using the same CSR as before.

Once approved, you'll receive a DigiCert zip containing:

  • lcmd-app_epfl_ch.crt — server certificate
  • DigiCertCA.crt — intermediate CA
  • TrustedRoot.crt — root CA

Run the renewal script

just renew_tls_cert ~/Downloads/lcmd-app_epfl_ch_<order_number>.zip

The script extracts the chain, fetches the existing private key from the cluster, verifies the cert matches the key, encrypts the new secret with SOPS → tls-secret.enc.yaml, and applies it to the cluster.

Commit and push

git add infrastructure/kubernetes/app/overlays/prod/tls-secret.enc.yaml
git commit -m "chore(infra): renew TLS certificate"
git push origin main

ArgoCD auto-syncs.

Verify

echo | openssl s_client -connect lcmd-app.epfl.ch:443 -servername lcmd-app.epfl.ch 2>/dev/null \
  | openssl x509 -noout -subject -dates -serial

Run the EPFL security scan: secure-it.epfl.ch/testssl2/testssl.php.

Prerequisites

  • sops, kubectl, openssl, unzip
  • SOPS age private key in ~/.config/sops/age/keys.txt (get from 1Password)
  • kubectl access to the prod namespace (see Kubernetes Setup)
  • Connected to EPFL network (or VPN)

The SOPS_AGE_KEY_FILE environment variable must point to ~/.config/sops/age/keys.txt. Add export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt to your shell profile if SOPS can't find the key automatically.

Architecture

DigiCert zip
  ├── lcmd-app_epfl_ch.crt  ──┐
  └── DigiCertCA.crt         ──┴── fullchain.pem

                            ┌──────────┘

            tls-secret.enc.yaml (SOPS + age)

                    ArgoCD decrypt


              K8s Secret: lcmd-app-tls
                   (tls.crt + tls.key)


                   Traefik Ingress
                  (TLS termination)

Cipher suite

The Traefik TLSOption (strict-tls) restricts connections to PFS + AEAD ciphers only:

CipherProtocol
TLS_AES_128_GCM_SHA256TLS 1.3
TLS_AES_256_GCM_SHA384TLS 1.3
TLS_CHACHA20_POLY1305_SHA256TLS 1.3
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256TLS 1.2
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384TLS 1.2
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256TLS 1.2

Troubleshooting

Certificate/key mismatch: If the script reports a mismatch, the CSR used for the renewal differs from the deployed key. You'll need to generate a new key + CSR and request a new certificate.

SOPS MAC error in ArgoCD: If ArgoCD fails with a MAC integrity error, re-encrypt the affected file:

cd infrastructure/kubernetes/app/overlays/prod
SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops --decrypt --ignore-mac <file>.enc.yaml > <file>.yaml
SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops --encrypt <file>.yaml > <file>.enc.yaml
rm <file>.yaml

ArgoCD not syncing: Force a refresh:

kubectl patch application lcmd-app -n argocd \
  --type merge -p '{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"hard"}}}'

On this page