A la lliçó anterior vam publicar www.rutasnorte.example i api.rutasnorte.example amb un únic Ingress, i vam tancar assenyalant el problema que no admet demora: tot viatja en HTTP en clar. El nom, el DNI, el telèfon i el correu que un client escriu per reservar un bitllet, i les dades que van cap a la passarel·la de pagaments, estan creuant internet sense xifrar. Aquesta lliçó posa HTTPS als dos dominis: primer a mà amb un certificat autosignat per entendre les peces, i després de manera automàtica amb cert-manager, que emetrà i renovarà certificats de Let's Encrypt sense que ningú hagi de recordar res.

Advertiment de seguretat. Aquesta lliçó ensenya la mecànica de Kubernetes per gestionar certificats. La configuració criptogràfica real d'una plataforma en producció —conjunts de xifratge, versions de TLS permeses, polítiques d'HSTS, custòdia de claus privades, requisits de compliment normatiu— l'ha de revisar i aprovar un professional de seguretat. Els valors que apareixen aquí són didàctics i raonables el 2026, però envelleixen: no els copiïs a producció sense validar-los amb qui correspongui a la teva organització.

Contingut

  1. Per què HTTPS és obligatori a Rutas Norte
  2. On acaba el TLS en un clúster
  3. El Secret kubernetes.io/tls i el bloc tls: de l'Ingress
  4. Certificat autosignat per a desenvolupament
  5. cert-manager: quin problema resol i instal·lació
  6. Els objectes de cert-manager i el flux d'emissió
  7. ACME amb Let's Encrypt: HTTP-01 davant de DNS-01
  8. Emissió automàtica des de l'Ingress
  9. L'entorn de staging i els límits d'emissió
  10. Renovació automàtica i comprovació de l'estat
  11. Redirecció a HTTPS i HSTS
  12. Més enllà: mTLS i malles de servei

  1. Per què HTTPS és obligatori a Rutas Norte

No és una bona pràctica: és un requisit.

Motiu Què passa sense TLS
Dades personals Nom, DNI, telèfon i correu viatgen llegibles per qualsevol xarxa intermèdia. Al RGPD, el xifratge en trànsit és una mesura tècnica esperable
Pagaments PCI DSS exigeix xifratge del canal. Cap passarel·la seriosa no accepta integrar-se per HTTP
Sessions Un JWT o una galeta interceptada en una wifi pública és un compte robat
Integritat Un intermediari pot modificar la resposta: injectar scripts, canviar l'import
Navegadors i SEO Marquen "No segur", bloquegen API (geolocalització, service workers) i els cercadors penalitzen HTTP. La botiga semblaria fraudulenta

I una precisió: TLS no només xifra, també autentica. El certificat demostra que qui respon a www.rutasnorte.example és realment Rutas Norte i no algú que ha segrestat el DNS. Sense aquella part, el xifratge seria amb un desconegut.

  1. On acaba el TLS en un clúster

"Acabar el TLS" significa desxifrar: el punt on el trànsit deixa d'estar xifrat. Hi ha tres opcions.

flowchart LR
    CA["Client"] -->|HTTPS| IA["A. Ingress:<br/>desxifra aqui"]
    IA -->|"HTTP en clar"| SA["Pods"]
    CB["Client"] -->|HTTPS| IB["B. Ingress: desxifra<br/>i torna a xifrar"]
    IB -->|HTTPS| SB["Pods amb certificat propi"]
    CC["Client"] -->|HTTPS| IC["C. Ingress: NO desxifra,<br/>reenvia per SNI"]
    IC -->|HTTPS| SC["El pod acaba el TLS"]
Model Avantatges Inconvenients Quan
A. Terminació a l'Ingress Un sol lloc amb certificats; les aplicacions no saben de TLS; el controlador pot encaminar per ruta i capçaleres El trànsit intern va en clar dins del clúster Per defecte, i el que fa servir Rutas Norte
B. Rexifratge Xifrat també dins del clúster Cada aplicació gestiona el seu certificat Entorns regulats, xarxes no fiables
C. Passthrough El certificat no surt mai de l'aplicació L'Ingress no veu HTTP: sense encaminament per ruta, sense reescriptura mTLS estricte, protocols opacs

Rutas Norte fa servir A. El trànsit intern en clar es compensa amb les polítiques de xarxa de 04-06, i si calgués xifratge dins del clúster, la resposta seria una malla de servei (08-04).

  1. El Secret kubernetes.io/tls i el bloc tls: de l'Ingress

Un certificat a Kubernetes és un Secret de tipus kubernetes.io/tls amb exactament dues claus: tls.crt, la cadena de certificats en PEM (el del servidor primer, després els intermedis), i tls.key, la clau privada en PEM. El tipus obliga que totes dues existeixin.

Recorda de 03-02: això és base64, no xifratge, i qualsevol amb permís de lectura sobre Secrets en aquell namespace té la clau privada de la plataforma; és l'argument més contundent per al RBAC de 08-01 i per al xifratge en repòs d'etcd. L'Ingress el consumeix amb el bloc tls::

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rutas-norte
  namespace: rutas-norte-pro
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - www.rutasnorte.example
        - api.rutasnorte.example
      secretName: rutasnorte-tls      # Secret del MATEIX namespace que l'Ingress
  rules:
    # ... les mateixes regles de 04-04, sense canvis

Tres regles que trenquen això si no es compleixen: el Secret ha de ser al mateix namespace que l'Ingress (no hi ha referències entre namespaces, així que publicar els tres entorns exigeix el certificat als tres, i cert-manager el pot emetre a cadascun); els hosts del bloc tls han de coincidir amb els del certificat, o el navegador donarà ERR_CERT_COMMON_NAME_INVALID; i es poden posar diverses entrades tls, cadascuna amb el seu Secret, triant el controlador per SNI, el nom que el client anuncia en iniciar la salutació TLS.

  1. Certificat autosignat per a desenvolupament

Per a rutas-norte-dev no demanem certificats a una autoritat pública: el domini no existeix i el clúster és local. En generem un de propi.

# Clau privada de 2048 bits i certificat autosignat a 365 dies
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout /tmp/rutasnorte-dev.key \
  -out    /tmp/rutasnorte-dev.crt \
  -subj "/C=ES/ST=Cantabria/L=Santander/O=Rutas Norte S.L./CN=www.rutasnorte.example" \
  -addext "subjectAltName=DNS:www.rutasnorte.example,DNS:api.rutasnorte.example"

Desglossament: -x509 genera un certificat directament en lloc d'una sol·licitud (CSR); -nodes deixa la clau privada sense contrasenya, imprescindible perquè ningú no la teclejarà en arrencar; -newkey rsa:2048 crea clau i certificat en un pas; -subj dona les dades del titular sense preguntar; i -addext subjectAltName afegeix el camp que de debò importa.

Sobre subjectAltName (SAN): els navegadors fa anys que ignoren el CN i validen exclusivament les SAN, de manera que un certificat sense SAN es rebutja encara que el CN sigui perfecte —és l'error número u en generar certificats a mà—. Aquí en necessitem dues, una per domini, perquè el mateix certificat serveixi la botiga i l'API.

kubectl create secret tls rutasnorte-tls \
  --cert=/tmp/rutasnorte-dev.crt --key=/tmp/rutasnorte-dev.key -n rutas-norte-dev

# Verificar que les SAN son les esperades
kubectl get secret rutasnorte-tls -n rutas-norte-dev \
  -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -text \
  | grep -A1 "Subject Alternative Name"

curl -k -s -o /dev/null -w '%{http_code}\n' https://www.rutasnorte.example/
curl    -s https://www.rutasnorte.example/ 2>&1 | head -1
X509v3 Subject Alternative Name:
    DNS:www.rutasnorte.example, DNS:api.rutasnorte.example
200
curl: (60) SSL certificate problem: self-signed certificate

kubectl create secret tls posa el tipus correcte i les dues claus amb els noms correctes.

Amb -k funciona; sense -k, falla. Això és correcte i és la lliçó: el certificat xifra igual de bé, però ningú no pot verificar qui el va emetre, i el navegador mostrarà "La connexió no és privada" amb NET::ERR_CERT_AUTHORITY_INVALID. Un client que veiés aquell avís no compraria. Per això els autosignats serveixen per a desenvolupament i proves internes, mai per a producció.

  1. cert-manager: quin problema resol i instal·lació

Amb certificats d'una autoritat pública, el procés manual és: generar clau i CSR, demostrar que controles el domini, rebre el certificat, crear el Secret, i repetir-ho cada 90 dies, que és el que duren els de Let's Encrypt. El resultat previsible és l'incident clàssic: un dissabte a la nit caduca el certificat, la botiga deixa de funcionar en ple pont, i ningú no recorda com es renovava perquè ho va fer una persona que ja no hi és.

cert-manager converteix això en un bucle de reconciliació com qualsevol altre: declares que vols un certificat i ell l'obté, el desa en un Secret i el renova abans que caduqui, indefinidament.

# Instal-lacio amb els CRD inclosos
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
kubectl get pods -n cert-manager
kubectl get crds | grep cert-manager.io
cert-manager-6d8b9c7f4d-jk29p             1/1   Running   0   60s
cert-manager-cainjector-5f9b7d8c6-w4xr2   1/1   Running   0   60s
cert-manager-webhook-7c4d9f8b5-mn6vt      1/1   Running   0   60s

certificaterequests.cert-manager.io    certificates.cert-manager.io
challenges.acme.cert-manager.io        clusterissuers.cert-manager.io
issuers.cert-manager.io                orders.acme.cert-manager.io

Tres components: cert-manager és el controlador que observa els Certificate i executa el flux d'emissió; cainjector injecta paquets de CA en webhooks i APIServices; i webhook valida i aplica valors per defecte als recursos propis.

Sis CRD (06-06): cert-manager estén l'API de Kubernetes amb tipus propis i un controlador que els reconcilia. És el patró operador de 06-07 en estat pur, i probablement l'exemple més útil que trobaràs.

  1. Els objectes de cert-manager i el flux d'emissió

Objecte Àmbit Qui el crea Què és
Issuer Namespace Tu Font de certificats vàlida només en aquell namespace
ClusterIssuer Clúster Tu (administrador) Font utilitzable des de qualsevol namespace
Certificate Namespace Tu, o l'Ingress amb l'anotació "Vull un certificat per a aquests dominis, en aquest Secret"
CertificateRequest / Order / Challenge Namespace cert-manager La petició amb el seu CSR, la comanda ACME i cada desafiament a superar

Els tres últims són interns: no s'escriuen a mà, però són on es llegeix què està fallant, seguint la cadena CertificateCertificateRequestOrderChallenge.

sequenceDiagram
    participant U as Tu (o l'Ingress)
    participant CM as cert-manager
    participant LE as Let's Encrypt (ACME)
    participant K8S as API de Kubernetes

    U->>K8S: crea Certificate (dominis + secretName)
    CM->>K8S: observa el Certificate
    CM->>CM: genera clau privada i CSR
    CM->>K8S: crea CertificateRequest
    CM->>K8S: crea Order
    CM->>LE: sol-licita la comanda per als dominis
    LE-->>CM: desafiaments pendents (HTTP-01 o DNS-01)
    CM->>K8S: crea Challenge
    CM->>K8S: publica la prova (Ingress temporal o registre TXT)
    CM->>LE: "ja pots validar"
    LE->>LE: comprova la prova des d'internet
    LE-->>CM: validat; aqui tens el certificat
    CM->>K8S: crea/actualitza el Secret kubernetes.io/tls
    CM->>K8S: Certificate amb Ready=True
    Note over CM: i programa la renovacio als 60 dies

  1. ACME amb Let's Encrypt: HTTP-01 davant de DNS-01

ACME (Automatic Certificate Management Environment) és el protocol que automatitza l'emissió. El seu nucli és una pregunta: demostres que controles el domini?

Desafiament HTTP-01

Let's Encrypt demana publicar un fitxer amb un contingut concret a http://<domini>/.well-known/acme-challenge/<token>; cert-manager crea un pod i un Ingress temporal per servir-lo, i l'esborra en acabar.

# k8s/base/clusterissuer-letsencrypt-http01.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-produccio
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]       # avisos de caducitat i problemes
    privateKeySecretRef:
      name: letsencrypt-produccio-compte     # clau del COMPTE ACME, no del certificat
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

Requisit imprescindible: el domini ha de resoldre al teu Ingress i ser accessible des d'internet al port 80. Conseqüències directes: no funciona a minikube amb dominis .example resolts per /etc/hosts (per això a dev fem servir autosignats); no funciona per a serveis interns sense exposició pública; no pot emetre certificats comodí; i si redirigeixes tot l'HTTP a HTTPS has d'excloure /.well-known/acme-challenge/ —els controladors moderns ho gestionen sols, però amb regles pròpies és una fallada habitual.

Desafiament DNS-01

Let's Encrypt demana un registre TXT a _acme-challenge.<domini>. cert-manager el crea fent servir l'API del proveïdor de DNS i l'esborra després.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-dns-compte
    solvers:
      - dns01:
          cloudflare:
            email: [email protected]
            apiTokenSecretRef:
              name: cloudflare-token       # Secret amb el token de l'API del DNS
              key: token
        selector:
          dnsZones:
            - rutasnorte.example           # aquest solver nomes per a aquesta zona
HTTP-01 DNS-01
Què es publica Un fitxer a /.well-known/acme-challenge/ Un registre TXT
Requereix port 80 públic No
Funciona amb serveis interns No
Certificats comodí No Sí, obligatori
Credencials necessàries Cap Token de l'API del DNS
Velocitat Segons Minuts (propagació del DNS)
Risc Cap d'especial Un token amb permís per editar la teva zona DNS

Regla pràctica: HTTP-01 per defecte, i DNS-01 quan necessitis comodins o el servei no sigui públic. Rutas Norte faria servir HTTP-01 per als seus dos dominis; si demà volgués *.rutasnorte.example per donar un subdomini a cada agència, hauria de passar a DNS-01.

  1. Emissió automàtica des de l'Ingress

Es pot crear un Certificate explícit, útil quan es volen controlar la durada, l'algorisme de clau o la rotació:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: rutasnorte-tls
  namespace: rutas-norte-pro
spec:
  secretName: rutasnorte-tls          # el Secret que es crearà
  duration: 2160h                     # 90 dies
  renewBefore: 720h                   # renovar 30 dies abans de caducar
  privateKey:
    algorithm: ECDSA
    size: 256
    rotationPolicy: Always            # clau nova a cada renovació
  dnsNames: [www.rutasnorte.example, api.rutasnorte.example]
  issuerRef:
    name: letsencrypt-produccio
    kind: ClusterIssuer

Però el còmode és l'anotació a l'Ingress: cert-manager la detecta i genera el Certificate per tu a partir del bloc tls:.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rutas-norte
  namespace: rutas-norte-pro
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-produccio"
    # per a un Issuer de namespace seria: cert-manager.io/issuer
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - www.rutasnorte.example
        - api.rutasnorte.example
      secretName: rutasnorte-tls      # cert-manager crearà aquest Secret
  rules:
    # ... les mateixes regles de 04-04

Tota la gestió de certificats de Rutas Norte es redueix a una anotació: els dominis surten de tls.hosts i la destinació, de tls.secretName.

kubectl apply -f k8s/entorns/pro/ingress-rutas-norte.yaml
kubectl get certificate,order,challenge -n rutas-norte-pro
certificate.cert-manager.io/rutasnorte-tls              False     rutasnorte-tls           12s
order.acme.cert-manager.io/rutasnorte-tls-1-2894732     pending                            11s
challenge.acme.cert-manager.io/rutasnorte-tls-1-...-0   pending   www.rutasnorte.example   10s

# ... uns segons despres
certificate.cert-manager.io/rutasnorte-tls              True      rutasnorte-tls           47s

READY: True i els Order/Challenge desapareguts: el certificat és al Secret i el controlador d'Ingress ja l'està servint.

  1. L'entorn de staging i els límits d'emissió

Let's Encrypt imposa límits estrictes: 50 certificats per domini registrat i setmana, 5 fallades de validació per compte, domini i hora, 5 duplicats exactes per setmana (mateix conjunt de dominis) i 100 noms per certificat.

El de duplicats és el que mossega: cinc intents amb el mateix conjunt de dominis i quedes bloquejat una setmana; depurant una configuració nova es consumeixen en deu minuts i no hi ha manera d'accelerar-ho. Per això existeix l'entorn de staging, amb els mateixos límits multiplicats per molt i certificats emesos per una CA de proves que els navegadors no reconeixen.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory   # l'unica diferencia
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-staging-compte
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

El procediment correcte, sense excepcions: (1) configurar l'Ingress amb cert-manager.io/cluster-issuer: letsencrypt-staging; (2) comprovar kubectl get certificate fins a READY: True; (3) verificar amb curl -k que el TLS funciona i que l'emissor és (STAGING) Let's Encrypt; i (4) només llavors passar a producció:

kubectl annotate ingress rutas-norte -n rutas-norte-pro \
  cert-manager.io/cluster-issuer=letsencrypt-produccio --overwrite
kubectl delete secret rutasnorte-tls -n rutas-norte-pro
kubectl get certificate -n rutas-norte-pro -w

Aquell kubectl delete secret és necessari: sense ell, cert-manager veu un certificat vàlid i no el reemplaça fins que toqui renovar.

  1. Renovació automàtica i comprovació de l'estat

cert-manager renova quan queda menys de renewBefore per caducar (per defecte un terç de la vida; amb 90 dies, als 60). El procés és idèntic al d'emissió i el Secret s'actualitza al seu lloc, sense canviar de nom. Cal reiniciar els pods? No. El controlador d'Ingress observa el Secret i recarrega sol. Si un pod muntés el certificat com a volum, el kubelet propaga el canvi en aproximadament un minut, encara que l'aplicació l'hauria de rellegir.

Comprovacions

kubectl get certificate -A
kubectl describe certificate rutasnorte-tls -n rutas-norte-pro
Status:
  Conditions:
    Type:      Ready
    Status:    True
    Message:   Certificate is up to date and has not expired
  Not Before:  2026-07-30T09:14:23Z
  Not After:   2026-10-28T09:14:22Z
  Renewal Time: 2026-09-28T09:14:22Z
Events:
  Normal  Issued  21d  cert-manager-certificates-issuing  The certificate has been successfully issued

Not After i Renewal Time són els dos camps que cal vigilar. I el certificat servit de debò:

echo | openssl s_client -connect www.rutasnorte.example:443 \
  -servername www.rutasnorte.example 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates
issuer=C=US, O=Let's Encrypt, CN=R11
subject=CN=www.rutasnorte.example
notBefore=Jul 30 09:14:23 2026 GMT
notAfter=Oct 28 09:14:22 2026 GMT

El -servername és imprescindible: sense SNI, el controlador retornaria el seu certificat per defecte i en veuries un d'equivocat.

Diagnòstic quan READY és False

Segueix la cadena cap avall fins al Challenge, amb kubectl describe sobre certificate, certificaterequest, order i challenge, i finalment kubectl logs -n cert-manager -l app=cert-manager.

Missatge al Challenge Causa
Waiting for HTTP-01 challenge propagation: wrong status code '404' L'Ingress temporal no és accessible, o el DNS públic no apunta al teu clúster
connection refused / timeout El port 80 no està obert des d'internet
NXDOMAIN El domini no existeix al DNS públic
too many certificates already issued Límit setmanal assolit. Espera o fes servir staging
Issuer not found Confusió entre Issuer i ClusterIssuer, o nom mal escrit

Aquell últim mereix atenció: cert-manager.io/issuer busca un Issuer al namespace de l'Ingress; cert-manager.io/cluster-issuer busca un ClusterIssuer global. Fer servir l'anotació equivocada dona un error que sembla de nom i és de tipus.

  1. Redirecció a HTTPS i HSTS

Publicar HTTPS no n'hi ha prou: cal impedir l'HTTP, perquè un client que escrigui el domini sense esquema anirà per HTTP. A ingress-nginx la redirecció està activada per defecte tan bon punt l'Ingress té bloc tls, i es controla amb les anotacions ssl-redirect (HTTP → HTTPS) i force-ssl-redirect (fins i tot darrere d'un altre proxy):

curl -sI http://www.rutasnorte.example/ | head -2
HTTP/1.1 308 Permanent Redirect
Location: https://www.rutasnorte.example/

Però la redirecció deixa una finestra: la primera petició viatja en clar i pot ser interceptada. HSTS (HTTP Strict Transport Security) la tanca: una capçalera que ordena al navegador fer servir HTTPS per a aquell domini durant un temps, sense preguntar i sense permetre saltar-se l'avís.

metadata:
  annotations:
    nginx.ingress.kubernetes.io/hsts: "true"
    nginx.ingress.kubernetes.io/hsts-max-age: "31536000"          # 1 any
    nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
    nginx.ingress.kubernetes.io/hsts-preload: "false"

Advertiments seriosos sobre HSTS, perquè és de les poques coses d'aquesta lliçó que et poden deixar fora del teu propi domini:

  • No es pot revocar ràpidament. Els navegadors recorden la directiva fins que expiri max-age. Si el teu certificat falla, els usuaris no poden entrar, ni acceptant el risc.
  • includeSubDomains afecta TOTS els subdominis, inclosos els que encara no tenen HTTPS: un admin.rutasnorte.example intern per HTTP deixaria de ser accessible.
  • preload és pràcticament irreversible: entrar a la llista precarregada dels navegadors és fàcil, sortir-ne triga mesos. Comença amb max-age baix (300) i puja'l progressivament.

I aquí toca recordar l'advertiment inicial: la política d'HSTS, les versions de TLS acceptades (ssl-protocols: "TLSv1.2 TLSv1.3") i els conjunts de xifratge (ssl-ciphers), que són ajustos globals del ConfigMap del controlador i no anotacions per Ingress, han de ser revisats per un professional de seguretat abans d'aplicar-se en producció.

  1. Més enllà: mTLS i malles de servei

El que hem muntat protegeix el tram client ↔ Ingress; dins del clúster, el trànsit entre el controlador i botiga-web, o entre api-reserves i postgres-reserves, continua en clar. Dos mecanismes van més lluny. mTLS (TLS mutu): a més del servidor, el client presenta certificat, de manera que api-reserves no només verifica amb qui parla sinó que demostra qui és; és la base de la identitat criptogràfica entre serveis i substitueix la confiança basada en adreces IP. I les malles de servei (Istio, Linkerd, Cilium Service Mesh), que injecten un proxy al costat de cada pod —o l'integren al kernel amb eBPF— i estableixen mTLS automàticament entre tots els components, amb certificats de curta vida i sense tocar el codi.

Per a Rutas Norte avui és exagerat: cinc components en un clúster propi, amb les polítiques de xarxa de 04-06 limitant qui pot parlar amb qui. Es replantejaria si la plataforma creixés a desenes de serveis o si un requisit normatiu exigís xifratge extrem a extrem. L'anàlisi completa és a 08-04.

Errors Comuns i Consells

  • Començar directament a producció de Let's Encrypt. Cinc fallades i una setmana bloquejat. Sempre staging primer.
  • Certificat sense subjectAltName. Els navegadors ignoren el CN. Sense SAN, el certificat és inútil.
  • Secret TLS en un altre namespace. No hi ha referències creuades: el Secret viu on viu l'Ingress.
  • Confondre Issuer amb ClusterIssuer a l'anotació. L'error diu "no trobat" i és de tipus.
  • Canviar l'issuer sense esborrar el Secret. cert-manager veu un certificat vàlid i no reemplaça res.
  • Redirigir a HTTPS bloquejant /.well-known/acme-challenge/. El desafiament HTTP-01 deixa de funcionar i la renovació falla en silenci fins que caduca.
  • Activar HSTS amb preload des del primer dia. Pràcticament irreversible. Puja max-age per etapes.
  • Intentar HTTP-01 a minikube amb dominis d'/etc/hosts, o demanar un comodí amb HTTP-01: el primer no és accessible des de Let's Encrypt i el segon és impossible per disseny (exigeix DNS-01).
  • Consell: vigila certmanager_certificate_expiration_timestamp_seconds a Prometheus (07-03) i alerta amb 21 dies de marge. L'automatització també falla.
  • Consell: un Certificate per domini en lloc d'un amb molts SAN limita el dany: una fallada de validació en un domini no impedeix renovar els altres.
  • Consell: desa el ClusterIssuer a Git, però mai el Secret amb la clau privada del compte ACME ni el token del DNS. Aplica allò de 03-02: Sealed Secrets, ESO o Vault.

Exercicis

Exercici 1: HTTPS en desenvolupament amb certificat autosignat

Genera amb openssl un certificat vàlid per a www.rutasnorte.example i api.rutasnorte.example, crea'l com a Secret TLS a rutas-norte-dev, afegeix-lo a l'Ingress i comprova que HTTPS funciona amb -k i falla sense. Explica exactament quina garantia falta.

Exercici 2: Instal·lar cert-manager i crear els dos ClusterIssuers

Instal·la cert-manager, crea letsencrypt-staging i letsencrypt-produccio amb desafiament HTTP-01, i anota l'Ingress de producció amb el de staging. Segueix la cadena CertificateOrderChallenge i explica per què a minikube el desafiament no es completarà.

Exercici 3: Diagnosticar un certificat que no s'emet

Un Certificate porta 20 minuts en READY: False. Dissenya el procediment de diagnòstic complet, indicant quina ordre fer servir a cada nivell i quin missatge esperaries per a tres causes diferents: domini que no resol des d'internet, port 80 tancat i límit d'emissió assolit.

Solucions

Exercici 1

openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout /tmp/dev.key -out /tmp/dev.crt \
  -subj "/C=ES/O=Rutas Norte S.L./CN=www.rutasnorte.example" \
  -addext "subjectAltName=DNS:www.rutasnorte.example,DNS:api.rutasnorte.example"

kubectl create secret tls rutasnorte-tls \
  --cert=/tmp/dev.crt --key=/tmp/dev.key -n rutas-norte-dev

kubectl patch ingress rutas-norte -n rutas-norte-dev --type=merge -p '
spec:
  tls:
    - hosts: ["www.rutasnorte.example","api.rutasnorte.example"]
      secretName: rutasnorte-tls'

curl -k -s -o /dev/null -w 'amb -k: %{http_code}\n' https://www.rutasnorte.example/
curl    -s -o /dev/null https://www.rutasnorte.example/ || echo "sense -k: fallada (esperat)"

El que falta és l'autenticació, no el xifratge. El canal està igual de xifrat, però ningú de confiança no avala que aquell certificat pertanyi a Rutas Norte: un atacant que segrestés el DNS podria presentar el seu propi autosignat i el client no notaria la diferència. La cadena de confiança és el que aporta una CA pública.

Exercici 2

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
kubectl wait --for=condition=Available deploy --all -n cert-manager --timeout=180s
kubectl apply -f k8s/base/clusterissuer-letsencrypt-staging.yaml
kubectl apply -f k8s/base/clusterissuer-letsencrypt-produccio.yaml
kubectl get clusterissuer      # tots dos han d'apareixer amb READY True

kubectl annotate ingress rutas-norte -n rutas-norte-pro \
  cert-manager.io/cluster-issuer=letsencrypt-staging --overwrite
kubectl describe challenge -n rutas-norte-pro | tail -12
Reason: Waiting for HTTP-01 challenge propagation: failed to perform self check
        GET request: Get "http://www.rutasnorte.example/.well-known/acme-challenge/xY3...":
        dial tcp: lookup www.rutasnorte.example: no such host

A minikube el domini només existeix al teu /etc/hosts. Els servidors de Let's Encrypt no el resolen ni poden arribar al teu clúster, així que HTTP-01 no es pot completar per disseny. És exactament el motiu pel qual en desenvolupament es fan servir certificats autosignats.

Exercici 3

NS=rutas-norte-pro
kubectl describe certificate rutasnorte-tls -n $NS | sed -n '/Status/,$p'  # nivell 1
kubectl describe certificaterequest -n $NS | grep -A3 "Conditions"         # nivell 2
kubectl describe order -n $NS | grep -A5 "Status"                          # nivell 3
kubectl describe challenge -n $NS | grep -A5 "Reason"      # nivell 4: el missatge util
kubectl logs -n cert-manager -l app=cert-manager --tail=80 | grep -i error  # nivell 5
Causa Missatge esperat On apareix
Domini que no resol dial tcp: lookup www.rutasnorte.example: no such host Challenge
Port 80 tancat connection refused o context deadline exceeded després del self check Challenge
Límit assolit 429 urn:ietf:params:acme:error:rateLimited: too many certificates already issued Order i logs de cert-manager

Per als dos primers s'arregla la infraestructura (DNS públic, tallafocs). Per al tercer no hi ha arreglament tècnic: s'espera que passi la setmana o es treballa en staging.

Conclusió

Rutas Norte ja xifra el seu trànsit. Saps per què HTTPS no és opcional en una plataforma que gestiona DNI, telèfons i pagaments, i que TLS aporta dues coses diferents: xifratge i identitat. Coneixes els tres models de terminació —a l'Ingress, rexifratge i passthrough— i per què la terminació a l'Ingress és l'elecció correcta aquí. Domines la peça bàsica, el Secret de tipus kubernetes.io/tls amb les seves claus tls.crt i tls.key, i les tres regles que el governen: mateix namespace que l'Ingress, coincidència entre els hosts del bloc tls i les SAN del certificat, i selecció per SNI quan n'hi ha diversos.

Has generat un certificat autosignat amb openssl per a desenvolupament, amb el detall decisiu del subjectAltName que els navegadors validen ignorant el CN, i has comprovat al teu propi terminal la diferència entre "xifrat" i "verificable": funciona amb -k, falla sense, i aquella diferència és la raó que els autosignats no arribin mai a producció.

Després has automatitzat el problema amb cert-manager, que és el patró operador aplicat a certificats: sis CRD, un controlador i un bucle de reconciliació. Saps què és un Issuer i en què es diferencia d'un ClusterIssuer, i com cert-manager encadena per sota CertificateCertificateRequestOrderChallenge, que és també l'ordre en què es diagnostica qualsevol fallada. Entens ACME i els seus dos desafiaments: HTTP-01, senzill però amb el requisit de ser accessible des d'internet al port 80 i sense poder emetre comodins, i DNS-01, imprescindible per a comodins i per a serveis interns a canvi de confiar un token de l'API del DNS. I has reduït tota la gestió a una anotació a l'Ingress, amb la disciplina irrenunciable de començar sempre en staging per no consumir els cinc intents setmanals de Let's Encrypt.

Saps comprovar l'estat amb kubectl get certificate i openssl s_client -servername, que la renovació passa 30 dies abans de caducar sense reiniciar res, i com tancar la porta a l'HTTP amb la redirecció i amb HSTS, mesurant-ne bé els riscos perquè no es revoca a voluntat. I saps què queda fora: el trànsit dins del clúster continua en clar, i això són mTLS i malles de servei, matèria de 08-04.

Ens queda un forat dels grans, el mateix que arrosseguem des del mòdul 3: qualsevol pod del clúster es pot connectar a postgres-reserves:5432. Un pod compromès, una imatge amb una dependència maliciosa o un simple error de desplegament a rutas-norte-dev tenen accés directe a la base de dades amb les dades personals de tots els clients. A 04-06, l'última lliçó del mòdul, construirem el model d'aïllament de la plataforma: denegar-ho tot per defecte, obrir el DNS —la fallada clàssica que tomba el clúster sencer—, i autoritzar una a una únicament les converses que Rutas Norte necessita.

Curs de Kubernetes

Mòdul 1: Introducció a Kubernetes

Mòdul 2: Components Principals de Kubernetes

Mòdul 3: Gestió de Configuració i Secrets

Mòdul 4: Xarxes a Kubernetes

Mòdul 5: Emmagatzematge a Kubernetes

Mòdul 6: Conceptes Avançats de Kubernetes

Mòdul 7: Monitoratge i Registre

Mòdul 8: Seguretat a Kubernetes

Mòdul 9: Escalat i Rendiment

Mòdul 10: Ecosistema i Eines de Kubernetes

Mòdul 11: Estudis de Cas i Aplicacions del Món Real

Mòdul 12: Preparació per a la Certificació de Kubernetes

© Copyright 2026. Tots els drets reservats