chore: build
This commit is contained in:
@@ -813,8 +813,9 @@ func (h *HelmProvisioner) deleteNamespace(name string) error {
|
|||||||
const veleroNamespace = "velero"
|
const veleroNamespace = "velero"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
veleroScheduleGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "schedules"}
|
veleroScheduleGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "schedules"}
|
||||||
veleroBackupGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backups"}
|
veleroBackupGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backups"}
|
||||||
|
veleroBackupRepoGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backuprepositories"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// demoBackupScheduleName : nom du Schedule Velero dédié à ce namespace de
|
// demoBackupScheduleName : nom du Schedule Velero dédié à ce namespace de
|
||||||
@@ -857,14 +858,24 @@ func (h *HelmProvisioner) ensureDemoBackupSchedule(namespace string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteDemoBackups supprime le Schedule Velero de cette démo ainsi que
|
// deleteDemoBackups supprime le Schedule Velero de cette démo, toutes les
|
||||||
// toutes les sauvegardes déjà prises par ce planning (la suppression d'un
|
// sauvegardes déjà prises par ce planning, et le BackupRepository associé
|
||||||
// Backup déclenche le nettoyage asynchrone des données Kopia associées côté
|
// (la suppression d'un Backup déclenche le nettoyage asynchrone des données
|
||||||
// Velero — pas de suppression directe dans le bucket S3, qui risquerait de
|
// Kopia associées côté Velero — pas de suppression directe dans le bucket
|
||||||
// casser le dépôt Kopia partagé par les autres démos). Appelée depuis
|
// S3, qui risquerait de casser le dépôt Kopia partagé par les autres
|
||||||
// Teardown (expiration TTL et destruction forcée passent toutes les deux
|
// démos). Appelée depuis Teardown (expiration TTL et destruction forcée
|
||||||
// par ce même chemin) et lors d'une migration premium réussie (l'ancien
|
// passent toutes les deux par ce même chemin) et lors d'une migration
|
||||||
// namespace disparaît, son planning de sauvegarde n'a plus de sens).
|
// premium réussie (l'ancien namespace disparaît, son planning de sauvegarde
|
||||||
|
// n'a plus de sens).
|
||||||
|
//
|
||||||
|
// Le BackupRepository (l'objet qui référence le dépôt Kopia du namespace,
|
||||||
|
// nommé "<namespace>-default-kopia") n'est PAS supprimé automatiquement par
|
||||||
|
// Velero quand ses Backup disparaissent — sans ce nettoyage explicite, il
|
||||||
|
// reste indéfiniment et Velero continue de lancer des jobs de maintenance
|
||||||
|
// périodiques dessus (kopia-maintain-job) pour un dépôt qui ne sert plus à
|
||||||
|
// rien. Supprimer l'objet ne touche pas aux données déjà écrites dans le
|
||||||
|
// bucket S3 (même logique que pour les Backup ci-dessus), juste le
|
||||||
|
// bookkeeping Velero.
|
||||||
func (h *HelmProvisioner) deleteDemoBackups(namespace string) error {
|
func (h *HelmProvisioner) deleteDemoBackups(namespace string) error {
|
||||||
name := demoBackupScheduleName(namespace)
|
name := demoBackupScheduleName(namespace)
|
||||||
|
|
||||||
@@ -886,6 +897,13 @@ func (h *HelmProvisioner) deleteDemoBackups(namespace string) error {
|
|||||||
if err != nil && !k8sErrors.IsNotFound(err) {
|
if err != nil && !k8sErrors.IsNotFound(err) {
|
||||||
return fmt.Errorf("suppression planning %s: %w", name, err)
|
return fmt.Errorf("suppression planning %s: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoName := namespace + "-default-kopia"
|
||||||
|
err = h.dynamicClient.Resource(veleroBackupRepoGVR).Namespace(veleroNamespace).
|
||||||
|
Delete(context.Background(), repoName, metav1.DeleteOptions{})
|
||||||
|
if err != nil && !k8sErrors.IsNotFound(err) {
|
||||||
|
return fmt.Errorf("suppression backuprepository %s: %w", repoName, err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
# Identifiants à récupérer dans le Hub Beszel : Add System > Universal token.
|
# Les nœuds k3s ne sont pas sur le VPN (voir omnex/ansible/inventory/host.ini)
|
||||||
# Le token universel expire au bout d'1h mais permet l'auto-enregistrement de
|
# et le dashboard Beszel (8090) doit rester accessible uniquement via le VPN
|
||||||
# TOUS les agents du DaemonSet sans les déclarer un par un dans le Hub ; une
|
# (10.0.0.0/24) — donc pas question de pointer HUB_URL dessus. Le Hub expose
|
||||||
# fois qu'un agent s'est enregistré, il garde son identité même après
|
# à la place un port dédié 10002 (nginx, restreint aux 4 IP publiques des
|
||||||
# l'expiration du token (donc pas besoin de le renouveler après coup).
|
# nœuds k3s-prod + chemin /api/ uniquement — voir
|
||||||
|
# ansible-mln/monitoring/security/nginx/nginx.conf et
|
||||||
|
# firewall-monitoring-uber.sh) : c'est CE port que les agents utilisent pour
|
||||||
|
# s'auto-enregistrer, jamais le dashboard.
|
||||||
|
#
|
||||||
|
# TOKEN : token universel généré dans le Hub (Add System > Universal token,
|
||||||
|
# expire au bout d'1h mais un agent déjà enregistré garde son identité).
|
||||||
|
# KEY : clé publique SSH du Hub (une seule pour tous les agents, quel que
|
||||||
|
# soit le système) — même valeur que celle déjà utilisée par l'agent
|
||||||
|
# s3-uber (voir ansible-mln/monitoring/s3-backup/docker-compose.yml).
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
@@ -10,6 +19,6 @@ metadata:
|
|||||||
namespace: monitoring
|
namespace: monitoring
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
HUB_URL: "" # ex: https://beszel.exemple.com
|
HUB_URL: "https://185.103.167.138:10002"
|
||||||
TOKEN: "" # token universel généré dans le Hub
|
TOKEN: "" # token universel généré dans le Hub
|
||||||
KEY: "" # clé publique SSH affichée par le Hub
|
KEY: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEwRVn66crRDX5iL0bxNOnrp3OhTiXHKs+COx/bo5vmQ beszel-agent"
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# Isolation réseau du namespace monitoring (beszel-agent) — deny-by-default +
|
# Isolation réseau du namespace monitoring (beszel-agent) — deny-by-default +
|
||||||
# allowlist :
|
# allowlist :
|
||||||
# - Egress : HTTPS sortant vers le Hub (HUB_URL, hôte externe arbitraire —
|
# - Egress : HTTPS sortant vers le Hub sur son port dédié agents (10002,
|
||||||
# voir beszel-agent-secret.yml) pour l'auto-enregistrement au démarrage.
|
# pas 8090/dashboard — voir beszel-agent-secret.yml et
|
||||||
|
# ansible-mln/monitoring/security/nginx/nginx.conf) pour
|
||||||
|
# l'auto-enregistrement au démarrage.
|
||||||
# - Ingress : le Hub se connecte à l'agent en SSH sur le port LISTEN
|
# - Ingress : le Hub se connecte à l'agent en SSH sur le port LISTEN
|
||||||
# (45876) pour récupérer les métriques — IP du Hub non fixe/connue
|
# (10002) pour récupérer les métriques — IP du Hub non fixe/connue
|
||||||
# d'avance, ouvert largement mais authentifié par TOKEN/KEY (voir
|
# d'avance, ouvert largement mais authentifié par TOKEN/KEY (voir
|
||||||
# beszel-agent-secret.yml), pas par l'origine réseau.
|
# beszel-agent-secret.yml), pas par l'origine réseau.
|
||||||
#
|
#
|
||||||
@@ -14,7 +16,7 @@
|
|||||||
# GlobalNetworkPolicy Calico, une ressource différente, pour un enforcement
|
# GlobalNetworkPolicy Calico, une ressource différente, pour un enforcement
|
||||||
# réel au niveau du nœud). Ce manifeste reste appliqué pour la correction
|
# réel au niveau du nœud). Ce manifeste reste appliqué pour la correction
|
||||||
# d'intention et si hostNetwork est retiré un jour, mais ne pas compter
|
# d'intention et si hostNetwork est retiré un jour, mais ne pas compter
|
||||||
# dessus comme protection effective du port 45876 en l'état — un pare-feu
|
# dessus comme protection effective du port 10002 en l'état — un pare-feu
|
||||||
# hôte (iptables/nftables/ufw) ou une GlobalNetworkPolicy Calico est
|
# hôte (iptables/nftables/ufw) ou une GlobalNetworkPolicy Calico est
|
||||||
# nécessaire pour une vraie restriction.
|
# nécessaire pour une vraie restriction.
|
||||||
|
|
||||||
@@ -43,7 +45,7 @@ spec:
|
|||||||
- from: []
|
- from: []
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 45876
|
port: 10002
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
@@ -86,4 +88,4 @@ spec:
|
|||||||
- 192.168.0.0/16
|
- 192.168.0.0/16
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 443
|
port: 10002
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ data:
|
|||||||
# mécanisme que docker/waf (omnex-prod).
|
# mécanisme que docker/waf (omnex-prod).
|
||||||
SecAuditLogParts ABIFHZ
|
SecAuditLogParts ABIFHZ
|
||||||
|
|
||||||
|
# 911100 (REQUEST-911-METHOD-ENFORCEMENT) n'autorise par défaut que GET
|
||||||
|
# HEAD POST OPTIONS — bloque tout DELETE/PUT/PATCH légitime de l'API
|
||||||
|
# REST. Même correctif appliqué côté docker/waf/custom-rules.conf
|
||||||
|
# (omnex-prod) et côté Coraza/Traefik (deploy/chart-gestion/traefik/
|
||||||
|
# templates/middleware-waf.yaml).
|
||||||
|
SecRuleRemoveById 911100
|
||||||
|
|
||||||
SecRule IP:BANNED "@eq 1" \
|
SecRule IP:BANNED "@eq 1" \
|
||||||
"id:100000,phase:1,deny,status:403,log,\
|
"id:100000,phase:1,deny,status:403,log,\
|
||||||
msg:'IP is banned'"
|
msg:'IP is banned'"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ spec:
|
|||||||
name: beszel-agent-hub
|
name: beszel-agent-hub
|
||||||
env:
|
env:
|
||||||
- name: LISTEN
|
- name: LISTEN
|
||||||
value: "45876"
|
value: "10002"
|
||||||
- name: SYSTEM_NAME
|
- name: SYSTEM_NAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
|
|||||||
@@ -137,6 +137,36 @@ roleRef:
|
|||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
name: omnex-namespace-manager
|
name: omnex-namespace-manager
|
||||||
---
|
---
|
||||||
|
# "nodes" n'est pas un type namespaced : impossible de le scoper via un
|
||||||
|
# RoleBinding par démo comme omnex-demo-workload-manager, d'où une
|
||||||
|
# ClusterRole/ClusterRoleBinding dédiée. Portée strictement à "get" sur le
|
||||||
|
# sous-type "nodes/proxy" — c'est tout ce dont checkPVCUsage a besoin
|
||||||
|
# (voir alerts/resources.go:fetchNodeSummary, équivalent de
|
||||||
|
# `kubectl get --raw /api/v1/nodes/<node>/proxy/stats/summary`) pour lire
|
||||||
|
# le remplissage des PVC. Aucun accès en écriture, aucun accès aux autres
|
||||||
|
# sous-ressources de Node.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: omnex-node-stats-reader
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [nodes/proxy]
|
||||||
|
verbs: [get]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: omnex-control-plane-node-stats
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: omnex-control-plane
|
||||||
|
namespace: omnex-system
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: omnex-node-stats-reader
|
||||||
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
@@ -166,8 +196,8 @@ type: kubernetes.io/service-account-token
|
|||||||
# namespace fixe préexistant, pas un namespace de démo créé dynamiquement
|
# namespace fixe préexistant, pas un namespace de démo créé dynamiquement
|
||||||
# par le control-plane — pas besoin du mécanisme "bind" utilisé pour
|
# par le control-plane — pas besoin du mécanisme "bind" utilisé pour
|
||||||
# omnex-demo-workload-manager). Portée strictement limitée aux Schedule/
|
# omnex-demo-workload-manager). Portée strictement limitée aux Schedule/
|
||||||
# Backup Velero — aucun accès aux Secrets (dont cloud-credentials, la clé
|
# Backup/BackupRepository Velero — aucun accès aux Secrets (dont
|
||||||
# du bucket S3) ni au reste du namespace velero.
|
# cloud-credentials, la clé du bucket S3) ni au reste du namespace velero.
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: Role
|
kind: Role
|
||||||
metadata:
|
metadata:
|
||||||
@@ -178,8 +208,10 @@ rules:
|
|||||||
# Un Schedule + les Backup qu'il crée par démo (voir
|
# Un Schedule + les Backup qu'il crée par démo (voir
|
||||||
# HelmProvisioner.ensureDemoBackupSchedule/deleteDemoBackups) — jamais
|
# HelmProvisioner.ensureDemoBackupSchedule/deleteDemoBackups) — jamais
|
||||||
# de wildcard multi-namespace, pour pouvoir supprimer les sauvegardes
|
# de wildcard multi-namespace, pour pouvoir supprimer les sauvegardes
|
||||||
# d'UNE démo sans toucher aux autres.
|
# d'UNE démo sans toucher aux autres. backuprepositories : nettoyage du
|
||||||
resources: [schedules, backups]
|
# dépôt Kopia du namespace détruit (deleteDemoBackups) — sans quoi
|
||||||
|
# Velero continue de lancer des jobs de maintenance dessus indéfiniment.
|
||||||
|
resources: [schedules, backups, backuprepositories]
|
||||||
verbs: [get, list, watch, create, delete]
|
verbs: [get, list, watch, create, delete]
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ export function Demos() {
|
|||||||
// Les démos passées en abonnement payant vivent désormais dans le
|
// Les démos passées en abonnement payant vivent désormais dans le
|
||||||
// dashboard Premium (n'expirent plus, stockage persistant) — ne plus
|
// dashboard Premium (n'expirent plus, stockage persistant) — ne plus
|
||||||
// les mélanger avec les démos d'essai ici.
|
// les mélanger avec les démos d'essai ici.
|
||||||
setDemos((res.items ?? []).filter((d) => d.type_abonnement !== 'premium'))
|
setDemos(
|
||||||
|
(res.items ?? []).filter((d) => d.type_abonnement !== 'premium' && d.status !== 'expired'),
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ApiError && err.status === 401) navigate('/admin/login')
|
if (err instanceof ApiError && err.status === 401) navigate('/admin/login')
|
||||||
else toast({ status: 'error', title: 'Chargement des démos impossible' })
|
else toast({ status: 'error', title: 'Chargement des démos impossible' })
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ export function PremiumDemos() {
|
|||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await api.listDemos()
|
const res = await api.listDemos()
|
||||||
setDemos((res.items ?? []).filter((d) => d.type_abonnement === 'premium'))
|
setDemos(
|
||||||
|
(res.items ?? []).filter((d) => d.type_abonnement === 'premium' && d.status !== 'expired'),
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ApiError && err.status === 401) navigate('/admin/login')
|
if (err instanceof ApiError && err.status === 401) navigate('/admin/login')
|
||||||
else toast({ status: 'error', title: 'Chargement des démos impossible' })
|
else toast({ status: 'error', title: 'Chargement des démos impossible' })
|
||||||
|
|||||||
Reference in New Issue
Block a user