Bizalmas adatok és konfiguráció kezelése Kubernetesben
Legutóbb a Kubernetes Volume, Persistent Volume és Persistent Volume Claim hármasáról volt szó, vagyis arról, hogyan tud egy alkalmazás adata túlélni egy container újraindítását, sőt akár magának a Pod-nak a törlését is.

Folytassuk ezt a területet azzal, hogy hogyan lehet magát a storage-ot automatizáltan orchestrálni a cluster-en belül, illetve hogyan jusson el biztonságosan a konfigurációs adat, legyen az bizalmas vagy sem, a container-ekhez.
Storage orchestration Rookkal
Amikor egy adott storage backendet, például egy Ceph cluster-t kellene kézzel telepíteni és karbantartani minden egyes Kubernetes cluster mellett, az gyorsan sok manuális munkává válik. Erre ad megoldást a Rook projekt, amely a cloud native világ decoupled és distributed jellegéhez igazodva teszi lehetővé a storage orchestration-t több különböző storage provider felett.
A Rook ugyanúgy CRD-ket, azaz custom resource definition-öket és egy custom operatort használ, mint a Kubernetes más agentjei: az operator API hívás alapján, a mögöttes storage típusának megfelelően provisionálja a tárhelyet. Több storage provider is támogatott, köztük a Ceph, a Cassandra és a Network File System, vagyis NFS.
Secrets: amikor az adatot nem szabad nyílt szemmel olvasni
A Volume-ok jól működnek helyi adatok eléréséhez, de van olyan adat, amit nem szeretnél nyílt szöveggel olvashatóvá tenni. Egy jelszó tipikus példa erre. Erre a célra való a Secret API resource.
Egy Secret manuálisan vagy a kubectl create secret paranccsal is létrehozható:
kubectl create secret generic mysql --from-literal=password=root
Fontos tudni, hogy egy Secret alapértelmezetten nincs titkosítva, csak base64-encodolva van. Ahhoz, hogy tényleges titkosítást kapj, létre kell hoznod egy EncryptionConfiguration objektumot egy kulccsal és a megfelelő identity-vel, majd a kube-apiserver-nek meg kell adni a --encryption-provider-config flaget, amely egy korábban konfigurált providerre mutat, például aescbc-re vagy kms-re. Ha ezt bekapcsolod, minden meglévő Secretet újra kell létrehoznod, mert azok csak írás közben lesznek titkosítva.
Több kulcs is megadható egyszerre. Dekódoláskor minden provider minden kulcsát megpróbálja a rendszer, de titkosításhoz mindig az első provider első kulcsa lesz használva. Kulcs rotációhoz először egy új kulcsot kell létrehozni, majd újra kell indítani az összes kube-apiserver processzt, végül minden Secretet újra kell létrehozni.
A kubectl paranccsal megnézhető a Secret-ben tárolt encodolt string, amely dekódolva egy fájlba mentett stringként jelenik meg. Ez a fájl felhasználható environment variable-ként vagy egy új könyvtárban, hasonlóan ahhoz, ahogy egy Volume megjelenik.
Secrets használata environment variable-ként
spec:
containers:
- image: mysql:5.5
name: dbpod
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: password
A Secretek számára nincs darabszám limit, de méretben egy Secret legfeljebb 1MB lehet. Minden Secret memóriát foglal a többi API objektummal együtt, így nagyon sok Secret könnyen kimerítheti egy node memóriáját. A Secretek a node tmpfs storage-ában tárolódnak, és csak arra a node-ra kerülnek el, amelyiken az adott Pod fut. Mivel egy Pod minden Volume-ját mountolni kell, mielőtt a containerek elindulnának, a Secretnek már léteznie kell azelőtt, hogy a Pod igényelné.
Secrets mountolása Volumeként
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /mysqlpassword
name: mysql
name: busy
volumes:
- name: mysql
secret:
secretName: mysql
Miután a Pod fut, a container-en belül ellenőrizhető, hogy a Secret valóban elérhető-e:
kubectl exec -ti busybox -- cat /mysqlpassword/password
ConfigMap: hordozható konfiguráció bizalmas adat nélkül
Hasonló API resource a Secrethez a ConfigMap, azzal a különbséggel, hogy az adat nincs encodolva. Ez is a Kubernetes decoupling elvét szolgálja: egy ConfigMap segítségével a container image leválasztható a konfigurációs artifactoktól.
Az adat kulcs-érték párok halmazaként vagy akár teljes konfigurációs fájlként tárolható, bármilyen formátumban. Az adat származhat egy fájlból, egy teljes könyvtár összes fájljából, vagy akár egy literal értékből is.
Egy ConfigMap többféleképpen is felhasználható: a container environment variable-ként olvashatja az adatot egy vagy több forrásból, a benne tárolt értékek átadhatók a Pod-on belül futtatott parancsoknak, létrehozható belőle egy Volume vagy egy fájl egy Volume-on belül, adott névvel és access mode-dal, sőt akár cluster komponensek, például controllerek is használhatják.
Ha van egy config.js nevű fájlod a helyi fájlrendszereden, abból létrehozható egy ConfigMap, amely a fájl tartalmát egy data szekcióban tárolja:
kubectl get configmap foobar -o yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: foobar
data:
config.js: |
{ ... }
ConfigMap használata
Environment variable esetén a Pod manifestben a valueFrom kulcsot és a configMapKeyRef értéket kell használni:
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
Volume esetén egy configMap típusú Volume-ot kell definiálni a Pod-ban, majd mountolni oda, ahol szükség van rá:
volumes:
- name: config-volume
configMap:
name: special-config
Fontos, hogy a ConfigMap-nek, ugyanúgy mint a Secretnek, léteznie kell, mielőtt a Pod felhasználná, hacsak nincs optional-ként megjelölve. Emellett mindkettő egy adott namespace-hez kötött.
Mire érdemes figyeljünk
Az egyik leggyakoribb félreértés, hogy a base64 encoding valamiféle titkosítást is jelent. Nem jelent: base64-tel bárki egy egyszerű dekódolással visszaállítja az eredeti értéket, a tényleges titkosításhoz az EncryptionConfiguration és a megfelelő --encryption-provider-config beállítás szükséges a kube-apiserveren.
A másik gyakori jelenség, amikor valaki egy futó Pod-ban environment variable-ként vár frissülést egy módosított ConfigMap vagy Secret után. Egy Volume-ba mountolt ConfigMap vagy Secret idővel frissül a container-ben, de az environment variable-ként átadott érték csak a Pod újraindulása után változik, mert az érték a Pod indulásakor kerül beégetésre.
Mot nem térünk ki az RBAC-alapú hozzáférés-szabályozása a Secretek körül, sem az external secret store-okkal, például a HashiCorp Vaulttal való integrációra. Ezek fontosak lesznek, amint éles környezetben kell megtervezni a bizalmas adatok kezelését, de kezdésnek elég, ha érted, mi a különbség egy Secret és egy ConfigMap között, és hogyan jut el mindkettő a Pod-okhoz.
Összegezve
A Rook megmutatja, hogyan lehet magát a storage réteget is deklaratívan, operator alapon kezelni a cluster-en belül, míg a Secret és a ConfigMap arra ad választ, hogyan válasszuk le a konfigurációt, legyen az bizalmas vagy sem, a container image-től. Ha ezt a három fogalmat érted, és tudod, melyiket mikor érdemes használni, onnantól már csak a saját cluster-ed konkrét storage és biztonsági követelményeit kell hozzáigazítanod ehhez az alapmodellhez.
