raijin-server 0.3.4__py3-none-any.whl → 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of raijin-server might be problematic. Click here for more details.

@@ -0,0 +1,562 @@
1
+ """Utilitários centralizados para gerenciamento de usuários e políticas MinIO.
2
+
3
+ Este módulo implementa o princípio do menor privilégio para aplicações que
4
+ usam MinIO como backend de storage, criando usuários específicos com acesso
5
+ limitado aos buckets necessários.
6
+
7
+ Aplicações suportadas:
8
+ - vault-user → vault-storage bucket
9
+ - velero-user → velero-backups bucket
10
+ - harbor-user → harbor-registry, harbor-chartmuseum, harbor-jobservice buckets
11
+ - loki-user → loki-chunks, loki-ruler, loki-admin buckets (futuro)
12
+
13
+ Uso:
14
+ from raijin_server.minio_utils import get_or_create_minio_user
15
+
16
+ # Cria usuário específico para a aplicação
17
+ access_key, secret_key = get_or_create_minio_user(
18
+ ctx=ctx,
19
+ app_name="harbor",
20
+ buckets=["harbor-registry", "harbor-chartmuseum", "harbor-jobservice"],
21
+ )
22
+ """
23
+
24
+ import base64
25
+ import json
26
+ import secrets
27
+ import time
28
+ from pathlib import Path
29
+ from typing import Optional
30
+
31
+ import typer
32
+
33
+ from raijin_server.utils import ExecutionContext, run_cmd
34
+
35
+
36
+ # Configuração de usuários por aplicação
37
+ MINIO_APP_USERS = {
38
+ "vault": {
39
+ "username": "vault-user",
40
+ "buckets": ["vault-storage"],
41
+ "description": "Usuário para HashiCorp Vault backend storage",
42
+ },
43
+ "velero": {
44
+ "username": "velero-user",
45
+ "buckets": ["velero-backups"],
46
+ "description": "Usuário para Velero backup storage",
47
+ },
48
+ "harbor": {
49
+ "username": "harbor-user",
50
+ "buckets": ["harbor-registry", "harbor-chartmuseum", "harbor-jobservice"],
51
+ "description": "Usuário para Harbor container registry",
52
+ },
53
+ "loki": {
54
+ "username": "loki-user",
55
+ "buckets": ["loki-chunks", "loki-ruler", "loki-admin"],
56
+ "description": "Usuário para Loki logs storage",
57
+ },
58
+ }
59
+
60
+
61
+ def _generate_password(length: int = 32) -> str:
62
+ """Gera senha aleatória segura."""
63
+ return secrets.token_urlsafe(length)[:length]
64
+
65
+
66
+ def _get_minio_root_credentials(ctx: ExecutionContext) -> tuple[str, str]:
67
+ """Obtém credenciais root do MinIO do Secret do K8s."""
68
+ # Tenta secret 'minio' primeiro (padrão do Helm chart)
69
+ result = run_cmd(
70
+ ["kubectl", "-n", "minio", "get", "secret", "minio", "-o", "jsonpath={.data.rootUser}"],
71
+ ctx,
72
+ check=False,
73
+ )
74
+
75
+ if result.returncode == 0 and result.stdout:
76
+ root_user = base64.b64decode(result.stdout.strip()).decode("utf-8")
77
+
78
+ result = run_cmd(
79
+ ["kubectl", "-n", "minio", "get", "secret", "minio", "-o", "jsonpath={.data.rootPassword}"],
80
+ ctx,
81
+ check=False,
82
+ )
83
+
84
+ if result.returncode == 0 and result.stdout:
85
+ root_password = base64.b64decode(result.stdout.strip()).decode("utf-8")
86
+ return root_user, root_password
87
+
88
+ # Fallback para secret minio-credentials
89
+ result = run_cmd(
90
+ ["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.accesskey}"],
91
+ ctx,
92
+ check=False,
93
+ )
94
+
95
+ if result.returncode == 0 and result.stdout:
96
+ access_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
97
+
98
+ result = run_cmd(
99
+ ["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.secretkey}"],
100
+ ctx,
101
+ check=False,
102
+ )
103
+
104
+ if result.returncode == 0 and result.stdout:
105
+ secret_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
106
+ return access_key, secret_key
107
+
108
+ raise RuntimeError("Não foi possível obter credenciais root do MinIO. Verifique se o MinIO está instalado.")
109
+
110
+
111
+ def _setup_mc_alias(ctx: ExecutionContext, root_user: str, root_password: str) -> bool:
112
+ """Configura alias 'local' no mc dentro do pod MinIO."""
113
+ result = run_cmd(
114
+ [
115
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
116
+ "mc", "alias", "set", "local", "http://localhost:9000", root_user, root_password,
117
+ ],
118
+ ctx,
119
+ check=False,
120
+ )
121
+ return result.returncode == 0
122
+
123
+
124
+ def _check_user_exists(ctx: ExecutionContext, username: str) -> bool:
125
+ """Verifica se usuário já existe no MinIO."""
126
+ result = run_cmd(
127
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "admin", "user", "info", "local", username],
128
+ ctx,
129
+ check=False,
130
+ )
131
+ return result.returncode == 0
132
+
133
+
134
+ def _create_minio_user(ctx: ExecutionContext, username: str, password: str) -> bool:
135
+ """Cria usuário no MinIO."""
136
+ result = run_cmd(
137
+ [
138
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
139
+ "mc", "admin", "user", "add", "local", username, password,
140
+ ],
141
+ ctx,
142
+ check=False,
143
+ )
144
+ return result.returncode == 0
145
+
146
+
147
+ def _create_bucket_policy(ctx: ExecutionContext, policy_name: str, buckets: list[str]) -> bool:
148
+ """Cria política de acesso restrita aos buckets especificados."""
149
+ # Cria documento de policy S3
150
+ policy_doc = {
151
+ "Version": "2012-10-17",
152
+ "Statement": [
153
+ {
154
+ "Effect": "Allow",
155
+ "Action": [
156
+ "s3:GetBucketLocation",
157
+ "s3:ListBucket",
158
+ "s3:ListBucketMultipartUploads",
159
+ ],
160
+ "Resource": [f"arn:aws:s3:::{bucket}" for bucket in buckets],
161
+ },
162
+ {
163
+ "Effect": "Allow",
164
+ "Action": [
165
+ "s3:GetObject",
166
+ "s3:PutObject",
167
+ "s3:DeleteObject",
168
+ "s3:ListMultipartUploadParts",
169
+ "s3:AbortMultipartUpload",
170
+ ],
171
+ "Resource": [f"arn:aws:s3:::{bucket}/*" for bucket in buckets],
172
+ },
173
+ ],
174
+ }
175
+
176
+ policy_json = json.dumps(policy_doc)
177
+
178
+ # Salva policy em arquivo temporário dentro do pod
179
+ result = run_cmd(
180
+ [
181
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
182
+ "sh", "-c", f"echo '{policy_json}' > /tmp/{policy_name}.json",
183
+ ],
184
+ ctx,
185
+ check=False,
186
+ )
187
+
188
+ if result.returncode != 0:
189
+ return False
190
+
191
+ # Cria policy no MinIO
192
+ result = run_cmd(
193
+ [
194
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
195
+ "mc", "admin", "policy", "create", "local", policy_name, f"/tmp/{policy_name}.json",
196
+ ],
197
+ ctx,
198
+ check=False,
199
+ )
200
+
201
+ # Se policy já existe, atualiza
202
+ if result.returncode != 0:
203
+ result = run_cmd(
204
+ [
205
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
206
+ "mc", "admin", "policy", "remove", "local", policy_name,
207
+ ],
208
+ ctx,
209
+ check=False,
210
+ )
211
+ result = run_cmd(
212
+ [
213
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
214
+ "mc", "admin", "policy", "create", "local", policy_name, f"/tmp/{policy_name}.json",
215
+ ],
216
+ ctx,
217
+ check=False,
218
+ )
219
+
220
+ return result.returncode == 0
221
+
222
+
223
+ def _attach_policy_to_user(ctx: ExecutionContext, username: str, policy_name: str) -> bool:
224
+ """Associa política ao usuário."""
225
+ result = run_cmd(
226
+ [
227
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
228
+ "mc", "admin", "policy", "attach", "local", policy_name, "--user", username,
229
+ ],
230
+ ctx,
231
+ check=False,
232
+ )
233
+ return result.returncode == 0
234
+
235
+
236
+ def _create_bucket(ctx: ExecutionContext, bucket_name: str) -> bool:
237
+ """Cria bucket se não existir."""
238
+ # Verifica se bucket existe
239
+ result = run_cmd(
240
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "ls", f"local/{bucket_name}"],
241
+ ctx,
242
+ check=False,
243
+ )
244
+
245
+ if result.returncode == 0:
246
+ typer.echo(f" Bucket '{bucket_name}' já existe.")
247
+ return True
248
+
249
+ # Cria bucket
250
+ result = run_cmd(
251
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "mb", f"local/{bucket_name}"],
252
+ ctx,
253
+ check=False,
254
+ )
255
+
256
+ if result.returncode == 0:
257
+ typer.secho(f" ✓ Bucket '{bucket_name}' criado.", fg=typer.colors.GREEN)
258
+ return True
259
+
260
+ typer.secho(f" ✗ Falha ao criar bucket '{bucket_name}'.", fg=typer.colors.RED)
261
+ return False
262
+
263
+
264
+ def _save_credentials_to_k8s_secret(
265
+ ctx: ExecutionContext,
266
+ app_name: str,
267
+ username: str,
268
+ password: str,
269
+ namespace: str,
270
+ ) -> bool:
271
+ """Salva credenciais do usuário em um Secret K8s."""
272
+ import subprocess
273
+ import tempfile
274
+ from pathlib import Path
275
+
276
+ secret_name = f"minio-{app_name}-credentials"
277
+
278
+ # Cria ou atualiza secret
279
+ secret_manifest = f"""apiVersion: v1
280
+ kind: Secret
281
+ metadata:
282
+ name: {secret_name}
283
+ namespace: {namespace}
284
+ labels:
285
+ app.kubernetes.io/managed-by: raijin-server
286
+ app.kubernetes.io/component: minio-credentials
287
+ app.kubernetes.io/part-of: {app_name}
288
+ type: Opaque
289
+ stringData:
290
+ accesskey: "{username}"
291
+ secretkey: "{password}"
292
+ AWS_ACCESS_KEY_ID: "{username}"
293
+ AWS_SECRET_ACCESS_KEY: "{password}"
294
+ """
295
+
296
+ # Usa arquivo temporário para aplicar o manifest
297
+ tmp_path = None
298
+ try:
299
+ with tempfile.NamedTemporaryFile("w", delete=False, suffix=".yaml") as tmp:
300
+ tmp.write(secret_manifest)
301
+ tmp.flush()
302
+ tmp_path = Path(tmp.name)
303
+
304
+ result = run_cmd(
305
+ ["kubectl", "apply", "-f", str(tmp_path)],
306
+ ctx,
307
+ check=False,
308
+ )
309
+ return result.returncode == 0
310
+ finally:
311
+ if tmp_path and tmp_path.exists():
312
+ tmp_path.unlink(missing_ok=True)
313
+
314
+
315
+ def _get_credentials_from_k8s_secret(
316
+ ctx: ExecutionContext,
317
+ app_name: str,
318
+ namespace: str,
319
+ ) -> Optional[tuple[str, str]]:
320
+ """Recupera credenciais do Secret K8s se existir."""
321
+ secret_name = f"minio-{app_name}-credentials"
322
+
323
+ result = run_cmd(
324
+ ["kubectl", "-n", namespace, "get", "secret", secret_name, "-o", "jsonpath={.data.accesskey}"],
325
+ ctx,
326
+ check=False,
327
+ )
328
+
329
+ if result.returncode == 0 and result.stdout:
330
+ access_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
331
+
332
+ result = run_cmd(
333
+ ["kubectl", "-n", namespace, "get", "secret", secret_name, "-o", "jsonpath={.data.secretkey}"],
334
+ ctx,
335
+ check=False,
336
+ )
337
+
338
+ if result.returncode == 0 and result.stdout:
339
+ secret_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
340
+ return access_key, secret_key
341
+
342
+ return None
343
+
344
+
345
+ def get_or_create_minio_user(
346
+ ctx: ExecutionContext,
347
+ app_name: str,
348
+ buckets: Optional[list[str]] = None,
349
+ namespace: Optional[str] = None,
350
+ force_recreate: bool = False,
351
+ ) -> tuple[str, str]:
352
+ """Obtém ou cria usuário MinIO específico para uma aplicação.
353
+
354
+ Esta função implementa o princípio do menor privilégio, criando um usuário
355
+ MinIO com acesso restrito apenas aos buckets necessários para a aplicação.
356
+
357
+ Args:
358
+ ctx: Contexto de execução
359
+ app_name: Nome da aplicação (vault, velero, harbor, loki)
360
+ buckets: Lista de buckets para criar/permitir acesso (opcional, usa padrão se não fornecido)
361
+ namespace: Namespace K8s para salvar o secret (opcional, usa app_name se não fornecido)
362
+ force_recreate: Se True, recria usuário mesmo se já existir
363
+
364
+ Returns:
365
+ Tupla (access_key, secret_key) com as credenciais do usuário
366
+
367
+ Raises:
368
+ RuntimeError: Se não conseguir criar usuário ou MinIO não estiver disponível
369
+ """
370
+ typer.echo(f"\n🔐 Configurando usuário MinIO para '{app_name}'...")
371
+
372
+ # Obtém configuração padrão se não fornecida
373
+ if app_name in MINIO_APP_USERS:
374
+ config = MINIO_APP_USERS[app_name]
375
+ username = config["username"]
376
+ if buckets is None:
377
+ buckets = config["buckets"]
378
+ if namespace is None:
379
+ namespace = app_name
380
+ else:
381
+ username = f"{app_name}-user"
382
+ if buckets is None:
383
+ buckets = [f"{app_name}-data"]
384
+ if namespace is None:
385
+ namespace = app_name
386
+
387
+ policy_name = f"{app_name}-policy"
388
+
389
+ # Verifica se já existe secret com credenciais
390
+ if not force_recreate:
391
+ existing_creds = _get_credentials_from_k8s_secret(ctx, app_name, namespace)
392
+ if existing_creds:
393
+ typer.secho(f" ✓ Credenciais existentes encontradas para '{app_name}'.", fg=typer.colors.GREEN)
394
+ return existing_creds
395
+
396
+ if ctx.dry_run:
397
+ typer.echo(f" [DRY-RUN] Criaria usuário '{username}' com acesso a: {', '.join(buckets)}")
398
+ return (username, "dry-run-password")
399
+
400
+ # Obtém credenciais root
401
+ try:
402
+ root_user, root_password = _get_minio_root_credentials(ctx)
403
+ except RuntimeError as e:
404
+ typer.secho(f" ✗ {e}", fg=typer.colors.RED)
405
+ raise
406
+
407
+ # Configura mc alias
408
+ if not _setup_mc_alias(ctx, root_user, root_password):
409
+ raise RuntimeError("Falha ao configurar mc alias no MinIO.")
410
+
411
+ # Cria buckets necessários
412
+ typer.echo(f" Criando buckets para '{app_name}'...")
413
+ for bucket in buckets:
414
+ _create_bucket(ctx, bucket)
415
+
416
+ # Verifica se usuário já existe
417
+ user_exists = _check_user_exists(ctx, username)
418
+
419
+ if user_exists and not force_recreate:
420
+ # Recupera senha existente do secret se disponível
421
+ existing = _get_credentials_from_k8s_secret(ctx, app_name, namespace)
422
+ if existing:
423
+ typer.secho(f" ✓ Usuário '{username}' já existe.", fg=typer.colors.GREEN)
424
+ return existing
425
+
426
+ # Gera nova senha
427
+ password = _generate_password(32)
428
+
429
+ # Cria ou recria usuário
430
+ if user_exists:
431
+ typer.echo(f" Recriando usuário '{username}'...")
432
+ # Remove usuário antigo
433
+ run_cmd(
434
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "admin", "user", "remove", "local", username],
435
+ ctx,
436
+ check=False,
437
+ )
438
+
439
+ typer.echo(f" Criando usuário '{username}'...")
440
+ if not _create_minio_user(ctx, username, password):
441
+ raise RuntimeError(f"Falha ao criar usuário MinIO '{username}'.")
442
+
443
+ # Cria política de acesso
444
+ typer.echo(f" Criando política '{policy_name}'...")
445
+ if not _create_bucket_policy(ctx, policy_name, buckets):
446
+ raise RuntimeError(f"Falha ao criar política '{policy_name}'.")
447
+
448
+ # Associa política ao usuário
449
+ typer.echo(f" Associando política ao usuário...")
450
+ if not _attach_policy_to_user(ctx, username, policy_name):
451
+ raise RuntimeError(f"Falha ao associar política ao usuário '{username}'.")
452
+
453
+ # Garante que namespace existe
454
+ run_cmd(
455
+ ["kubectl", "create", "namespace", namespace, "--dry-run=client", "-o", "yaml"],
456
+ ctx,
457
+ check=False,
458
+ )
459
+ run_cmd(
460
+ ["kubectl", "create", "namespace", namespace],
461
+ ctx,
462
+ check=False,
463
+ )
464
+
465
+ # Salva credenciais em Secret K8s
466
+ typer.echo(f" Salvando credenciais em Secret K8s...")
467
+ if not _save_credentials_to_k8s_secret(ctx, app_name, username, password, namespace):
468
+ typer.secho(f" ⚠️ Falha ao salvar secret, mas usuário foi criado.", fg=typer.colors.YELLOW)
469
+
470
+ typer.secho(f" ✓ Usuário '{username}' criado com acesso a: {', '.join(buckets)}", fg=typer.colors.GREEN)
471
+
472
+ return (username, password)
473
+
474
+
475
+ def list_minio_users(ctx: ExecutionContext) -> list[dict]:
476
+ """Lista todos os usuários MinIO (exceto root)."""
477
+ try:
478
+ root_user, root_password = _get_minio_root_credentials(ctx)
479
+ _setup_mc_alias(ctx, root_user, root_password)
480
+ except RuntimeError:
481
+ return []
482
+
483
+ result = run_cmd(
484
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "admin", "user", "ls", "local", "--json"],
485
+ ctx,
486
+ check=False,
487
+ )
488
+
489
+ if result.returncode != 0:
490
+ return []
491
+
492
+ users = []
493
+ for line in (result.stdout or "").strip().split("\n"):
494
+ if line.strip():
495
+ try:
496
+ data = json.loads(line)
497
+ if data.get("accessKey"):
498
+ users.append({
499
+ "username": data.get("accessKey"),
500
+ "status": data.get("userStatus", "enabled"),
501
+ "policy": data.get("policyName", ""),
502
+ })
503
+ except json.JSONDecodeError:
504
+ pass
505
+
506
+ return users
507
+
508
+
509
+ def delete_minio_user(ctx: ExecutionContext, app_name: str) -> bool:
510
+ """Remove usuário MinIO e seu secret associado."""
511
+ if app_name in MINIO_APP_USERS:
512
+ username = MINIO_APP_USERS[app_name]["username"]
513
+ else:
514
+ username = f"{app_name}-user"
515
+
516
+ policy_name = f"{app_name}-policy"
517
+ secret_name = f"minio-{app_name}-credentials"
518
+
519
+ typer.echo(f"Removendo usuário MinIO '{username}'...")
520
+
521
+ try:
522
+ root_user, root_password = _get_minio_root_credentials(ctx)
523
+ _setup_mc_alias(ctx, root_user, root_password)
524
+ except RuntimeError as e:
525
+ typer.secho(f" ✗ {e}", fg=typer.colors.RED)
526
+ return False
527
+
528
+ # Remove associação de política
529
+ run_cmd(
530
+ [
531
+ "kubectl", "-n", "minio", "exec", "minio-0", "--",
532
+ "mc", "admin", "policy", "detach", "local", policy_name, "--user", username,
533
+ ],
534
+ ctx,
535
+ check=False,
536
+ )
537
+
538
+ # Remove usuário
539
+ result = run_cmd(
540
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "admin", "user", "remove", "local", username],
541
+ ctx,
542
+ check=False,
543
+ )
544
+
545
+ if result.returncode == 0:
546
+ typer.secho(f" ✓ Usuário '{username}' removido.", fg=typer.colors.GREEN)
547
+
548
+ # Remove política
549
+ run_cmd(
550
+ ["kubectl", "-n", "minio", "exec", "minio-0", "--", "mc", "admin", "policy", "remove", "local", policy_name],
551
+ ctx,
552
+ check=False,
553
+ )
554
+
555
+ # Remove secret K8s
556
+ run_cmd(
557
+ ["kubectl", "-n", app_name, "delete", "secret", secret_name, "--ignore-not-found"],
558
+ ctx,
559
+ check=False,
560
+ )
561
+
562
+ return True
@@ -15,6 +15,7 @@ __all__ = [
15
15
  "prometheus",
16
16
  "grafana",
17
17
  "loki",
18
+ "harbor",
18
19
  "harness",
19
20
  "velero",
20
21
  "kafka",
@@ -23,15 +24,12 @@ __all__ = [
23
24
  "vpn",
24
25
  "vpn_client",
25
26
  "internal_dns",
26
- "observability_ingress",
27
- "observability_dashboards",
28
- "apokolips_demo",
29
27
  "cert_manager",
30
28
  "secrets",
31
29
  "full_install",
32
30
  ]
33
31
 
34
- from raijin_server.modules import calico, essentials, firewall, grafana, harness, hardening, istio
35
- from raijin_server.modules import kafka, kong, kubernetes, loki, minio, network, observability_dashboards
36
- from raijin_server.modules import observability_ingress, prometheus, traefik, velero, apokolips_demo, secrets, cert_manager
32
+ from raijin_server.modules import calico, essentials, firewall, grafana, harbor, harness, hardening, istio
33
+ from raijin_server.modules import kafka, kong, kubernetes, loki, minio, network
34
+ from raijin_server.modules import prometheus, traefik, velero, secrets, cert_manager
37
35
  from raijin_server.modules import bootstrap, full_install, sanitize, ssh_hardening, vpn, vpn_client, internal_dns
@@ -15,12 +15,11 @@ from raijin_server.modules import (
15
15
  essentials,
16
16
  firewall,
17
17
  grafana,
18
+ harbor,
18
19
  hardening,
19
20
  kubernetes,
20
21
  loki,
21
22
  network,
22
- observability_dashboards,
23
- observability_ingress,
24
23
  prometheus,
25
24
  secrets,
26
25
  sanitize,
@@ -171,6 +170,13 @@ def _diag_secrets(ctx: ExecutionContext) -> None:
171
170
  _diag_namespace("external-secrets", ctx)
172
171
 
173
172
 
173
+ def _diag_harbor(ctx: ExecutionContext) -> None:
174
+ """Diagnostico do namespace harbor."""
175
+ ns = "harbor"
176
+ _run_cmd("Harbor pods", ["kubectl", "get", "pods", "-n", ns, "-o", "wide"], ctx)
177
+ _diag_namespace(ns, ctx)
178
+
179
+
174
180
  def _diag_prometheus(ctx: ExecutionContext) -> None:
175
181
  ns = "observability"
176
182
  _run_cmd("Prometheus pods", ["kubectl", "get", "pods", "-n", ns, "-l", "app.kubernetes.io/name=prometheus"], ctx)
@@ -195,18 +201,6 @@ def _diag_traefik(ctx: ExecutionContext) -> None:
195
201
  _diag_namespace(ns, ctx)
196
202
 
197
203
 
198
- def _diag_observability_ingress(ctx: ExecutionContext) -> None:
199
- ns = "observability"
200
- _run_cmd("Ingress objects", ["kubectl", "get", "ingress", "-n", ns], ctx)
201
- _diag_namespace(ns, ctx)
202
-
203
-
204
- def _diag_observability_dashboards(ctx: ExecutionContext) -> None:
205
- ns = "observability"
206
- _run_cmd("ConfigMaps dashboards", ["kubectl", "get", "configmap", "-n", ns, "-l", "raijin/dashboards=true"], ctx)
207
- _diag_namespace(ns, ctx)
208
-
209
-
210
204
  def _diag_minio(ctx: ExecutionContext) -> None:
211
205
  ns = "minio"
212
206
  _diag_namespace(ns, ctx)
@@ -232,12 +226,11 @@ DIAG_HANDLERS = {
232
226
  "cert_manager": cert_manager.diagnose,
233
227
  "calico": _diag_calico,
234
228
  "secrets": _diag_secrets,
229
+ "harbor": _diag_harbor,
235
230
  "prometheus": _diag_prometheus,
236
231
  "grafana": _diag_grafana,
237
232
  "loki": _diag_loki,
238
233
  "traefik": _diag_traefik,
239
- "observability_ingress": _diag_observability_ingress,
240
- "observability_dashboards": _diag_observability_dashboards,
241
234
  "minio": _diag_minio,
242
235
  "kafka": _diag_kafka,
243
236
  "velero": _diag_velero,
@@ -273,13 +266,12 @@ INSTALL_SEQUENCE = [
273
266
  ("kubernetes", kubernetes.run, "Cluster Kubernetes (kubeadm)", None),
274
267
  ("calico", calico.run, "CNI Calico + NetworkPolicy", None),
275
268
  ("cert_manager", _cert_manager_install_only, "cert-manager (instalacao base)", None),
276
- ("secrets", secrets.run, "Sealed-Secrets + External-Secrets", None),
269
+ ("secrets", secrets.run, "HashiCorp Vault + External Secrets Operator", None),
270
+ ("harbor", harbor.run, "Container Registry com Vulnerability Scanning", None),
277
271
  ("prometheus", prometheus.run, "Monitoramento Prometheus", None),
278
272
  ("grafana", grafana.run, "Dashboards Grafana", None),
279
273
  ("loki", loki.run, "Logs centralizados Loki", None),
280
274
  ("traefik", traefik.run, "Ingress Controller Traefik", None),
281
- ("observability_ingress", observability_ingress.run, "Ingress seguro para Grafana/Prometheus/Alertmanager", None),
282
- ("observability_dashboards", observability_dashboards.run, "Dashboards opinativos e alertas", None),
283
275
  ]
284
276
 
285
277