add tls sync

This commit is contained in:
2026-01-22 02:28:10 +01:00
parent c94d6254fa
commit 50b7092809
16 changed files with 979 additions and 75 deletions

View File

@@ -0,0 +1,47 @@
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: tls-sync-wildcard
namespace: argocd-ops
spec:
generators:
# Le CronJob de synchronisation est déployé UNIQUEMENT sur le cluster OPS
- merge:
generators:
# Premier générateur : scanne les répertoires Helm pour tls-sync-wildcard
- git:
repoURL: https://git.gkdomaine.fr/kubernetes/argocd.git
revision: main
directories:
- path: "helm/tls-sync-wildcard/*"
# Deuxième générateur : lit la config OPS uniquement
- git:
repoURL: https://git.gkdomaine.fr/kubernetes/argocd.git
revision: main
files:
- path: "configs/ops/config.json"
mergeKeys:
- path.basename
template:
metadata:
name: 'tls-sync-wildcard-{{path.basename}}'
spec:
project: default
source:
repoURL: '{{repository}}'
targetRevision: '{{targetRevision}}'
path: '{{helmPath}}/tls-sync-wildcard/{{path.basename}}'
helm:
valueFiles:
- values.yaml
destination:
# Déploie uniquement sur le cluster OPS
name: '{{name}}'
namespace: certificates-{{path.basename}}
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,33 +0,0 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: gkpoubelle78@gmail.com
privateKeySecretRef:
name: letsencrypt-dns01-prod
solvers:
- dns01:
webhook:
groupName: acme.gkdomaine.fr
solverName: ovh
config:
# Noms des champs imposés par le chart aureq
ovhEndpointName: "ovh-eu"
ovhAuthenticationMethod: "application"
# ATTENTION : Notez bien les noms des clés ci-dessous
applicationKey: "1d1a85ccc3a5bcc9"
applicationSecretRef:
name: ovh-credentials
key: application-secret
applicationConsumerKeyRef: # et non consumerKeyRef
name: ovh-credentials
key: consumer-key
selector:
dnsZones:
- "dev.gkdomaine.fr"
- "rct.gkdomaine.fr"
- "prd.gkdomaine.fr"

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: tls-sync-wildcard
description: CronJob pour synchroniser automatiquement les secrets TLS wildcard dans tous les namespaces
type: application
version: 1.0.0

View File

@@ -0,0 +1,208 @@
# TLS Sync Wildcard - Synchronisation automatique des certificats wildcard
Ce chart Helm déploie un CronJob qui synchronise automatiquement les secrets TLS wildcard depuis le cluster OPS vers tous les namespaces qui en ont besoin dans les clusters DEV, RCT et PRD.
## Problème résolu
En Kubernetes, un Ingress ne peut référencer un secret TLS que s'il est dans le **même namespace** que l'Ingress. C'est une limitation de Kubernetes.
Pour les certificats wildcard utilisés par plusieurs applications dans différents namespaces, il faut donc copier le secret dans chaque namespace.
Ce CronJob automatise cette synchronisation.
## Installation
### 1. Créer le Secret avec le kubeconfig
Le CronJob a besoin d'accéder aux différents clusters. Créez un Secret contenant les kubeconfigs :
```bash
# Option 1 : Utiliser le kubeconfig par défaut (si tous les contextes sont dedans)
kubectl create secret generic tls-sync-kubeconfig \
--from-file=config=$HOME/.kube/config \
-n certificates-ops \
--dry-run=client -o yaml | kubectl apply -f -
# Option 2 : Créer un kubeconfig combiné avec tous les contextes
kubectl config view --flatten > /tmp/combined-kubeconfig.yaml
kubectl create secret generic tls-sync-kubeconfig \
--from-file=config=/tmp/combined-kubeconfig.yaml \
-n certificates-ops \
--dry-run=client -o yaml | kubectl apply -f -
```
**Important** : Assurez-vous que le fichier dans le Secret s'appelle `config` pour que kubectl le trouve automatiquement dans `/root/.kube/config`.
### 2. Créer le ConfigMap avec le script
Le script de synchronisation doit être disponible dans un ConfigMap :
```bash
kubectl create configmap tls-sync-wildcard-script \
--from-file=sync-all-certificates.sh=../../scripts/sync-all-certificates.sh \
-n certificates-ops \
--dry-run=client -o yaml | kubectl apply -f -
```
### 3. Déployer via ArgoCD
Créez un ApplicationSet ou une Application ArgoCD pour déployer ce chart :
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: tls-sync-wildcard
namespace: argocd-ops
spec:
project: default
source:
repoURL: https://git.gkdomaine.fr/kubernetes/argocd.git
targetRevision: main
path: helm/tls-sync-wildcard/ops
destination:
name: cluster-ops
namespace: certificates-ops
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
```
### 4. Déploiement manuel (alternative)
```bash
helm install tls-sync-wildcard ./helm/tls-sync-wildcard/ops \
-n certificates-ops \
--create-namespace
```
## Configuration
### Modifier la fréquence de synchronisation
Éditez `helm/tls-sync-wildcard/ops/values.yaml` :
```yaml
tlsSync:
schedule: "0 */2 * * *" # Toutes les 2 heures
# Autres exemples :
# "0 * * * *" - Toutes les heures
# "*/30 * * * *" - Toutes les 30 minutes
# "0 0 * * *" - Tous les jours à minuit
```
### Ajouter des namespaces cibles
Pour ajouter d'autres namespaces qui utilisent le certificat wildcard, modifiez la fonction `get_wildcard_target_namespaces` dans `scripts/sync-all-certificates.sh` :
```bash
case "$env" in
dev)
WILDCARD_TARGET_NAMESPACES=("headlamp-dev" "homarr-dev" "longhorn-dev" "autre-app-dev")
;;
# ...
esac
```
## Vérification
```bash
# Vérifier le CronJob
kubectl get cronjob -n certificates-ops tls-sync-wildcard
# Vérifier les Jobs créés
kubectl get jobs -n certificates-ops -l app=tls-sync-wildcard
# Voir les logs du dernier Job
kubectl logs -n certificates-ops -l app=tls-sync-wildcard --tail=100
# Déclencher manuellement une synchronisation
kubectl create job --from=cronjob/tls-sync-wildcard tls-sync-wildcard-manual-$(date +%s) -n certificates-ops
```
## Dépannage
### Le CronJob ne se déclenche pas
```bash
# Vérifier le schedule
kubectl get cronjob tls-sync-wildcard -n certificates-ops -o yaml | grep schedule
# Vérifier les événements
kubectl get events -n certificates-ops --sort-by='.lastTimestamp' | grep tls-sync-wildcard
```
### Les Jobs échouent
```bash
# Voir les logs du dernier Job
kubectl logs -n certificates-ops -l app=tls-sync-wildcard --tail=100
# Vérifier les erreurs
kubectl describe job -n certificates-ops -l app=tls-sync-wildcard
```
### Erreur "context not found"
Le Secret `tls-sync-kubeconfig` n'est pas correctement configuré ou les contextes kubectl ne sont pas disponibles.
```bash
# Vérifier le Secret
kubectl get secret tls-sync-kubeconfig -n certificates-ops -o yaml
# Tester l'accès depuis un pod
kubectl run -it --rm debug --image=bitnami/kubectl:1.31 --restart=Never \
-n certificates-ops \
--overrides='
{
"spec": {
"containers": [{
"name": "debug",
"image": "bitnami/kubectl:1.31",
"volumeMounts": [{
"name": "kubeconfig",
"mountPath": "/root/.kube",
"readOnly": true
}]
}],
"volumes": [{
"name": "kubeconfig",
"secret": {
"secretName": "tls-sync-kubeconfig"
}
}]
}
}' \
-- kubectl config get-contexts
```
## Architecture
```
┌─────────────────┐
│ Cluster OPS │
│ │
│ cert-manager │─── Génère les certificats Let's Encrypt
│ │
│ Certificates │─── Crée les secrets TLS dans certificates-ops
│ │
│ CronJob │─── Synchronise automatiquement les secrets
│ tls-sync- │ wildcard vers tous les namespaces nécessaires
│ wildcard │
└─────────────────┘
├───► Cluster DEV
│ ├─── headlamp-dev (wildcard-dev-tls)
│ ├─── homarr-dev (wildcard-dev-tls)
│ └─── longhorn-dev (wildcard-dev-tls)
├───► Cluster RCT
│ └─── (même principe)
└───► Cluster PRD
└─── (même principe)
```

View File

@@ -0,0 +1,388 @@
#!/bin/bash
# Script pour synchroniser automatiquement tous les secrets TLS depuis les certificats dans OPS
# Usage: ./sync-all-certificates.sh [--sourceCluster "cluster-name"] [--sourceNS "certificates-ops"]
set -e
# Valeurs par défaut
SOURCE_CLUSTER="${SOURCE_CLUSTER:-}"
SOURCE_NS="${SOURCE_NS:-certificates-ops}"
# Parsing des arguments
while [[ $# -gt 0 ]]; do
case $1 in
--sourceCluster)
SOURCE_CLUSTER="$2"
shift 2
;;
--sourceNS)
SOURCE_NS="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --sourceCluster NAME Nom du cluster source (défaut: contexte actuel)"
echo " --sourceNS NAME Namespace source (défaut: certificates-ops)"
echo " --help, -h Afficher cette aide"
echo ""
echo "Ce script récupère automatiquement tous les certificats depuis OPS"
echo "et synchronise les secrets TLS vers les clusters appropriés."
exit 0
;;
*)
echo "❌ Option inconnue: $1"
echo "Utilisez --help pour voir l'aide"
exit 1
;;
esac
done
echo "=== Synchronisation automatique des secrets TLS ==="
echo "Source: $SOURCE_CLUSTER (namespace: $SOURCE_NS)"
echo ""
# Fonction pour déterminer les namespaces cibles pour un certificat wildcard
# Les certificats wildcard doivent être copiés dans tous les namespaces qui les utilisent
get_wildcard_target_namespaces() {
local cert_name=$1
local env=""
# Extraire l'environnement depuis le nom du certificat
if [[ $cert_name =~ wildcard-dev-tls ]]; then
env="dev"
elif [[ $cert_name =~ wildcard-rct-tls ]]; then
env="rct"
elif [[ $cert_name =~ wildcard-prd-tls ]]; then
env="prd"
else
return 1
fi
# Liste des namespaces qui utilisent le certificat wildcard pour cet environnement
# Ajoutez ici tous les namespaces qui référencent ce secret dans leurs Ingress
case "$env" in
dev)
WILDCARD_TARGET_NAMESPACES=("headlamp-dev" "homarr-dev" "longhorn-dev")
;;
rct)
WILDCARD_TARGET_NAMESPACES=("headlamp-rct" "homarr-rct" "longhorn-rct")
;;
prd)
WILDCARD_TARGET_NAMESPACES=("headlamp-prd" "homarr-prd" "longhorn-prd")
;;
*)
return 1
;;
esac
return 0
}
# Fonction pour déterminer le cluster et namespace cible à partir du nom du certificat
# Format attendu: <app>-<env>-tls
# Exemple: homarr-dev-tls -> cluster-dev, namespace homarr-dev
# Pour les certificats wildcard, cette fonction détermine seulement le cluster
determine_target() {
local cert_name=$1
# Détecter les certificats wildcard
if [[ $cert_name =~ ^wildcard- ]]; then
# Pour les certificats wildcard, on détermine seulement le cluster
# Les namespaces seront déterminés par get_wildcard_target_namespaces
if [[ $cert_name =~ -dev-tls$ ]]; then
TARGET_CLUSTER="cluster-dev"
TARGET_NS="" # Sera rempli par get_wildcard_target_namespaces
IS_WILDCARD=true
elif [[ $cert_name =~ -rct-tls$ ]]; then
TARGET_CLUSTER="cluster-rct"
TARGET_NS=""
IS_WILDCARD=true
elif [[ $cert_name =~ -prd-tls$ ]]; then
TARGET_CLUSTER="cluster-prd"
TARGET_NS=""
IS_WILDCARD=true
else
echo "⚠️ Impossible de déterminer le cluster cible pour $cert_name"
return 1
fi
else
# Certificats normaux (non-wildcard)
IS_WILDCARD=false
# Extraire l'environnement (dev, rct, prd)
if [[ $cert_name =~ -dev-tls$ ]]; then
TARGET_CLUSTER="cluster-dev"
TARGET_NS="${cert_name%-tls}" # Garde le suffixe -dev
elif [[ $cert_name =~ -rct-tls$ ]]; then
TARGET_CLUSTER="cluster-rct"
TARGET_NS="${cert_name%-tls}" # Garde le suffixe -rct
elif [[ $cert_name =~ -prd-tls$ ]]; then
TARGET_CLUSTER="cluster-prd"
TARGET_NS="${cert_name%-tls}" # Garde le suffixe -prd
else
# Par défaut, essayer de deviner depuis le nom
# Si le nom contient "dev", utiliser cluster-dev
if [[ $cert_name == *"dev"* ]]; then
TARGET_CLUSTER="cluster-dev"
TARGET_NS="${cert_name%-tls}"
else
echo "⚠️ Impossible de déterminer le cluster cible pour $cert_name"
return 1
fi
fi
fi
return 0
}
# Déterminer le contexte à utiliser
echo "1. Détermination du contexte kubectl..."
if [ -z "$SOURCE_CLUSTER" ] || [ "$SOURCE_CLUSTER" == "in-cluster" ]; then
# Utiliser le contexte actuel
SOURCE_CLUSTER=$(kubectl config current-context 2>/dev/null)
if [ -z "$SOURCE_CLUSTER" ]; then
echo "❌ Erreur: Aucun contexte kubectl actuel trouvé"
echo " Contextes disponibles:"
kubectl config get-contexts -o name 2>/dev/null || echo " (aucun contexte trouvé)"
exit 1
fi
echo " Utilisation du contexte actuel: $SOURCE_CLUSTER"
else
# Vérifier que le contexte spécifié existe
if ! kubectl config get-contexts "$SOURCE_CLUSTER" &>/dev/null; then
echo "❌ Erreur: Le contexte kubectl '$SOURCE_CLUSTER' n'existe pas"
echo " Contextes disponibles:"
kubectl config get-contexts -o name 2>/dev/null || echo " (aucun contexte trouvé)"
exit 1
fi
echo " Utilisation du contexte: $SOURCE_CLUSTER"
fi
# Vérifier l'accès au cluster
echo "2. Vérification de l'accès au cluster..."
if [ -z "$SOURCE_CLUSTER" ]; then
# Pas de contexte spécifié, utiliser le contexte actuel
if ! kubectl get nodes &>/dev/null; then
echo "❌ Erreur: Impossible d'accéder au cluster"
echo " Vérifiez votre configuration kubectl et votre connexion réseau"
exit 1
fi
else
# Utiliser le contexte spécifié
if ! kubectl get nodes --context="$SOURCE_CLUSTER" &>/dev/null; then
echo "❌ Erreur: Impossible d'accéder au cluster $SOURCE_CLUSTER"
echo " Vérifiez votre configuration kubectl et votre connexion réseau"
exit 1
fi
fi
# Récupérer tous les certificats
echo "3. Récupération des certificats depuis $SOURCE_CLUSTER..."
if [ -z "$SOURCE_CLUSTER" ]; then
CERTIFICATES=$(kubectl get certificates -n "$SOURCE_NS" -o json 2>&1)
else
CERTIFICATES=$(kubectl get certificates -n "$SOURCE_NS" --context="$SOURCE_CLUSTER" -o json 2>&1)
fi
KUBECTL_EXIT_CODE=$?
if [ $KUBECTL_EXIT_CODE -ne 0 ]; then
echo "❌ Erreur lors de la récupération des certificats:"
echo "$CERTIFICATES" | head -5
exit 1
fi
if [ -z "$CERTIFICATES" ] || [ "$CERTIFICATES" == "null" ] || [ "$(echo "$CERTIFICATES" | jq -r '.items // empty')" == "" ]; then
echo "⚠️ Aucun certificat trouvé dans $SOURCE_NS sur $SOURCE_CLUSTER"
echo " Vérifiez que les certificats existent avec:"
if [ -z "$SOURCE_CLUSTER" ]; then
echo " kubectl get certificates -n $SOURCE_NS"
else
echo " kubectl get certificates -n $SOURCE_NS --context=$SOURCE_CLUSTER"
fi
exit 0
fi
# Vérifier que jq est installé
if ! command -v jq &> /dev/null; then
echo "❌ Erreur: jq n'est pas installé"
echo " Installez jq avec: sudo apt-get install jq (Ubuntu/Debian) ou brew install jq (macOS)"
exit 1
fi
# Extraire les certificats et leurs informations
CERT_COUNT=$(echo "$CERTIFICATES" | jq -r '.items | length' 2>/dev/null)
if [ -z "$CERT_COUNT" ] || [ "$CERT_COUNT" == "null" ] || [ "$CERT_COUNT" == "0" ]; then
echo "⚠️ Aucun certificat trouvé dans $SOURCE_NS sur $SOURCE_CLUSTER"
exit 0
fi
echo " Trouvé $CERT_COUNT certificat(s)"
echo ""
# Traiter chaque certificat
SUCCESS_COUNT=0
SKIP_COUNT=0
ERROR_COUNT=0
for i in $(seq 0 $((CERT_COUNT - 1))); do
CERT_NAME=$(echo "$CERTIFICATES" | jq -r ".items[$i].metadata.name")
SECRET_NAME=$(echo "$CERTIFICATES" | jq -r ".items[$i].spec.secretName")
CERT_NAMESPACE=$(echo "$CERTIFICATES" | jq -r ".items[$i].metadata.namespace")
IS_WILDCARD=false # Initialisation par défaut
# Si secretName n'est pas défini, utiliser le nom du certificat
if [ "$SECRET_NAME" == "null" ] || [ -z "$SECRET_NAME" ]; then
SECRET_NAME="$CERT_NAME"
fi
echo "📋 Traitement du certificat: $CERT_NAME"
echo " Secret: $SECRET_NAME"
# Déterminer le cluster et namespace cible
if ! determine_target "$CERT_NAME"; then
echo " ⚠️ Ignoré (impossible de déterminer la destination)"
SKIP_COUNT=$((SKIP_COUNT + 1))
echo ""
continue
fi
# Vérifier que le secret existe dans la source
if [ -z "$SOURCE_CLUSTER" ]; then
SECRET_CHECK_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\""
else
SECRET_CHECK_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\" --context=\"$SOURCE_CLUSTER\""
fi
if ! eval "$SECRET_CHECK_CMD" &>/dev/null; then
echo " ⚠️ Le secret $SECRET_NAME n'existe pas encore (certificat peut-être en cours de génération)"
SKIP_COUNT=$((SKIP_COUNT + 1))
echo ""
continue
fi
# Récupérer le secret une seule fois
TEMP_FILE=$(mktemp)
if [ -z "$SOURCE_CLUSTER" ]; then
SECRET_GET_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\" -o yaml"
else
SECRET_GET_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\" --context=\"$SOURCE_CLUSTER\" -o yaml"
fi
if ! eval "$SECRET_GET_CMD" > "$TEMP_FILE" 2>/dev/null; then
echo " ❌ Erreur: Impossible de récupérer le secret"
rm -f "$TEMP_FILE"
ERROR_COUNT=$((ERROR_COUNT + 1))
echo ""
continue
fi
# Traitement spécial pour les certificats wildcard
if [ "$IS_WILDCARD" = true ]; then
echo " 🌐 Certificat wildcard détecté"
# Obtenir la liste des namespaces cibles
if ! get_wildcard_target_namespaces "$CERT_NAME"; then
echo " ⚠️ Impossible de déterminer les namespaces cibles pour le wildcard"
rm -f "$TEMP_FILE"
SKIP_COUNT=$((SKIP_COUNT + 1))
echo ""
continue
fi
echo " Destination: $TARGET_CLUSTER (namespaces: ${WILDCARD_TARGET_NAMESPACES[*]})"
# Copier le secret dans chaque namespace cible
for TARGET_NS in "${WILDCARD_TARGET_NAMESPACES[@]}"; do
echo " → Synchronisation vers namespace: $TARGET_NS"
# Créer une copie temporaire du fichier pour ce namespace
TEMP_FILE_NS=$(mktemp)
cp "$TEMP_FILE" "$TEMP_FILE_NS"
# Modifier les métadonnées pour ce namespace
sed -i.bak '/^ uid:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ resourceVersion:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ selfLink:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ creationTimestamp:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak "s/namespace: $SOURCE_NS/namespace: $TARGET_NS/" "$TEMP_FILE_NS" 2>/dev/null || true
rm -f "${TEMP_FILE_NS}.bak" 2>/dev/null || true
# Créer le namespace s'il n'existe pas
kubectl create namespace "$TARGET_NS" --context="$TARGET_CLUSTER" --dry-run=client -o yaml | kubectl apply --context="$TARGET_CLUSTER" -f - >/dev/null 2>&1 || true
# Appliquer le secret
if kubectl apply -f "$TEMP_FILE_NS" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
# Vérifier que le secret existe maintenant
if kubectl get secret "$SECRET_NAME" -n "$TARGET_NS" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
echo " ✅ Synchronisé avec succès dans $TARGET_NS"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo " ❌ Erreur: Le secret n'a pas été créé dans $TARGET_NS"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
else
echo " ❌ Erreur lors de l'application du secret dans $TARGET_NS"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
# Nettoyage
rm -f "$TEMP_FILE_NS" "${TEMP_FILE_NS}.bak" 2>/dev/null || true
done
else
# Certificats normaux (non-wildcard) - comportement original
echo " Destination: $TARGET_CLUSTER (namespace: $TARGET_NS)"
echo " Synchronisation en cours..."
# Modifier les métadonnées
sed -i.bak '/^ uid:/d' "$TEMP_FILE" 2>/dev/null || true
sed -i.bak '/^ resourceVersion:/d' "$TEMP_FILE" 2>/dev/null || true
sed -i.bak '/^ selfLink:/d' "$TEMP_FILE" 2>/dev/null || true
sed -i.bak '/^ creationTimestamp:/d' "$TEMP_FILE" 2>/dev/null || true
sed -i.bak "s/namespace: $SOURCE_NS/namespace: $TARGET_NS/" "$TEMP_FILE" 2>/dev/null || true
rm -f "${TEMP_FILE}.bak" 2>/dev/null || true
# Créer le namespace s'il n'existe pas
kubectl create namespace "$TARGET_NS" --context="$TARGET_CLUSTER" --dry-run=client -o yaml | kubectl apply --context="$TARGET_CLUSTER" -f - >/dev/null 2>&1 || true
# Appliquer le secret
if kubectl apply -f "$TEMP_FILE" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
# Vérifier que le secret existe maintenant
if kubectl get secret "$SECRET_NAME" -n "$TARGET_NS" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
echo " ✅ Synchronisé avec succès"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo " ❌ Erreur: Le secret n'a pas été créé"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
else
echo " ❌ Erreur lors de l'application du secret"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
fi
# Nettoyage
rm -f "$TEMP_FILE" "${TEMP_FILE}.bak" 2>/dev/null || true
echo ""
done
# Résumé
echo "=== Résumé ==="
echo "✅ Synchronisés avec succès: $SUCCESS_COUNT"
echo "⚠️ Ignorés: $SKIP_COUNT"
echo "❌ Erreurs: $ERROR_COUNT"
echo ""
if [ $ERROR_COUNT -eq 0 ] && [ $SUCCESS_COUNT -gt 0 ]; then
echo "🎉 Toutes les synchronisations réussies !"
exit 0
elif [ $ERROR_COUNT -gt 0 ]; then
echo "⚠️ Certaines synchronisations ont échoué"
exit 1
else
echo " Aucune synchronisation effectuée"
exit 0
fi

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: tls-sync-wildcard-script
namespace: {{ .Values.tlsSync.sourceNamespace }}
labels:
app: tls-sync-wildcard
data:
sync-all-certificates.sh: |
{{- .Files.Get "scripts/sync-all-certificates.sh" | indent 4 }}

View File

@@ -0,0 +1,61 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: tls-sync-wildcard
namespace: {{ .Values.tlsSync.sourceNamespace }}
labels:
app: tls-sync-wildcard
spec:
schedule: {{ .Values.tlsSync.schedule | quote }}
successfulJobsHistoryLimit: {{ .Values.tlsSync.successfulJobsHistoryLimit }}
failedJobsHistoryLimit: {{ .Values.tlsSync.failedJobsHistoryLimit }}
jobTemplate:
spec:
backoffLimit: {{ .Values.tlsSync.backoffLimit }}
activeDeadlineSeconds: {{ .Values.tlsSync.activeDeadlineSeconds }}
template:
metadata:
labels:
app: tls-sync-wildcard
spec:
restartPolicy: {{ .Values.tlsSync.restartPolicy }}
serviceAccountName: tls-sync-wildcard
containers:
- name: sync
image: {{ .Values.tlsSync.image.repository }}:{{ .Values.tlsSync.image.tag }}
imagePullPolicy: {{ .Values.tlsSync.image.pullPolicy }}
command:
- /bin/bash
- -c
- |
set -e
# Copier le script depuis le ConfigMap
cp /scripts/sync-all-certificates.sh /tmp/sync-all-certificates.sh
chmod +x /tmp/sync-all-certificates.sh
# Exécuter le script
/tmp/sync-all-certificates.sh \
--sourceCluster "{{ .Values.tlsSync.sourceCluster }}" \
--sourceNS "{{ .Values.tlsSync.sourceNamespace }}"
volumeMounts:
- name: kubeconfig
mountPath: /root/.kube
readOnly: true
- name: script
mountPath: /scripts
readOnly: true
env:
- name: KUBECONFIG
value: /root/.kube/config
resources:
{{- toYaml .Values.tlsSync.resources | nindent 14 }}
volumes:
- name: kubeconfig
secret:
secretName: {{ .Values.tlsSync.kubeconfigSecret }}
- name: script
configMap:
name: tls-sync-wildcard-script
defaultMode: 0755

View File

@@ -0,0 +1,51 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tls-sync-wildcard
labels:
app: tls-sync-wildcard
rules:
# Permissions pour lire les certificats et secrets dans le namespace source
- apiGroups:
- cert-manager.io
resources:
- certificates
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
# Permissions pour créer et gérer les secrets dans tous les namespaces
- apiGroups:
- ""
resources:
- secrets
- namespaces
verbs:
- get
- list
- create
- update
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tls-sync-wildcard
labels:
app: tls-sync-wildcard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tls-sync-wildcard
subjects:
- kind: ServiceAccount
name: tls-sync-wildcard
namespace: {{ .Values.tlsSync.sourceNamespace }}

View File

@@ -0,0 +1,8 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: tls-sync-wildcard
namespace: {{ .Values.tlsSync.sourceNamespace }}
labels:
app: tls-sync-wildcard

View File

@@ -0,0 +1,41 @@
# Configuration pour le CronJob de synchronisation des secrets TLS wildcard
tlsSync:
# Schedule Cron pour la synchronisation (toutes les heures par défaut)
schedule: "0 * * * *" # Toutes les heures
# Cluster source (où les certificats sont générés)
sourceCluster: "cluster-ops"
sourceNamespace: "certificates-ops"
# Image à utiliser (doit contenir kubectl et bash)
image:
repository: bitnami/kubectl
tag: "1.31"
pullPolicy: IfNotPresent
# Ressources
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
# Nom du secret contenant le kubeconfig
# Ce secret doit être créé manuellement avec tous les contextes kubectl
kubeconfigSecret: "tls-sync-kubeconfig"
# Restart policy pour les Jobs
restartPolicy: OnFailure
# Nombre de tentatives en cas d'échec
backoffLimit: 3
# Timeout pour les Jobs (en secondes)
activeDeadlineSeconds: 600
# Historique des Jobs à conserver
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3

View File

@@ -43,12 +43,73 @@ echo "=== Synchronisation automatique des secrets TLS ==="
echo "Source: $SOURCE_CLUSTER (namespace: $SOURCE_NS)"
echo ""
# Fonction pour déterminer les namespaces cibles pour un certificat wildcard
# Les certificats wildcard doivent être copiés dans tous les namespaces qui les utilisent
get_wildcard_target_namespaces() {
local cert_name=$1
local env=""
# Extraire l'environnement depuis le nom du certificat
if [[ $cert_name =~ wildcard-dev-tls ]]; then
env="dev"
elif [[ $cert_name =~ wildcard-rct-tls ]]; then
env="rct"
elif [[ $cert_name =~ wildcard-prd-tls ]]; then
env="prd"
else
return 1
fi
# Liste des namespaces qui utilisent le certificat wildcard pour cet environnement
# Ajoutez ici tous les namespaces qui référencent ce secret dans leurs Ingress
case "$env" in
dev)
WILDCARD_TARGET_NAMESPACES=("headlamp-dev" "homarr-dev" "longhorn-dev")
;;
rct)
WILDCARD_TARGET_NAMESPACES=("headlamp-rct" "homarr-rct" "longhorn-rct")
;;
prd)
WILDCARD_TARGET_NAMESPACES=("headlamp-prd" "homarr-prd" "longhorn-prd")
;;
*)
return 1
;;
esac
return 0
}
# Fonction pour déterminer le cluster et namespace cible à partir du nom du certificat
# Format attendu: <app>-<env>-tls
# Exemple: homarr-dev-tls -> cluster-dev, namespace homarr-dev
# Pour les certificats wildcard, cette fonction détermine seulement le cluster
determine_target() {
local cert_name=$1
# Détecter les certificats wildcard
if [[ $cert_name =~ ^wildcard- ]]; then
# Pour les certificats wildcard, on détermine seulement le cluster
# Les namespaces seront déterminés par get_wildcard_target_namespaces
if [[ $cert_name =~ -dev-tls$ ]]; then
TARGET_CLUSTER="cluster-dev"
TARGET_NS="" # Sera rempli par get_wildcard_target_namespaces
IS_WILDCARD=true
elif [[ $cert_name =~ -rct-tls$ ]]; then
TARGET_CLUSTER="cluster-rct"
TARGET_NS=""
IS_WILDCARD=true
elif [[ $cert_name =~ -prd-tls$ ]]; then
TARGET_CLUSTER="cluster-prd"
TARGET_NS=""
IS_WILDCARD=true
else
echo "⚠️ Impossible de déterminer le cluster cible pour $cert_name"
return 1
fi
else
# Certificats normaux (non-wildcard)
IS_WILDCARD=false
# Extraire l'environnement (dev, rct, prd)
if [[ $cert_name =~ -dev-tls$ ]]; then
TARGET_CLUSTER="cluster-dev"
@@ -70,6 +131,7 @@ determine_target() {
return 1
fi
fi
fi
return 0
}
@@ -167,6 +229,7 @@ for i in $(seq 0 $((CERT_COUNT - 1))); do
CERT_NAME=$(echo "$CERTIFICATES" | jq -r ".items[$i].metadata.name")
SECRET_NAME=$(echo "$CERTIFICATES" | jq -r ".items[$i].spec.secretName")
CERT_NAMESPACE=$(echo "$CERTIFICATES" | jq -r ".items[$i].metadata.namespace")
IS_WILDCARD=false # Initialisation par défaut
# Si secretName n'est pas défini, utiliser le nom du certificat
if [ "$SECRET_NAME" == "null" ] || [ -z "$SECRET_NAME" ]; then
@@ -184,8 +247,6 @@ for i in $(seq 0 $((CERT_COUNT - 1))); do
continue
fi
echo " Destination: $TARGET_CLUSTER (namespace: $TARGET_NS)"
# Vérifier que le secret existe dans la source
if [ -z "$SOURCE_CLUSTER" ]; then
SECRET_CHECK_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\""
@@ -200,10 +261,7 @@ for i in $(seq 0 $((CERT_COUNT - 1))); do
continue
fi
# Synchroniser le secret
echo " Synchronisation en cours..."
# Récupérer le secret
# Récupérer le secret une seule fois
TEMP_FILE=$(mktemp)
if [ -z "$SOURCE_CLUSTER" ]; then
SECRET_GET_CMD="kubectl get secret \"$SECRET_NAME\" -n \"$SOURCE_NS\" -o yaml"
@@ -219,6 +277,63 @@ for i in $(seq 0 $((CERT_COUNT - 1))); do
continue
fi
# Traitement spécial pour les certificats wildcard
if [ "$IS_WILDCARD" = true ]; then
echo " 🌐 Certificat wildcard détecté"
# Obtenir la liste des namespaces cibles
if ! get_wildcard_target_namespaces "$CERT_NAME"; then
echo " ⚠️ Impossible de déterminer les namespaces cibles pour le wildcard"
rm -f "$TEMP_FILE"
SKIP_COUNT=$((SKIP_COUNT + 1))
echo ""
continue
fi
echo " Destination: $TARGET_CLUSTER (namespaces: ${WILDCARD_TARGET_NAMESPACES[*]})"
# Copier le secret dans chaque namespace cible
for TARGET_NS in "${WILDCARD_TARGET_NAMESPACES[@]}"; do
echo " → Synchronisation vers namespace: $TARGET_NS"
# Créer une copie temporaire du fichier pour ce namespace
TEMP_FILE_NS=$(mktemp)
cp "$TEMP_FILE" "$TEMP_FILE_NS"
# Modifier les métadonnées pour ce namespace
sed -i.bak '/^ uid:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ resourceVersion:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ selfLink:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak '/^ creationTimestamp:/d' "$TEMP_FILE_NS" 2>/dev/null || true
sed -i.bak "s/namespace: $SOURCE_NS/namespace: $TARGET_NS/" "$TEMP_FILE_NS" 2>/dev/null || true
rm -f "${TEMP_FILE_NS}.bak" 2>/dev/null || true
# Créer le namespace s'il n'existe pas
kubectl create namespace "$TARGET_NS" --context="$TARGET_CLUSTER" --dry-run=client -o yaml | kubectl apply --context="$TARGET_CLUSTER" -f - >/dev/null 2>&1 || true
# Appliquer le secret
if kubectl apply -f "$TEMP_FILE_NS" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
# Vérifier que le secret existe maintenant
if kubectl get secret "$SECRET_NAME" -n "$TARGET_NS" --context="$TARGET_CLUSTER" >/dev/null 2>&1; then
echo " ✅ Synchronisé avec succès dans $TARGET_NS"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo " ❌ Erreur: Le secret n'a pas été créé dans $TARGET_NS"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
else
echo " ❌ Erreur lors de l'application du secret dans $TARGET_NS"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
# Nettoyage
rm -f "$TEMP_FILE_NS" "${TEMP_FILE_NS}.bak" 2>/dev/null || true
done
else
# Certificats normaux (non-wildcard) - comportement original
echo " Destination: $TARGET_CLUSTER (namespace: $TARGET_NS)"
echo " Synchronisation en cours..."
# Modifier les métadonnées
sed -i.bak '/^ uid:/d' "$TEMP_FILE" 2>/dev/null || true
sed -i.bak '/^ resourceVersion:/d' "$TEMP_FILE" 2>/dev/null || true
@@ -244,6 +359,7 @@ for i in $(seq 0 $((CERT_COUNT - 1))); do
echo " ❌ Erreur lors de l'application du secret"
ERROR_COUNT=$((ERROR_COUNT + 1))
fi
fi
# Nettoyage
rm -f "$TEMP_FILE" "${TEMP_FILE}.bak" 2>/dev/null || true