La lliçó anterior va treure la configuració de botiga-web de la imatge i la va posar en un ConfigMap, però acabava amb un advertiment: un ConfigMap no protegeix res. Qualsevol que pugui llistar ConfigMaps a rutas-norte-pro en llegeix el contingut íntegre en text pla. I el deute que arrosseguem des del mòdul 2 és precisament dels que no admeten això: la contrasenya de postgres-reserves està escrita en clar en un manifest versionat a Git, i aquesta base de dades desa el nom, el DNI, el telèfon i el correu de cada client de Rutas Norte. Aquesta lliçó el salda amb l'objecte Secret, però sobretot t'ensenya a no refiar-te'n més del que mereix: veuràs per què base64 no és xifratge, per què el xifratge en repòs d'etcd no ve activat per defecte, qui pot llegir un secret sense que te n'assabentis, i què fa servir l'ecosistema quan el Secret natiu es queda curt.
Advertiment important. Aquesta lliçó explica els mecanismes que Kubernetes ofereix per gestionar credencials i et dona un punt de partida sòlid, però no substitueix un disseny de seguretat professional.
postgres-reservesemmagatzema dades personals de clients reals (nom, DNI, telèfon, correu), cosa que a la Unió Europea entra de ple en l'àmbit del RGPD i, a Espanya, de la LOPDGDD. El disseny concret de la gestió de credencials, el xifratge en repòs, la política de rotació, els registres d'auditoria i les mesures de seguretat aplicables l'ha de revisar i aprovar un professional de seguretat o el responsable de compliment normatiu de la teva organització. Tracta tot el que segueix com a coneixement tècnic necessari, no com una recomanació de compliment.
Contingut
- El deute del mòdul 2 i per què fa mal
- Què és un Secret i en què es diferencia d'un ConfigMap
- Base64 no és xifratge: la demostració
- Els tipus de Secret
- Crear Secrets: imperatiu i declaratiu amb
stringData - Consumir un Secret com a volum
- El secret
postgres-reserves-credencialsa Rutas Norte imagePullSecretsper al registre privat- Xifratge en repòs a etcd
- Qui pot llegir un secret
- Rotació de credencials
- Què no s'ha de fer mai
- Les solucions reals de l'ecosistema
- El deute del mòdul 2 i per què fa mal
Aquest és el manifest que tenim avui al repositori de Rutas Norte:
# k8s/base/postgres-reserves/deployment.yaml <-- AIXO ESTA MALAMENT
spec:
containers:
- name: postgres
image: postgres:16.4
env:
- name: POSTGRES_PASSWORD
value: "R3s3rv3s2026!" # en clar, a Git, per sempre
- name: POSTGRES_USER
value: "rutasnorte"
- name: POSTGRES_DB
value: "reserves"El problema no és només estètic. Enumerem el dany real:
- Git no oblida. Encara que esborris la línia avui i facis commit, la contrasenya continua a l'historial. Treure-la d'allà exigeix reescriure l'historial del repositori (
git filter-repo) i forçar el push a tots els clons. A la pràctica, una credencial que ha estat a Git es considera compromesa i cal rotar-la, no esborrar-la. - Es propaga sense control. Cada desenvolupador que va clonar el repositori té la contrasenya de producció al seu portàtil. També la tenen el CI, el sistema de còpies del repositori, el cercador de codi intern i qualsevol anàlisi estàtica que desi memòria cau.
- No hi ha traçabilitat. No hi ha manera de saber qui ha llegit aquesta contrasenya ni quan.
- Rotar-la és un desplegament de codi. Canviar la contrasenya exigeix un commit, una revisió, un merge i un desplegament. Això empeny a no rotar-la mai.
- Bots automatitzats la trobaran. Si el repositori arriba a ser públic un sol minut, hi ha rastrejadors que localitzen credencials en segons.
L'objectiu d'aquesta lliçó és que aquest manifest quedi així:
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-reserves-credencials
key: passwordEl manifest es pot publicar sense filtrar res. Aquest és el llistó dels dotze factors que vèiem a la lliçó anterior.
- Què és un Secret i en què es diferencia d'un ConfigMap
Un Secret és, estructuralment, gairebé idèntic a un ConfigMap: metadata més dades, sense spec ni status, amb namespace, i amb el mateix límit d'1 MiB. La diferència és en el tractament que Kubernetes li dona i en les expectatives que crea.
| Aspecte | ConfigMap | Secret |
|---|---|---|
apiVersion |
v1 |
v1 |
| Camp de dades | data (text), binaryData |
data (base64), stringData (només escriptura) |
Codificació de data |
Text pla UTF-8 | Base64 obligatori |
Camp type |
No existeix | Sí, i determina validacions |
| En muntar-se com a volum | Escrit al disc del node | Escrit en tmpfs (memòria RAM) |
Apareix a kubectl describe |
El contingut complet | Només la mida en bytes de cada clau |
| Xifratge en repòs a etcd | No | Només si es configura (no per defecte) |
| Pot ser immutable | Sí | Sí |
| Límit de mida | 1 MiB | 1 MiB |
Pot portar imagePullSecrets |
No | Sí |
| Es pot restringir amb RBAC | Sí | Sí, i és imprescindible |
Les tres diferències que de debò importen:
Primera: stringData. És un camp només d'escriptura. Hi escrius text pla i l'apiserver el codifica en base64 i el mou a data abans de desar-lo. És el que fa tolerable escriure Secrets a mà.
Segona: el muntatge en tmpfs. Quan un Secret es munta com a volum, el kubelet crea un sistema de fitxers a memòria RAM, no al disc del node. Conseqüència pràctica: si el node s'apaga bruscament, la credencial no queda escrita en cap disc recuperable. És una protecció real, encara que limitada.
Tercera: describe no mostra el contingut.
Name: postgres-reserves-credencials
Namespace: rutas-norte-dev
Labels: app=postgres-reserves
app.kubernetes.io/part-of=rutas-norte
entorn=dev
Annotations: <none>
Type: Opaque
Data
====
password: 13 bytes
username: 10 bytes
database: 8 bytesNomés les mides. Això evita l'accident clàssic de mostrar la contrasenya en una sessió compartida o deixar-la al log d'una consola. Però és un detall de presentació, no un control de seguretat, com demostra l'apartat següent.
- Base64 no és xifratge: la demostració
Aquesta és la idea més important de la lliçó i cal interioritzar-la fent-la.
Creem el Secret:
kubectl create secret generic postgres-reserves-credencials \
--from-literal=username=rutasnorte \
--from-literal=password='R3s3rv3s2026!' \
--from-literal=database=reserves \
-n rutas-norte-devEl mirem en YAML:
apiVersion: v1
data:
database: cmVzZXJ2ZXM=
password: UjNzM3J2M3MyMDI2IQ==
username: cnV0YXNub3J0ZQ==
kind: Secret
metadata:
creationTimestamp: "2026-08-05T11:02:47Z"
name: postgres-reserves-credencials
namespace: rutas-norte-dev
resourceVersion: "191455"
uid: 8a3f7d21-5c6b-4e09-b1d7-4f2a9c8e3105
type: OpaqueA primera vista sembla xifrat. No ho és. Base64 és una codificació reversible sense clau, dissenyada als anys 80 per transportar binaris per canals de text. Qualsevol la pot desfer:
kubectl get secret postgres-reserves-credencials -n rutas-norte-dev \
-o jsonpath='{.data.password}' | base64 -d; echoTres segons i una ordre. Ni contrasenya, ni clau, ni permisos especials més enllà de poder llegir el Secret.
Podem treure tot el contingut de cop:
kubectl get secret postgres-reserves-credencials -n rutas-norte-dev \
-o go-template='{{range $k, $v := .data}}{{$k}}={{$v | base64decode}}{{"\n"}}{{end}}'O, des de Kubernetes 1.30, amb el visor incorporat:
kubectl get secret postgres-reserves-credencials -n rutas-norte-dev -o jsonpath='{.data}' \
| jq -r 'to_entries[] | "\(.key)=\(.value|@base64d)"'Què implica això exactament:
| Creença falsa | Realitat |
|---|---|
| "Està codificat, és segur" | Base64 es desfà sense clau, en una ordre |
| "Com que és en un Secret, el puc pujar a Git" | No. És equivalent a pujar-lo en clar |
| "Només els administradors ho veuen" | Ho veu qui tingui permís get secrets al namespace |
| "A etcd està xifrat" | Per defecte no (apartat 9) |
| "Els logs no ho mostren" | Un kubectl get secret -o yaml en un pipeline sí |
Aleshores, per a què serveix base64? Per a dues coses legítimes i cap d'elles és seguretat:
- Permetre contingut binari. Un certificat en format DER o una clau privada amb bytes no imprimibles no cap en un camp de text YAML. Base64 ho fa possible.
- Evitar problemes d'escapament. Una contrasenya amb cometes, salts de línia o
$trencaria el YAML. Codificada, no.
La regla que has de memoritzar: un Secret de Kubernetes no protegeix la dada; el que fa és donar-li un lloc on aplicar controls (RBAC, xifratge en repòs, auditoria, muntatge en memòria) i separar-la del manifest de l'aplicació. La protecció l'aporten aquests controls, no l'objecte.
- Els tipus de Secret
El camp type no és decoratiu: determina quines claus exigeix Kubernetes i quins components saben fer servir el Secret.
type |
Claus obligatòries | Per a què serveix | Ordre de creació |
|---|---|---|---|
Opaque |
Cap | Ús general: contrasenyes, tokens d'API, claus de xifratge | kubectl create secret generic |
kubernetes.io/dockerconfigjson |
.dockerconfigjson |
Credencials d'un registre privat d'imatges | kubectl create secret docker-registry |
kubernetes.io/tls |
tls.crt, tls.key |
Certificat i clau privada per a TLS (Ingress) | kubectl create secret tls |
kubernetes.io/basic-auth |
username, password |
Autenticació bàsica HTTP | generic amb --type |
kubernetes.io/ssh-auth |
ssh-privatekey |
Clau SSH (clonar repositoris privats) | generic amb --type |
kubernetes.io/service-account-token |
token, ca.crt, namespace |
Token permanent d'una ServiceAccount | Manifest amb anotació |
bootstrap.kubernetes.io/token |
token-id, token-secret |
Unir nodes nous al clúster (kubeadm) | Manifest |
Notes d'ús a Rutas Norte:
Opaqueés el que farem servir per apostgres-reserves-credencials, per a la clau de la passarel·la de pagament i per al token del proveïdor de correu deworker-notificacions. És el tipus per defecte si no hi poses res.kubernetes.io/dockerconfigjsonserà el que permeti descarregar imatges deregistry.rutasnorte.example(apartat 8).kubernetes.io/tlsserà el que sostingui el certificat dewww.rutasnorte.example. El veuràs a TLS i Certificats, on cert-manager el generarà i renovarà tot sol.kubernetes.io/service-account-token: compte amb aquest. Abans de Kubernetes 1.24, cada ServiceAccount tenia associat un Secret d'aquest tipus amb un token sense caducitat. Ja no es creen automàticament i crear-los a mà és una mala idea llevat de casos molt concrets: un token etern és un token que mai no es pot revocar de manera neta. El mecanisme actual el veuràs a ServiceAccounts.
El tipus també es valida. Això falla:
kubectl create secret generic tipus-incorrecte --type=kubernetes.io/basic-auth \
--from-literal=usuari=rutasnorte -n rutas-norte-devEl tipus basic-auth exigeix exactament la clau username, no usuari. Aquesta validació és útil: evita que un Ingress o un controlador rebi un Secret amb la forma equivocada.
- Crear Secrets: imperatiu i declaratiu amb
stringData
stringData5.1. kubectl create secret generic
kubectl create secret generic postgres-reserves-credencials \
--from-literal=username=rutasnorte \
--from-literal=password='R3s3rv3s2026!' \
-n rutas-norte-devCompte amb l'historial del shell. Aquesta ordre queda escrita a ~/.bash_history amb la contrasenya a dins. Dues maneres d'evitar-ho:
# Opcio A: un espai al davant (amb HISTCONTROL=ignorespace)
kubectl create secret generic ... --from-literal=password='R3s3rv3s2026!'
# Opcio B (millor): des de fitxer, i esborrar el fitxer despres
printf '%s' 'R3s3rv3s2026!' > /tmp/pw
kubectl create secret generic postgres-reserves-credencials \
--from-file=password=/tmp/pw -n rutas-norte-dev
shred -u /tmp/pwEl printf '%s' en lloc d'echo és deliberat: echo afegeix un salt de línia final que acabaria dins de la contrasenya. És un error clàssic i desconcertant: la contrasenya "sembla correcta" però PostgreSQL la rebutja. Si fas servir echo, posa-hi -n.
5.2. kubectl create secret docker-registry
kubectl create secret docker-registry registry-rutasnorte \
--docker-server=registry.rutasnorte.example \
--docker-username=desplegaments \
--docker-password='<token-de-desplegament>' \
[email protected] \
-n rutas-norte-pro5.3. kubectl create secret tls
kubectl create secret tls botiga-web-tls \
--cert=certs/www.rutasnorte.example.crt \
--key=certs/www.rutasnorte.example.key \
-n rutas-norte-pro5.4. Manifest amb stringData
Si escrius el manifest a mà, fes servir sempre stringData, mai data:
apiVersion: v1
kind: Secret
metadata:
name: postgres-reserves-credencials
namespace: rutas-norte-dev
labels:
app: postgres-reserves
app.kubernetes.io/name: postgres-reserves
app.kubernetes.io/component: base-dades
app.kubernetes.io/part-of: rutas-norte
entorn: dev
type: Opaque
stringData: # text pla; l'apiserver el codifica en desar-lo
username: rutasnorte
password: "R3s3rv3s2026!"
database: reservesAvantatges de stringData davant de data:
- S'escriu i es llegeix sense codificar ni descodificar res.
- Elimina l'error de codificar amb
echosense-n, que fica un\nal valor. - Un
diffen una pull request és llegible... cosa que, atenció, també és el motiu pel qual aquest fitxer no pot anar a Git tal qual.
Si una clau apareix a stringData i a data, guanya stringData.
I aquí arriba la pregunta òbvia: si aquest fitxer no pot anar a Git, on viu? Tres respostes legítimes, en ordre de maduresa:
- Només al clúster. El fitxer s'aplica una vegada des d'un lloc segur i es destrueix. Simple, però es perd la traçabilitat i cal documentar a part quines claus existeixen.
- A Git, xifrat. Sealed Secrets o SOPS permeten versionar un fitxer que només el clúster pot desxifrar (apartat 13).
- Fora de Kubernetes. Un gestor de secrets extern (Vault, AWS Secrets Manager) és la font de veritat i un operador el sincronitza (apartat 13).
Mentrestant, el mínim imprescindible al repositori de Rutas Norte:
I un secret-postgres.yaml.exemple amb valors ficticis, versionat, perquè l'equip sàpiga quines claus calen.
- Consumir un Secret com a volum
El mecanisme és idèntic al dels ConfigMaps de la lliçó anterior, canviant configMap per secret i name per secretName:
spec:
containers:
- name: api
image: registry.rutasnorte.example/api-reserves:2.5.0
volumeMounts:
- name: credencials-bd
mountPath: /etc/secrets/postgres # ruta propia, no un directori del sistema
readOnly: true
volumes:
- name: credencials-bd
secret:
secretName: postgres-reserves-credencials
defaultMode: 0400 # nomes el propietari llegeix
items: # opcional: nomes aquestes claus
- key: password
path: passwordDins del contenidor:
kubectl exec -n rutas-norte-dev deploy/api-reserves -- ls -la /etc/secrets/postgres
kubectl exec -n rutas-norte-dev deploy/api-reserves -- mount | grep secretstotal 0
drwxrwxrwt 3 root root 100 Aug 5 11:31 .
drwxr-xr-x 3 root root 4096 Aug 5 11:31 ..
lrwxrwxrwx 1 root root 15 Aug 5 11:31 password -> ..data/password
tmpfs on /etc/secrets/postgres type tmpfs (ro,relatime,size=...)Dues coses a destacar:
type tmpfs: el volum és a memòria RAM. No toca mai el disc del node. En morir el pod, desapareix.- La mateixa cadena d'enllaços a
..dataque als ConfigMaps, amb la mateixa conseqüència: si el Secret canvia, el fitxer muntat s'actualitza (amb el mateix retard del kubelet, i amb la mateixa excepció desubPath, que no propaga).
Aquesta capacitat de refresc és la raó principal per preferir el volum davant de la variable d'entorn quan es tracta de credencials. Una aplicació ben escrita pot rellegir el fitxer i adoptar una contrasenya rotada sense reiniciar-se. Hi tornarem a l'apartat 11.
El consum com a variable d'entorn (secretKeyRef, envFrom amb secretRef) és igual de comú i sovint més pràctic, sobretot amb imatges de tercers com postgres:16, que esperen POSTGRES_PASSWORD a l'entorn. El veuràs amb tot el detall a Variables d'Entorn. N'avanço la comparació, perquè condiciona el disseny:
| Volum | Variable d'entorn | |
|---|---|---|
| Es refresca en rotar | Sí | No: exigeix recrear el pod |
Visible a kubectl describe pod |
No | La referència sí, el valor no |
Visible a /proc/<pid>/environ |
No | Sí, per a qualsevol procés del contenidor |
| Pot filtrar-se en un bolcat d'error | Poc probable | Sí: molts frameworks bolquen l'entorn |
| Compatible amb imatges de tercers | De vegades (*_FILE) |
Gairebé sempre |
Moltes imatges serioses admeten la variant _FILE: POSTGRES_PASSWORD_FILE=/etc/secrets/postgres/password. Quan existeixi, prefereix-la: combina la compatibilitat de la variable amb la seguretat del fitxer.
- El secret
postgres-reserves-credencials a Rutas Norte
postgres-reserves-credencials a Rutas NorteAnem a saldar el deute de debò. Disseny complet:
flowchart LR
S["Secret<br/>postgres-reserves-credencials<br/>type: Opaque"]
S -->|"env: POSTGRES_PASSWORD"| PG["postgres-reserves<br/>(crea l'usuari en inicialitzar-se)"]
S -->|"volum /etc/secrets/postgres"| API["api-reserves<br/>(es connecta a la BD)"]
S -->|"volum /etc/secrets/postgres"| W["worker-notificacions<br/>(llegeix reserves pendents)"]
S2["Secret<br/>smtp-notificacions"] -->|"env: SMTP_PASSWORD"| W
RBAC["RBAC (08-01)<br/>nomes aquestes SA llegeixen el Secret"] -.controla.-> S
El Secret, a k8s/entorns/dev/secret-postgres.yaml (fora de Git):
apiVersion: v1
kind: Secret
metadata:
name: postgres-reserves-credencials
namespace: rutas-norte-dev
labels:
app: postgres-reserves
app.kubernetes.io/name: postgres-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: dev
type: Opaque
stringData:
username: rutasnorte
password: "d3v-C4nvi4m3-2026"
database: reserves
# URL completa: comoda per a l'app, pero duplica la contrasenya.
# Si l'app la pot compondre, millor no tenir-la aqui.
url: "postgresql://rutasnorte:d3v-C4nvi4m3-2026@postgres-reserves:5432/reserves"El Deployment de postgres-reserves, ja sense contrasenyes en clar:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-reserves
namespace: rutas-norte-dev
labels:
app: postgres-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
replicas: 1
selector:
matchLabels:
app: postgres-reserves
entorn: dev
strategy:
type: Recreate # una BD no admet dos pods sobre el mateix disc
template:
metadata:
labels:
app: postgres-reserves
app.kubernetes.io/name: postgres-reserves
app.kubernetes.io/component: base-dades
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
containers:
- name: postgres
image: postgres:16.4
ports:
- name: postgres
containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: postgres-reserves-credencials
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-reserves-credencials
key: password
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: postgres-reserves-credencials
key: database
- name: PGDATA
value: /var/lib/postgresql/data/pgdataI api-reserves, que la consumeix com a fitxer:
- name: api
image: registry.rutasnorte.example/api-reserves:2.5.0
env:
- name: DB_HOST
value: postgres-reserves # el Service del modul 2
- name: DB_PORT
value: "5432"
- name: DB_PASSWORD_FILE
value: /etc/secrets/postgres/password
volumeMounts:
- name: credencials-bd
mountPath: /etc/secrets/postgres
readOnly: true
volumes:
- name: credencials-bd
secret:
secretName: postgres-reserves-credencials
defaultMode: 0400
items:
- key: password
path: password
- key: username
path: usernameFixa't en el detall de disseny: api-reserves rep només username i password gràcies a items. No rep la clau url, que conté la contrasenya duplicada, ni cap altra clau que es pugui afegir al Secret en el futur. És el principi de mínim privilegi aplicat al contingut d'un objecte.
Verificació que el deute està saldat:
grep -r "R3s3rv3s2026" k8s/ ; echo "sortida: $?"
kubectl exec -n rutas-norte-dev deploy/api-reserves -- cat /etc/secrets/postgres/password; echogrep no troba res al repositori i l'aplicació té la seva credencial. Aquest és l'objectiu.
imagePullSecrets per al registre privat
imagePullSecrets per al registre privatLes imatges de Rutas Norte viuen a registry.rutasnorte.example, que és privat. Sense credencials, el pod falla així:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulling 45s kubelet Pulling image "registry.rutasnorte.example/api-reserves:2.5.0"
Warning Failed 43s kubelet Failed to pull image: rpc error: code = Unknown
desc = failed to resolve reference: unexpected status from HEAD request: 401 Unauthorized
Warning Failed 43s kubelet Error: ErrImagePull
Normal BackOff 18s (x3 over 42s) kubelet Back-off pulling image
Warning Failed 18s kubelet Error: ImagePullBackOffImagePullBackOff amb un 401 és sempre el mateix: falten credencials de registre.
Es crea el Secret del tipus adequat:
kubectl create secret docker-registry registry-rutasnorte \
--docker-server=registry.rutasnorte.example \
--docker-username=desplegaments \
--docker-password='<token-de-desplegament-de-nomes-lectura>' \
-n rutas-norte-proEl que desa a dins és un fitxer .dockerconfigjson:
kubectl get secret registry-rutasnorte -n rutas-norte-pro \
-o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .{
"auths": {
"registry.rutasnorte.example": {
"username": "desplegaments",
"password": "<token-de-desplegament-de-nomes-lectura>",
"auth": "ZGVzcGxlZ2FtZW50czo8dG9rZW4uLi4+"
}
}
}Una altra demostració que base64 no protegeix res: el camp auth és simplement usuari:contrasenya codificat.
I es referencia al pod:
spec:
imagePullSecrets:
- name: registry-rutasnorte # a nivell de POD, no de contenidor
containers:
- name: api
image: registry.rutasnorte.example/api-reserves:2.5.0Tres detalls importants:
imagePullSecretsva aspecdel pod, no dins decontainers. Aplica a totes les imatges del pod, inclosos els init containers.- Se'n poden llistar diversos, un per registre. El kubelet tria el que casa amb el servidor de la imatge.
- Cal repetir-ho a cada pod del namespace, cosa que és tediosa i fàcil d'oblidar. La solució elegant és declarar-ho a la ServiceAccount: tots els pods que la facin servir l'heretaran sense escriure'l. Ho veuràs a ServiceAccounts.
Bona pràctica de seguretat: el token del registre ha de ser de només lectura d'imatges (pull), mai un amb permís de pujada. Si el clúster es veu compromès, un token de pull limita el dany a poder descarregar imatges; un de push permetria substituir-les per versions malicioses. La lliçó de Seguretat d'Imatges hi aprofundeix.
- Xifratge en repòs a etcd
Arribem al punt que més gent desconeix.
Per defecte, els Secrets es desen a etcd sense xifrar. Codificats en base64, sí; xifrats, no. Qui tingui accés al disc d'un node del pla de control, a una còpia de seguretat d'etcd o a un bolcat del sistema de fitxers, té totes les credencials del clúster.
Demostració (en un clúster on tinguis accés al pla de control):
sudo ETCDCTL_API=3 etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/rutas-norte-dev/postgres-reserves-credencials | hexdump -C | head -800000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 72 75 74 61 73 2d 6e 6f 72 74 65 2d 64 65 |s/rutas-norte-de|
00000020 76 2f 70 6f 73 74 67 72 65 73 0a 6b 38 73 00 0a |v/postgres.k8s..|
...
000000a0 64 33 76 2d 43 34 6e 76 69 34 6d 33 2d 32 30 32 |d3v-C4nvi4m3-202|
000000b0 36 12 0a 72 75 74 61 73 6e 6f 72 74 65 |6..rutasnorte|La contrasenya es llegeix directament al bolcat. Ni tan sols cal descodificar base64: etcd desa l'objecte serialitzat amb els valors ja en binari.
La solució és una EncryptionConfiguration a l'apiserver:
# /etc/kubernetes/enc/encryption-config.yaml (nomes als nodes del pla de control)
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets # quins objectes es xifren
- configmaps # opcional, te cost
providers:
- aescbc: # el PRIMER es fa servir per ESCRIURE
keys:
- name: clau-2026-08
secret: <32 bytes aleatoris en base64>
- identity: {} # sense xifrar: necessari per LLEGIR el que hi haviaI s'activa arrencant l'apiserver amb --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml.
Els proveïdors disponibles:
| Proveïdor | Xifratge | Velocitat | Fortalesa | Comentari |
|---|---|---|---|---|
identity |
Cap | — | Nul·la | El comportament per defecte |
secretbox |
XSalsa20 + Poly1305 | Molt ràpida | Forta | Bona opció si no hi ha KMS |
aescbc |
AES-CBC 32 bytes | Ràpida | Acceptable | Vulnerable a padding oracle en teoria |
aesgcm |
AES-GCM | Molt ràpida | Forta | Exigeix rotar la clau amb freqüència |
kms v2 |
Delegat en un KMS extern | Ràpida | La millor | La clau mai no és al disc del node |
Quatre regles crítiques:
- L'ordre importa. El primer proveïdor de la llista és el que xifra en escriure. Tots els de la llista es proven en llegir. Per això
identityha d'anar al final durant la migració: permet llegir els Secrets que ja existien sense xifrar. - Activar-ho no xifra el que ja existeix. Només es xifra el que s'escriu a partir d'aquell moment. Cal forçar una reescriptura de tot:
- La clau del fitxer és la joia de la corona. Si fas servir
aescbcosecretbox, la clau és en text pla al disc del node del pla de control. Qui tingui aquest fitxer i una còpia d'etcd ho té tot. Per aixòkmsv2 és l'única opció realment sòlida en producció: la clau de xifratge de dades s'embolcalla amb una clau que viu en un HSM o al KMS del núvol i mai no s'escriu al node. - A Kubernetes gestionat (EKS, AKS, GKE) això s'activa amb una casella, delegant en el KMS del proveïdor. És una de les raons de pes per fer servir un clúster gestionat, com veuràs a 10-06.
Per a Rutas Norte, que desa DNI i dades de contacte de clients, el xifratge en repòs amb KMS no és opcional: és una mesura tècnica de les que el RGPD espera veure documentada. I un cop més: la decisió concreta l'ha de validar el responsable de compliment.
- Qui pot llegir un secret
Un Secret no té cap protecció pròpia. Tot depèn de RBAC, que s'estudia a fons a Control d'Accés Basat en Rols. Aquí n'hi ha prou d'entendre per què és inseparable.
Qui pot llegir postgres-reserves-credencials avui:
| Qui | Com | Com es restringeix |
|---|---|---|
Qualsevol amb get secrets al namespace |
kubectl get secret -o yaml |
RBAC: no concedir aquest verb |
Qualsevol amb list secrets al namespace |
kubectl get secrets -o yaml (llista els continguts!) |
RBAC: list és tan perillós com get |
| Qualsevol que pugui crear un pod al namespace | Munta el Secret en un pod seu i el llegeix | RBAC sobre create pods |
Qualsevol amb exec en un pod que el munti |
kubectl exec ... -- cat /etc/secrets/... |
RBAC sobre pods/exec |
| El kubelet dels nodes on corren aquests pods | Necessita el Secret per muntar-lo | NodeRestriction limita als dels seus pods |
| Qui tingui accés a etcd o a les seves còpies | Bolcat directe | Xifratge en repòs + seguretat del node |
| Un administrador del clúster | Tot | Auditoria |
Les dues entrades que sorprenen i cal subratllar:
list filtra els continguts. Molta gent concedeix list secrets pensant que només permet veure els noms. No és així: kubectl get secrets -o yaml amb permís de list retorna els objectes complets, amb les seves dades. A RBAC, list sobre secrets és equivalent a get.
Poder crear pods equival a poder llegir tots els secrets del namespace. Qui pugui desplegar un pod pot muntar qualsevol Secret del namespace i bolcar-lo. No hi ha manera d'evitar-ho amb RBAC sobre secrets: cal restringir la creació de pods. És la raó principal per la qual el namespace és la unitat real d'aïllament de credencials i per la qual Rutas Norte separa dev, pre i pro: ningú amb accés de desenvolupament no ha de poder crear pods a rutas-norte-pro.
Un bon exercici d'auditoria, que anticipa el mòdul 8:
kubectl auth can-i get secrets -n rutas-norte-pro
kubectl auth can-i list secrets -n rutas-norte-pro --as=system:serviceaccount:rutas-norte-pro:default
- Rotació de credencials
Rotar una credencial és canviar-la de manera periòdica i, obligatòriament, quan se sospita que s'ha filtrat. I aquí apareix un problema que Kubernetes no resol tot sol.
El problema: un pod que va arrencar ahir té la contrasenya vella. Si la canvies al Secret:
- Si la consumeix com a volum: el fitxer s'actualitza en un parell de minuts. Però l'aplicació només se n'assabentarà si rellegeix el fitxer. La majoria llegeix en arrencar i manté la connexió oberta.
- Si la consumeix com a variable d'entorn: no s'actualitza mai. Les variables d'entorn d'un procés es fixen en executar-lo i no hi ha manera de canviar-les des de fora.
La seqüència correcta de rotació, amb doble credencial vàlida, que és el que evita el tall de servei:
sequenceDiagram
participant OP as Operador
participant PG as postgres-reserves
participant K8S as Secret
participant API as pods d'api-reserves
OP->>PG: 1. Crear la contrasenya NOVA (la vella segueix valida)
OP->>K8S: 2. Actualitzar el Secret amb la nova
Note over API: 3. Els pods vells segueixen amb la vella: SEGUEIXEN FUNCIONANT
OP->>API: 4. kubectl rollout restart (progressiu, sense tall)
Note over API: 5. Els pods nous arrenquen amb la nova
OP->>PG: 6. Revocar la contrasenya VELLA
OP->>PG: 7. Verificar als logs que ningu no la fa servir
En ordres:
# 1. A PostgreSQL, crear la credencial nova sense treure la vella
kubectl exec -n rutas-norte-pro deploy/postgres-reserves -- \
psql -U postgres -c "ALTER USER rutasnorte PASSWORD 'nova-2026-08';"
# 2. Actualitzar el Secret
kubectl create secret generic postgres-reserves-credencials \
--from-literal=username=rutasnorte \
--from-literal=password='nova-2026-08' \
--from-literal=database=reserves \
-n rutas-norte-pro --dry-run=client -o yaml | kubectl apply -f -
# 3. Reiniciar de manera progressiva els consumidors
kubectl rollout restart deploy/api-reserves -n rutas-norte-pro
kubectl rollout restart deploy/worker-notificacions -n rutas-norte-pro
kubectl rollout status deploy/api-reserves -n rutas-norte-prosecret/postgres-reserves-credencials configured
deployment.apps/api-reserves restarted
deployment.apps/worker-notificacions restarted
deployment "api-reserves" successfully rolled outUn avís sobre ALTER USER: a PostgreSQL un usuari té una sola contrasenya, així que el pas 1 la substitueix i els pods vells es trencaran tan bon punt obrin una connexió nova. En un sistema amb rotació seriosa es fan servir dos usuaris (rutasnorte_a i rutasnorte_b) i s'alterna entre ells, que és el que fan els motors de secrets dinàmics de Vault. Dissenya-ho abans de necessitar-ho.
Punts que convé fixar:
- Automatitza la detecció del canvi. La tècnica del hash del Secret en una anotació del
template, que veuràs a 03-03, fa que actualitzar el Secret dispari el desplegament tot sol, senserollout restartmanual. - Rota sempre després d'una exposició. Si la contrasenya va estar a Git, rotar-la és obligatori encara que hagis reescrit l'historial.
- Vigila els pods resseguits. Un CronJob o un pod que no forma part d'un Deployment no es reinicia amb
rollout restart. Fes-te una llista de consumidors abans de rotar.
- Què no s'ha de fer mai
| Pràctica | Per què és greu |
|---|---|
| Pujar el Secret a Git, encara que sigui en base64 | Base64 no és xifratge. Equival a pujar-lo en clar i queda a l'historial per sempre |
| Posar una credencial en una anotació | Les anotacions es copien a objectes derivats, surten a describe i als logs dels controladors |
Passar-la a args o command |
Apareix a kubectl describe pod, a ps aux dins del contenidor i als logs del kubelet |
| Coure-la a la imatge | Queda en una capa del registre; docker history la mostra encara que l'esborris en una capa posterior |
| Escriure-la en un ConfigMap | Sense describe protegit, sense tmpfs, sense xifratge en repòs, i RBAC de ConfigMaps sol ser lax |
| Registrar-la en un log | Els logs es centralitzen, es copien i es conserven mesos (07-05) |
| Reutilitzar la mateixa credencial als tres entorns | Un incident a dev compromet pro |
Fer servir el Secret default o compartir-ne un entre components |
Impedeix revocar l'accés d'un sol component |
Concedir list secrets "perquè només llista noms" |
list retorna els continguts complets |
| Crear tokens de ServiceAccount permanents | Un token sense caducitat no es pot revocar netament |
Dependre només del .gitignore |
Un git add -f o un fitxer amb un altre nom se'l salten. Afegeix un pre-commit hook que detecti credencials |
Sobre el punt d'args, una demostració del que queda exposat:
# NO FACIS MAI AIXO
- name: worker
image: registry.rutasnorte.example/worker-notificacions:1.8.0
args: ["--smtp-password=Env10-2026!"]Qualsevol amb permís de lectura sobre pods —un permís que es concedeix a molta gent, inclosos els sistemes de monitoratge— acaba de llegir la contrasenya del correu.
- Les solucions reals de l'ecosistema
El Secret natiu resol el on viu la credencial dins del clúster. No resol com hi arriba de manera segura i auditable, ni com es versiona, ni com es rota sola. Per a això existeixen aquestes quatre famílies. No farem un tutorial de cap —cadascuna dona per a un curs—, però has de saber quan mirar cap a cada costat.
| Solució | Idea central | Quan triar-la |
|---|---|---|
| Sealed Secrets (Bitnami) | Un controlador al clúster té una clau privada; tu xifres amb la pública i puges el fitxer xifrat a Git. Només aquell clúster el pot desxifrar | Equips petits, GitOps pur, sense infraestructura externa |
| SOPS (+ age/KMS) | Xifra només els valors del YAML, deixant les claus llegibles. S'integra amb Flux, Helm i Kustomize | Quan vols diffs llegibles i ja fas servir GitOps |
| External Secrets Operator | Un CRD ExternalSecret declara d'on treure la credencial; l'operador la llegeix de Vault/AWS/Azure/GCP i manté sincronitzat un Secret natiu |
L'estàndard de facto a empreses amb un gestor de secrets ja muntat |
| HashiCorp Vault | Gestor de secrets complet: credencials dinàmiques de vida curta, motor de PKI, auditoria detallada, polítiques pròpies | Organitzacions grans, requisits d'auditoria forts |
| KMS dels núvols (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) | El gestor del núvol és la font de veritat; s'hi accedeix amb identitat federada, sense credencials estàtiques | Ja ets en aquell núvol |
Un exemple de com es veu un ExternalSecret, només perquè reconeguis el patró quan te'l trobis:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: postgres-reserves-credencials
namespace: rutas-norte-pro
spec:
refreshInterval: 1h # resincronitza cada hora: rotacio automatica
secretStoreRef:
name: vault-rutasnorte
kind: SecretStore
target:
name: postgres-reserves-credencials # el Secret NATIU que es creara
creationPolicy: Owner
data:
- secretKey: password # clau al Secret de Kubernetes
remoteRef:
key: rutasnorte/pro/postgres # ruta a Vault
property: passwordL'elegant del patró: aquest fitxer sí que pot anar a Git. No conté la credencial, només diu on trobar-la. I refreshInterval: 1h significa que si algú rota la contrasenya a Vault, el Secret del clúster s'actualitza sol en menys d'una hora.
On encaixa cada cosa en la maduresa d'una plataforma com Rutas Norte:
- Avui (on som): Secrets natius creats a mà, fora de Git, amb
.gitignorei un fitxer d'exemple. Acceptable adev. - Pas següent: xifratge en repòs amb KMS al clúster i RBAC estricte sobre secrets a
rutas-norte-pro. - Maduresa: External Secrets Operator apuntant al gestor de secrets corporatiu, amb rotació automàtica i auditoria de cada lectura.
Errors Comuns i Consells
| Error | Símptoma | Solució |
|---|---|---|
| Creure que base64 protegeix | Es puja el Secret a Git | És codificació, no xifratge. Mai a Git sense xifrar |
echo sense -n en codificar |
L'app rebutja la contrasenya "correcta" | Fes servir printf '%s' o echo -n, o millor stringData |
| Tipus incorrecte de Secret | data[username]: Required value |
Revisa la taula de tipus de l'apartat 4 |
| Secret en un altre namespace | Pod en ContainerCreating, FailedMount |
Els Secrets no creuen namespaces |
| Esperar refresc amb variables d'entorn | La rotació no arriba als pods | Les variables són immutables: rollout restart |
subPath amb un Secret |
La rotació no es propaga mai | Igual que amb ConfigMaps: evita subPath |
defaultMode: 0400 amb usuari no root |
Permission denied |
Fes servir 0440 amb fsGroup, o 0444 |
Oblidar imagePullSecrets |
ImagePullBackOff amb 401 |
Afegeix-lo al pod o, millor, a la ServiceAccount |
imagePullSecrets dins de containers |
Error de validació del YAML | Va a spec del pod |
Concedir list secrets |
Fuita silenciosa de totes les credencials | list retorna continguts. Tracta'l com get |
| No activar xifratge en repòs | Credencials llegibles a les còpies d'etcd | EncryptionConfiguration amb kms v2 |
| Rotar sense doble credencial | Tall de servei durant la rotació | Credencial nova vàlida abans de revocar la vella |
Contrasenya a args o en un log |
Visible a describe pod i al sistema de logs |
Volum o variable d'entorn, mai argument |
Consells finals:
- Tracta tot Secret com si s'hagués de filtrar. Dissenya assumint que algú el llegirà: credencials diferents per entorn i per component, amb el mínim privilegi a la base de dades.
- Instal·la un detector de credencials al
pre-commit.gitleaksodetect-secretscosten deu minuts de configuració i eviten el 90 % dels accidents. - Tingues un inventari de secrets. Què existeix, qui el consumeix, quan es va rotar per última vegada, qui n'és el responsable.
- Documenta les claus, mai els valors. Un
secret-postgres.yaml.exempleambpassword: CANVIAMversionat és útil i segur. - I el que s'ha dit al principi: que el disseny el revisi un professional de seguretat o el responsable de compliment. Amb dades personals de clients, això no és negociable.
Exercicis
Exercici 1: Demostrar que base64 no és xifratge
- Crea un Secret
passarella-pagamentarutas-norte-devamb les clausapi_key(valorsk_test_4eC39HqLyjWDarjtT1zdp7dc) ientorn(valorsandbox). - Mostra l'objecte en YAML i comprova que no es llegeix el valor.
- Descodifica
api_keyamb una sola ordre encadenada. - Comprova què mostra
kubectl described'aquest Secret i explica per què això no és una mesura de seguretat. - Respon: si un company té el verb
listsobre secrets en aquell namespace però noget, pot llegir la clau? Demostra-ho ambkubectl auth can-i.
Exercici 2: Saldar el deute de postgres-reserves
- Escriu el manifest del Secret
postgres-reserves-credencialsper arutas-norte-devfent servirstringData, ambusername,passwordidatabase, i amb l'esquema d'etiquetes de Rutas Norte. - Modifica el Deployment de
postgres-reservesperquè prengui les tres variables del Secret. - Modifica
api-reservesperquè munti nomésusernameipasswordcom a fitxers a/etc/secrets/postgresamb permisos de només lectura. - Verifica que el volum és a tmpfs.
- Demostra amb
grepque ak8s/no queda cap contrasenya en clar, i afegeix les regles necessàries al.gitignore.
Exercici 3: Rotació sense tall de servei
api-reserves corre amb 4 rèpliques a rutas-norte-pro i cal rotar la contrasenya de la base de dades perquè un antic membre de l'equip la coneixia.
- Escriu la seqüència completa de passos, indicant en quin d'ells hi ha risc de tall i com l'evites.
- Executa l'actualització del Secret amb una sola ordre que funcioni tant si el Secret existeix com si no.
- Fes que els pods adoptin la credencial nova sense quedar-te sense servei en cap moment.
- Verifica que els 4 pods nous són amunt i que cap no conserva la credencial anterior.
- Identifica quins consumidors del Secret no es reinicien amb
kubectl rollout restarti què faries amb ells.
Solucions
Solució 1
kubectl create secret generic passarella-pagament \
--from-literal=api_key=sk_test_4eC39HqLyjWDarjtT1zdp7dc \
--from-literal=entorn=sandbox \
-n rutas-norte-dev
kubectl get secret passarella-pagament -n rutas-norte-dev -o yamlsecret/passarella-pagament created
apiVersion: v1
data:
api_key: c2tfdGVzdF80ZUMzOUhxTHlqV0Rhcmp0VDF6ZHA3ZGM=
entorn: c2FuZGJveA==
kind: Secret
metadata:
name: passarella-pagament
namespace: rutas-norte-dev
type: OpaqueLa descodificació:
kubectl get secret passarella-pagament -n rutas-norte-dev \
-o jsonpath='{.data.api_key}' | base64 -d; echoName: passarella-pagament
Namespace: rutas-norte-dev
Type: Opaque
Data
====
api_key: 32 bytes
entorn: 7 bytesdescribe només mostra la mida. No és una mesura de seguretat, sinó d'higiene visual: evita mostrar la credencial per accident en una pantalla compartida o a la sortida d'un script. Qui té permís per fer describe té el verb get, i amb get -o yaml n'obté el valor. La protecció real la donen RBAC i el xifratge en repòs, no el format de sortida.
Sobre list:
kubectl auth can-i list secrets -n rutas-norte-dev [email protected]
kubectl get secrets -n rutas-norte-dev -o yaml [email protected] | grep api_keySí que la pot llegir. list retorna els objectes complets, no només els seus noms. És un error de concessió de permisos molt estès.
Solució 2
# k8s/entorns/dev/secret-postgres.yaml (NO es versiona a Git)
apiVersion: v1
kind: Secret
metadata:
name: postgres-reserves-credencials
namespace: rutas-norte-dev
labels:
app: postgres-reserves
app.kubernetes.io/name: postgres-reserves
app.kubernetes.io/component: base-dades
app.kubernetes.io/part-of: rutas-norte
entorn: dev
type: Opaque
stringData:
username: rutasnorte
password: "d3v-C4nvi4m3-2026"
database: reservesEl Deployment de postgres-reserves és el de l'apartat 7. El fragment d'api-reserves:
volumeMounts:
- name: credencials-bd
mountPath: /etc/secrets/postgres
readOnly: true
volumes:
- name: credencials-bd
secret:
secretName: postgres-reserves-credencials
defaultMode: 0400
items:
- key: username
path: username
- key: password
path: passwordVerificació del tmpfs i que només hi ha dos fitxers:
kubectl exec -n rutas-norte-dev deploy/api-reserves -- sh -c \
'ls /etc/secrets/postgres && mount | grep secrets'Dos fitxers, no tres: database no s'ha projectat perquè no és a items. I el volum és tmpfs, és a dir, RAM.
Sense resultats. El .gitignore:
# Secrets: mai al repositori
k8s/**/secret-*.yaml
k8s/**/*-secret.yaml
!k8s/**/*.yaml.exemple
*.key
*.pem
*.p12La línia !k8s/**/*.yaml.exemple permet versionar les plantilles amb valors ficticis, que són les que documenten quines claus calen.
Solució 3
La seqüència, amb el risc assenyalat:
- Crear la contrasenya nova a PostgreSQL. Risc alt si el motor només admet una contrasenya per usuari: els pods vius es trencaran a la connexió següent. S'evita creant un usuari nou (
rutasnorte_b) en lloc de canviar el de l'actual, deixant tots dos vàlids. - Actualitzar el Secret. Sense risc: els pods vius ja tenen la credencial carregada a memòria.
rollout restart. Sense risc simaxUnavailableestà ben posat: el mòdul 2 ens garanteix substitució progressiva.- Revocar la credencial vella. Risc si queda algun consumidor sense reiniciar; per això va després de verificar.
kubectl create secret generic postgres-reserves-credencials \
--from-literal=username=rutasnorte_b \
--from-literal=password='R0t4d4-2026-08!' \
--from-literal=database=reserves \
-n rutas-norte-pro --dry-run=client -o yaml | kubectl apply -f -El truc --dry-run=client -o yaml | kubectl apply -f - és l'idiomàtic: genera l'objecte i l'aplica, funcionant tant per crear com per actualitzar, cosa que kubectl create sol no fa (fallaria amb AlreadyExists).
kubectl rollout restart deploy/api-reserves -n rutas-norte-pro
kubectl rollout status deploy/api-reserves -n rutas-norte-pro
kubectl get pods -l app=api-reserves -n rutas-norte-prodeployment.apps/api-reserves restarted
Waiting for deployment "api-reserves" rollout to finish: 2 of 4 updated replicas are available...
deployment "api-reserves" successfully rolled out
NAME READY STATUS RESTARTS AGE
api-reserves-7f4b8c9d6-2xkqp 1/1 Running 0 48s
api-reserves-7f4b8c9d6-8mvtr 1/1 Running 0 62s
api-reserves-7f4b8c9d6-j9wzl 1/1 Running 0 35s
api-reserves-7f4b8c9d6-qh4nc 1/1 Running 0 21sEls quatre pods són nous (mateix pod-template-hash, AGE menor d'un minut i RESTARTS 0). Verificació de la credencial efectiva:
Consumidors que no es reinicien amb rollout restart dels Deployments:
- CronJobs com
informes-ocupacio(mòdul 6): els pods es creen a cada execució, així que prendran la credencial nova tots sols; però si hi ha una execució en curs durant la rotació, fallarà. Cal mirarkubectl get jobsabans de revocar. - Pods solts creats a mà per depurar. Es localitzen amb
kubectl get pods -n rutas-norte-pro -o json | jq '.items[] | select(.metadata.ownerReferences == null) | .metadata.name'. - StatefulSets (mòdul 6):
rollout restartfunciona, però és ordinal i més lent; cal esperar que acabi. - Clients fora del clúster: eines d'informes, còpies de seguretat, quadres de comandament. No els veu Kubernetes i són els que provoquen l'incident en revocar. Per això l'inventari de consumidors de l'apartat 11 no és burocràcia.
Conclusió
Has saldat el deute més greu del mòdul 2. La contrasenya de postgres-reserves ja no és a cap manifest de Git: viu en un Secret, api-reserves la rep com a fitxer muntat en memòria amb només les claus que necessita, i postgres-reserves la pren de secretKeyRef per inicialitzar-se. Un grep sobre k8s/ no retorna res.
Però l'important d'aquesta lliçó no és l'objecte, sinó el criteri. Saps que un Secret es diferencia d'un ConfigMap en stringData, en el muntatge en tmpfs, en què describe amaga el contingut i en què existeix un camp type que valida la forma. I saps, perquè ho has demostrat amb una ordre, que base64 no és xifratge: la protecció real l'aporten RBAC, el xifratge en repòs i l'aïllament per namespaces, no l'objecte en si. Coneixes els set tipus de Secret i per a què serveix cadascun, les tres formes de crear-los, i per què stringData és sempre preferible a data en escriure a mà.
Saps que el xifratge en repòs a etcd no ve activat, has vist la contrasenya llegible en un bolcat d'etcd, coneixes els cinc proveïdors d'EncryptionConfiguration i per què kms v2 és l'únic realment sòlid, i que activar-lo no xifra retroactivament el que ja existeix. Tens clar qui pot llegir un secret —amb les dues sorpreses: list filtra continguts i poder crear pods equival a poder-ho llegir tot—, com es rota una credencial sense tallar el servei i per què les variables d'entorn no es refresquen mai. Tens la llista del que no s'ha de fer mai i un mapa de l'ecosistema real —Sealed Secrets, SOPS, External Secrets Operator, Vault, els KMS dels núvols— per quan el Secret natiu es quedi curt. I no oblidis l'advertiment de l'inici: amb les dades personals que desa Rutas Norte, el disseny definitiu l'ha de validar un professional de seguretat o el responsable de compliment.
Les dues lliçons han deixat un cap solt comú. Hem vist els objectes que guarden la configuració i els secrets, i una de les formes de portar-los al contenidor: el fitxer muntat. Falta l'altra, la que apareix al 80 % dels manifests del món real i que totes dues lliçons han esmentat sense desenvolupar. La següent, Variables d'Entorn, la cobreix sencera: env, envFrom amb prefix, valueFrom amb configMapKeyRef i secretKeyRef, el flag optional, la Downward API perquè api-reserves registri a les seves traces quin pod i quin node va atendre cada petició, l'expansió $(VAR) i les seves regles, les col·lisions quan la mateixa clau arriba per diverses vies, i la tècnica del hash de la configuració en una anotació que resol d'una vegada el problema que les variables d'entorn no es refresquin mai.
Curs de Kubernetes
Mòdul 1: Introducció a Kubernetes
- Què és Kubernetes?
- Arquitectura de Kubernetes
- Conceptes i Terminologia Clau
- Configuració d'un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objectes, Manifests YAML i el Model Declaratiu
- El Projecte del Curs: la Plataforma Rutas Norte
Mòdul 2: Components Principals de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualitzacions, Rollbacks i Estratègies de Desplegament
- Serveis
- Namespaces
- Etiquetes, Selectors i Anotacions
Mòdul 3: Gestió de Configuració i Secrets
- ConfigMaps
- Secrets
- Variables d'Entorn
- Quotes i Límits de Recursos
- LimitRanges i Classes de Qualitat de Servei (QoS)
- ServiceAccounts i Accés a l'API des dels Pods
Mòdul 4: Xarxes a Kubernetes
- Xarxes de Clúster
- Tipus de Serveis
- DNS Intern i Descobriment de Serveis
- Controladors d'Ingress
- TLS i Gestió de Certificats amb cert-manager
- Polítiques de Xarxa
Mòdul 5: Emmagatzematge a Kubernetes
- Volums
- Volums Persistents
- Reclamacions de Volums Persistents
- Classes d'Emmagatzematge
- Aprovisionament Dinàmic, Expansió i Snapshots
- Còpies de Seguretat i Restauració de Dades
Mòdul 6: Conceptes Avançats de Kubernetes
- StatefulSets
- DaemonSets
- Treballs i CronJobs
- Init Containers, Sidecars i Patrons Multicontenidor
- Planificació: Afinitat, Taints i Toleracions
- Definicions de Recursos Personalitzats (CRDs)
- Operadors i el Patró Controlador
Mòdul 7: Monitoratge i Registre
- Verificacions de Salut i Sondes
- Servidor de Mètriques i kubectl top
- Monitoratge amb Prometheus
- Visualització i Alertes amb Grafana i Alertmanager
- Registre Centralitzat amb Elasticsearch, Fluentd i Kibana (EFK)
- Depuració d'Aplicacions i Esdeveniments del Clúster
Mòdul 8: Seguretat a Kubernetes
- Control d'Accés Basat en Rols (RBAC)
- Contextos de Seguretat i Enduriment del Contenidor
- Polítiques de Seguretat de Pods i Pod Security Standards
- Seguretat de Xarxa
- Seguretat d'Imatges
- Auditoria, Escaneig i Gestió de Vulnerabilitats
Mòdul 9: Escalat i Rendiment
- Autoescalat Horitzontal de Pods
- Autoescalat Vertical de Pods
- Autoescalat de Clúster
- Escalat per Esdeveniments i Mètriques Personalitzades amb KEDA
- Alta Disponibilitat: PodDisruptionBudgets i Topologia
- Ajust de Rendiment
Mòdul 10: Ecosistema i Eines de Kubernetes
- Minikube i Entorns Locals amb kind
- Kubeadm
- Helm
- Kustomize
- GitOps amb Argo CD i Flux
- Kubernetes Gestionat: EKS, AKS i GKE
Mòdul 11: Estudis de Cas i Aplicacions del Món Real
- Desplegament d'una Aplicació Web
- Execució d'Aplicacions amb Estat
- CI/CD amb Kubernetes
- Estratègies de Desplegament: Blue-Green i Canary
- Gestió Multi-Clúster
- Operació en Producció: Incidències, Runbooks i Costos
