468d4562c7
Toolkit per deployare/aggiornare un tenant LoginMaster su qualsiasi Kubernetes (EKS/AKS/DOKS/Scaleway/vSphere/...). Contiene: - deploy.sh: bootstrap di un nuovo tenant (idempotente, re-run protection, storage class auto-rilevata, prompt separati api/admin tag, generazione segreti crittografici via openssl rand). - update.sh: rolling update zero-downtime con tag api/admin separati, rollback hint via 'kubectl rollout undo', riapplicazione opzionale del ConfigMap. - templates/: 8 manifest parametrici (envsubst): namespace, cert-manager TLS Mongo, NetworkPolicy intra-namespace, ConfigMap, MongoDB StatefulSet 3 repliche con TLS interno + initContainer per keyfile/PEM, tenant-api Deployment 2 repliche con CA validation, tenant-admin, ingress nginx + Let's Encrypt. Sicurezza: TLS interno Mongo (cert-manager CA self-signed 10y), keyFile per auth replica set, password client mai in argv, NetworkPolicy che isola il tenant, pod Mongo non-root (uid 999) con initContainer come root per i file runtime in tmpfs.
199 lines
6.7 KiB
YAML
199 lines
6.7 KiB
YAML
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: mongodb-tenant-headless
|
|
namespace: ${NAMESPACE}
|
|
spec:
|
|
clusterIP: None
|
|
selector:
|
|
app: mongodb-tenant
|
|
ports:
|
|
- port: 27017
|
|
targetPort: 27017
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: mongodb-tenant
|
|
namespace: ${NAMESPACE}
|
|
spec:
|
|
serviceName: mongodb-tenant-headless
|
|
replicas: 3
|
|
podManagementPolicy: Parallel
|
|
selector:
|
|
matchLabels:
|
|
app: mongodb-tenant
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: mongodb-tenant
|
|
spec:
|
|
imagePullSecrets:
|
|
- name: registry-codebaker
|
|
securityContext:
|
|
# fsGroup garantisce ownership corretta sul PVC (mongo data) per uid 999.
|
|
# runAsUser/Group: il container principale gira come mongodb (uid 999),
|
|
# non root. L'init invece deve essere root per poter chmod 400 + chown.
|
|
runAsUser: 999
|
|
runAsGroup: 999
|
|
fsGroup: 999
|
|
initContainers:
|
|
# Prepara keyfile e PEM nel tmpfs /run/mongo con owner=mongodb mode=0400.
|
|
# Mongod richiede esattamente 0400/0600 owned-by-self e quel mode non si
|
|
# può ottenere via secret defaultMode senza far girare il container come
|
|
# root. Quindi: init come root, main come 999.
|
|
- name: init-tls
|
|
image: mongo:6.0
|
|
securityContext:
|
|
runAsUser: 0
|
|
runAsGroup: 0
|
|
command:
|
|
- /bin/sh
|
|
- -c
|
|
- >-
|
|
cp /etc/secrets/keyfile /run/mongo/mongodb-keyfile &&
|
|
cat /etc/mongo-tls/tls.crt /etc/mongo-tls/tls.key > /run/mongo/mongo-server.pem &&
|
|
chown 999:999 /run/mongo/mongodb-keyfile /run/mongo/mongo-server.pem &&
|
|
chmod 400 /run/mongo/mongodb-keyfile /run/mongo/mongo-server.pem
|
|
volumeMounts:
|
|
- name: mongo-runtime
|
|
mountPath: /run/mongo
|
|
- name: mongo-keyfile
|
|
mountPath: /etc/secrets
|
|
readOnly: true
|
|
- name: mongo-tls
|
|
mountPath: /etc/mongo-tls
|
|
readOnly: true
|
|
affinity:
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchLabels:
|
|
app: mongodb-tenant
|
|
topologyKey: kubernetes.io/hostname
|
|
containers:
|
|
- name: mongodb
|
|
image: mongo:6.0
|
|
# I file runtime (keyfile + PEM cert+key) sono già stati preparati dall'
|
|
# initContainer in /run/mongo con owner mongodb:mongodb mode 0400.
|
|
command:
|
|
- mongod
|
|
- --bind_ip_all
|
|
- --replSet
|
|
- rs0
|
|
- --auth
|
|
- --keyFile
|
|
- /run/mongo/mongodb-keyfile
|
|
- --tlsMode
|
|
- requireTLS
|
|
- --tlsCertificateKeyFile
|
|
- /run/mongo/mongo-server.pem
|
|
- --tlsCAFile
|
|
- /etc/mongo-tls/ca.crt
|
|
# Senza questo flag, --tlsCAFile mette mongod in mTLS e rifiuta ogni
|
|
# client che non presenta un cert. I membri del replica set si autenticano
|
|
# via keyFile (clusterAuthMode keyFile, default), non x.509: quindi non
|
|
# serve mTLS. I client (API, probe, mongosh) presentano solo password.
|
|
- --tlsAllowConnectionsWithoutCertificates
|
|
ports:
|
|
- containerPort: 27017
|
|
volumeMounts:
|
|
- name: mongodb-data
|
|
mountPath: /data/db
|
|
- name: mongo-runtime
|
|
mountPath: /run/mongo
|
|
readOnly: true
|
|
- name: mongo-tls
|
|
mountPath: /etc/mongo-tls
|
|
readOnly: true
|
|
resources:
|
|
requests:
|
|
cpu: "${MONGO_CPU_REQUEST}"
|
|
memory: "${MONGO_MEM_REQUEST}"
|
|
limits:
|
|
cpu: "${MONGO_CPU_LIMIT}"
|
|
memory: "${MONGO_MEM_LIMIT}"
|
|
# quit(N) propaga N come exit code: probe fallisce solo se ping.ok != 1.
|
|
# Connessione su 127.0.0.1 con --tlsAllowInvalidHostnames perché il cert
|
|
# non include localhost nei SAN (la CA però viene validata).
|
|
livenessProbe:
|
|
exec:
|
|
command:
|
|
- mongosh
|
|
- --quiet
|
|
- --tls
|
|
- --tlsCAFile
|
|
- /etc/mongo-tls/ca.crt
|
|
- --tlsAllowInvalidHostnames
|
|
- --host
|
|
- "127.0.0.1"
|
|
- --eval
|
|
- "quit(db.adminCommand('ping').ok===1?0:1)"
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
readinessProbe:
|
|
exec:
|
|
command:
|
|
- mongosh
|
|
- --quiet
|
|
- --tls
|
|
- --tlsCAFile
|
|
- /etc/mongo-tls/ca.crt
|
|
- --tlsAllowInvalidHostnames
|
|
- --host
|
|
- "127.0.0.1"
|
|
- --eval
|
|
- "quit(db.adminCommand('ping').ok===1?0:1)"
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
volumes:
|
|
- name: mongo-runtime
|
|
emptyDir:
|
|
medium: Memory
|
|
sizeLimit: 16Mi
|
|
- name: mongo-keyfile
|
|
secret:
|
|
secretName: mongodb-tenant-auth
|
|
items:
|
|
- key: keyfile
|
|
path: keyfile
|
|
# Con fsGroup=999 il file è root:mongodb 0440 — leggibile solo dal gruppo
|
|
# mongodb (uid/gid 999), non world-readable. Il container poi lo copia in
|
|
# /data/db/ e fa chmod 400 perché mongod richiede mode 0400/0600 owner-only.
|
|
defaultMode: 0440
|
|
- name: mongo-tls
|
|
secret:
|
|
# Secret popolato da cert-manager (vedi templates/01-mongodb-tls.yaml).
|
|
# Contiene tls.crt, tls.key, ca.crt — leggibili dal gruppo 999 via fsGroup.
|
|
secretName: mongodb-tenant-tls
|
|
defaultMode: 0440
|
|
volumeClaimTemplates:
|
|
- metadata:
|
|
name: mongodb-data
|
|
spec:
|
|
accessModes: ["ReadWriteOnce"]
|
|
storageClassName: "${STORAGE_CLASS}"
|
|
resources:
|
|
requests:
|
|
storage: ${STORAGE_SIZE}
|
|
---
|
|
apiVersion: policy/v1
|
|
kind: PodDisruptionBudget
|
|
metadata:
|
|
name: mongodb-tenant-pdb
|
|
namespace: ${NAMESPACE}
|
|
spec:
|
|
# Il replica set tollera la perdita di 1 membro (quorum = 2/3).
|
|
# minAvailable: 2 evita che drain/voluntary disruption tolga il quorum.
|
|
minAvailable: 2
|
|
selector:
|
|
matchLabels:
|
|
app: mongodb-tenant
|
|
# NB: rs.initiate e la creazione degli utenti root/app sono eseguite da
|
|
# `deploy.sh` via `kubectl exec` nel pod mongodb-tenant-0 (sfrutta la
|
|
# "localhost exception" di Mongo per creare il primo utente senza auth).
|