Garder l'utilisateur de base de données synchronisé¶
Utilisateurs de base de données crée l'utilisateur tenax une fois. Cela suffit dans la
plupart des cas. Cette page s'adresse aux déploiements GitOps où l'utilisateur doit rester conforme à son Secret sans
que personne n'ait rien à lancer :
- le mot de passe du Secret fait l'objet d'une rotation ;
- la base est reconstruite ou restaurée depuis un dump plus ancien, et l'utilisateur a disparu ;
- quelqu'un modifie l'utilisateur à la main (retire un droit, change le mot de passe) ;
- la base est créée après les manifestes qui décrivent son utilisateur (un cluster neuf).
Un Job ponctuel ne gère aucun de ces cas : il s'exécute une fois, et Kubernetes ne relance pas un Job terminé quand le Secret change. Un CronJob qui réapplique le même SQL idempotent, si : en une période au plus, l'utilisateur correspond de nouveau au Secret.
Fonctionnement¶
Un CronJob par base, dans le namespace de la base. À chaque exécution, il :
- attend que la base réponde (dans la limite de l'échéance du Job) ;
- se connecte en administrateur, avec le Secret qu'utilise la base elle-même ;
- crée l'utilisateur
tenaxs'il manque, et définit son mot de passe et ses droits à partir du Secret Tenax.
Chaque instruction est idempotente : une exécution qui trouve tout en place ne change rien. Les mots de passe arrivent dans le conteneur sous forme de variables d'environnement issues de Secrets ; ils ne figurent jamais sur une ligne de commande ni dans un log.
PostgreSQL¶
Remplacez les quatre valeurs marquées # ← : le namespace, le Service de la base, la base, ainsi que le Secret et la
clé qui contiennent le mot de passe administrateur.
apiVersion: batch/v1
kind: CronJob
metadata:
name: tenax-user-postgres
namespace: team-a # ←
spec:
schedule: "*/30 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 300
template:
spec:
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext: { runAsNonRoot: true, runAsUser: 999, runAsGroup: 999 }
containers:
- name: psql
image: docker.io/library/postgres:16
resources: { requests: { cpu: 10m, memory: 32Mi }, limits: { memory: 128Mi } }
securityContext: { allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
env:
- { name: PGHOST, value: postgres } # ← the database's Service
- { name: PGDATABASE, value: app } # ← any database on the server
- { name: PGUSER, value: postgres }
- { name: PGCONNECT_TIMEOUT, value: "10" }
- name: PGPASSWORD
valueFrom: { secretKeyRef: { name: postgres-admin, key: password } } # ←
- name: TENAX_PASSWORD
valueFrom: { secretKeyRef: { name: tenax-postgres, key: password } }
command: [sh, -ec]
args:
- |
until pg_isready -q; do echo "waiting for $PGHOST"; sleep 5; done
psql -v ON_ERROR_STOP=1 -X <<'SQL'
\getenv tenax_password TENAX_PASSWORD
SELECT 'CREATE ROLE tenax LOGIN REPLICATION' WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'tenax') \gexec
ALTER ROLE tenax WITH LOGIN REPLICATION NOSUPERUSER NOCREATEDB NOCREATEROLE PASSWORD :'tenax_password';
GRANT pg_read_all_data, pg_monitor TO tenax;
ALTER ROLE tenax SET idle_in_transaction_session_timeout = '10min';
SQL
echo "tenax user in sync on $PGHOST"
\getenv nécessite psql 15 ou plus récent dans l'image ; le serveur peut être de n'importe quelle version prise en
charge. Définissez max_slot_wal_keep_size dans la configuration du serveur (par exemple
-c max_slot_wal_keep_size=2GB), pas ici : c'est un réglage du serveur, pas de l'utilisateur.
MySQL¶
apiVersion: batch/v1
kind: CronJob
metadata:
name: tenax-user-mysql
namespace: team-a # ←
spec:
schedule: "*/30 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 300
template:
spec:
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext: { runAsNonRoot: true, runAsUser: 999, runAsGroup: 999 }
containers:
- name: mysql
image: docker.io/library/mysql:8.4
resources: { requests: { cpu: 10m, memory: 64Mi }, limits: { memory: 256Mi } }
securityContext: { allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
env:
- { name: MYSQL_HOST, value: mysql } # ← the database's Service
- name: ROOT_PASSWORD
valueFrom: { secretKeyRef: { name: mysql-admin, key: root-password } } # ←
- name: TENAX_PASSWORD
valueFrom: { secretKeyRef: { name: tenax-mysql, key: password } }
command: [bash, -ec]
args:
- |
umask 077
printf '[client]\nuser=root\npassword=%s\nhost=%s\nconnect-timeout=10\n' "$ROOT_PASSWORD" "$MYSQL_HOST" > /tmp/root.cnf
until mysqladmin --defaults-extra-file=/tmp/root.cnf ping --silent; do echo "waiting for $MYSQL_HOST"; sleep 5; done
mysql --defaults-extra-file=/tmp/root.cnf <<SQL
CREATE USER IF NOT EXISTS 'tenax'@'%' IDENTIFIED BY '${TENAX_PASSWORD}';
ALTER USER 'tenax'@'%' IDENTIFIED BY '${TENAX_PASSWORD}';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, LOCK TABLES, PROCESS, RELOAD, SHOW_ROUTINE,
REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'tenax'@'%';
SQL
rm -f /tmp/root.cnf
echo "tenax user in sync on $MYSQL_HOST"
Le mot de passe est inséré dans le SQL sans autre échappement que le littéral de chaîne : générez-le donc en
hexadécimal (openssl rand -hex 24).
L'exécuter maintenant¶
kubectl -n team-a create job --from=cronjob/tenax-user-postgres tenax-user-now
kubectl -n team-a logs -f job/tenax-user-now
Toutes les 30 minutes suffit largement : Tenax relance lui-même une connexion refusée et le signale sur la cible, si
bien qu'une rotation est prise en compte au plus une période plus tard. La règle pg_hba.conf et les réglages du
binlog restent de la configuration serveur (Utilisateurs de base de données, étape 3) ; ce
CronJob n'y touche pas.