Initial commit — LoginMaster tenant deployment toolkit
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.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
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).
|
||||
Reference in New Issue
Block a user