raijin-server 0.3.4__py3-none-any.whl → 0.3.6__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.

@@ -1,10 +1,15 @@
1
- """Automacao de sealed-secrets e external-secrets via Helm (production-ready).
1
+ """Automacao de HashiCorp Vault e External Secrets Operator (production-ready).
2
2
 
3
- Instala os controladores necessários para criptografar e consumir segredos
4
- em clusters Kubernetes. Inclui opcionalmente a exportacao do certificado
5
- publico do sealed-secrets para permitir geracao de manifests lacrados.
3
+ Instala Vault com MinIO backend para persistencia e External Secrets Operator
4
+ para sincronizar segredos do Vault para Secrets nativos do Kubernetes.
5
+
6
+ Arquitetura:
7
+ - Vault: Gerenciamento centralizado de segredos com MinIO como storage backend
8
+ - External Secrets Operator: Sincroniza segredos do Vault para K8s Secrets
9
+ - Aplicações: Usam Secrets nativos do K8s (transparente)
6
10
  """
7
11
 
12
+ import base64
8
13
  import socket
9
14
  import time
10
15
  from pathlib import Path
@@ -20,7 +25,7 @@ from raijin_server.utils import (
20
25
  write_file,
21
26
  )
22
27
 
23
- SEALED_NAMESPACE = "kube-system"
28
+ VAULT_NAMESPACE = "vault"
24
29
  ESO_NAMESPACE = "external-secrets"
25
30
 
26
31
 
@@ -36,10 +41,10 @@ def _detect_node_name(ctx: ExecutionContext) -> str:
36
41
  return socket.gethostname()
37
42
 
38
43
 
39
- def _check_existing_sealed_secrets(ctx: ExecutionContext, namespace: str) -> bool:
40
- """Verifica se existe instalacao do Sealed Secrets."""
44
+ def _check_existing_vault(ctx: ExecutionContext, namespace: str) -> bool:
45
+ """Verifica se existe instalacao do Vault."""
41
46
  result = run_cmd(
42
- ["helm", "status", "sealed-secrets", "-n", namespace],
47
+ ["helm", "status", "vault", "-n", namespace],
43
48
  ctx,
44
49
  check=False,
45
50
  )
@@ -56,12 +61,12 @@ def _check_existing_external_secrets(ctx: ExecutionContext, namespace: str) -> b
56
61
  return result.returncode == 0
57
62
 
58
63
 
59
- def _uninstall_sealed_secrets(ctx: ExecutionContext, namespace: str) -> None:
60
- """Remove instalacao anterior do Sealed Secrets."""
61
- typer.echo("Removendo instalacao anterior do Sealed Secrets...")
64
+ def _uninstall_vault(ctx: ExecutionContext, namespace: str) -> None:
65
+ """Remove instalacao anterior do Vault."""
66
+ typer.echo("Removendo instalacao anterior do Vault...")
62
67
 
63
68
  run_cmd(
64
- ["helm", "uninstall", "sealed-secrets", "-n", namespace],
69
+ ["helm", "uninstall", "vault", "-n", namespace],
65
70
  ctx,
66
71
  check=False,
67
72
  )
@@ -82,16 +87,16 @@ def _uninstall_external_secrets(ctx: ExecutionContext, namespace: str) -> None:
82
87
  time.sleep(5)
83
88
 
84
89
 
85
- def _wait_for_sealed_secrets_ready(ctx: ExecutionContext, namespace: str, timeout: int = 120) -> bool:
86
- """Aguarda pods do Sealed Secrets ficarem Ready."""
87
- typer.echo("Aguardando pods do Sealed Secrets ficarem Ready...")
90
+ def _wait_for_pods_ready(ctx: ExecutionContext, namespace: str, label: str, timeout: int = 120) -> bool:
91
+ """Aguarda pods ficarem Ready."""
92
+ typer.echo(f"Aguardando pods com label {label} ficarem Ready...")
88
93
  deadline = time.time() + timeout
89
94
 
90
95
  while time.time() < deadline:
91
96
  result = run_cmd(
92
97
  [
93
98
  "kubectl", "-n", namespace, "get", "pods",
94
- "-l", "app.kubernetes.io/name=sealed-secrets",
99
+ "-l", label,
95
100
  "-o", "jsonpath={range .items[*]}{.metadata.name}={.status.phase} {end}",
96
101
  ],
97
102
  ctx,
@@ -109,49 +114,266 @@ def _wait_for_sealed_secrets_ready(ctx: ExecutionContext, namespace: str, timeou
109
114
  pods.append((parts[0], parts[1]))
110
115
 
111
116
  if pods and all(phase == "Running" for _, phase in pods):
112
- typer.secho(" Sealed Secrets Ready.", fg=typer.colors.GREEN)
117
+ typer.secho(f" Pods {label} Ready.", fg=typer.colors.GREEN)
113
118
  return True
114
119
 
115
120
  time.sleep(5)
116
121
 
117
- typer.secho(" Timeout aguardando Sealed Secrets.", fg=typer.colors.YELLOW)
122
+ typer.secho(f" Timeout aguardando pods {label}.", fg=typer.colors.YELLOW)
118
123
  return False
119
124
 
120
125
 
121
- def _export_sealed_cert(namespace: str, ctx: ExecutionContext) -> None:
122
- """Exporta o certificado publico do sealed-secrets para um caminho local."""
126
+ def _get_minio_credentials(ctx: ExecutionContext) -> tuple[str, str]:
127
+ """Obtem credenciais do MinIO do Secret do K8s."""
128
+ typer.echo("Obtendo credenciais do MinIO...")
129
+
130
+ # Tenta obter do secret minio-credentials no namespace minio
131
+ result = run_cmd(
132
+ ["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.accesskey}"],
133
+ ctx,
134
+ check=False,
135
+ )
136
+
137
+ if result.returncode == 0 and result.stdout:
138
+ access_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
139
+
140
+ result = run_cmd(
141
+ ["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.secretkey}"],
142
+ ctx,
143
+ check=False,
144
+ )
145
+
146
+ if result.returncode == 0 and result.stdout:
147
+ secret_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
148
+ return access_key, secret_key
149
+
150
+ # Fallback para prompt manual
151
+ typer.secho("Não foi possível obter credenciais automaticamente.", fg=typer.colors.YELLOW)
152
+ access_key = typer.prompt("MinIO Access Key", default="thor")
153
+ secret_key = typer.prompt("MinIO Secret Key", default="rebel1on", hide_input=True)
154
+
155
+ return access_key, secret_key
156
+
123
157
 
124
- default_path = Path("/tmp/sealed-secrets-cert.pem")
125
- dest = typer.prompt(
126
- "Caminho para salvar o certificado publico do sealed-secrets",
127
- default=str(default_path),
158
+ def _initialize_vault(ctx: ExecutionContext, vault_ns: str, node_ip: str) -> tuple[str, list[str]]:
159
+ """Inicializa o Vault e retorna root token e unseal keys."""
160
+ typer.echo("\n Inicializando Vault...")
161
+
162
+ result = run_cmd(
163
+ ["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "operator", "init", "-format=json"],
164
+ ctx,
165
+ check=False,
128
166
  )
129
- typer.echo(f"Exportando certificado para {dest}...")
130
- cmd = [
131
- "kubectl",
132
- "-n",
133
- namespace,
134
- "get",
135
- "secret",
136
- "-l",
137
- "sealedsecrets.bitnami.com/sealed-secrets-key",
138
- "-o",
139
- r"jsonpath={.items[0].data.tls\.crt}",
140
- ]
141
- result = run_cmd(cmd, ctx, check=False)
167
+
142
168
  if result.returncode != 0:
143
- typer.secho("Nao foi possivel obter o certificado (tente novamente apos o pod estar Ready).", fg=typer.colors.YELLOW)
144
- return
169
+ typer.secho("Falha ao inicializar Vault.", fg=typer.colors.RED)
170
+ raise typer.Exit(1)
171
+
172
+ import json
173
+ init_data = json.loads(result.stdout)
174
+ root_token = init_data["root_token"]
175
+ unseal_keys = init_data["unseal_keys_b64"]
176
+
177
+ # Salva keys localmente
178
+ vault_keys_path = Path("/etc/vault/keys.json")
179
+ vault_keys_path.parent.mkdir(parents=True, exist_ok=True)
180
+ vault_keys_path.write_text(json.dumps(init_data, indent=2))
181
+ typer.secho(f"\n✓ Vault keys salvas em {vault_keys_path}", fg=typer.colors.GREEN)
182
+ typer.secho("⚠️ IMPORTANTE: Guarde essas keys em local seguro!", fg=typer.colors.YELLOW, bold=True)
183
+
184
+ return root_token, unseal_keys
185
+
186
+
187
+ def _unseal_vault(ctx: ExecutionContext, vault_ns: str, unseal_keys: list[str]) -> None:
188
+ """Destrava o Vault usando as unseal keys."""
189
+ typer.echo("\nDesbloqueando Vault...")
190
+
191
+ # Precisa de 3 keys das 5 geradas (threshold padrão)
192
+ for i in range(3):
193
+ run_cmd(
194
+ ["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "operator", "unseal", unseal_keys[i]],
195
+ ctx,
196
+ )
197
+
198
+ typer.secho("✓ Vault desbloqueado.", fg=typer.colors.GREEN)
199
+
200
+
201
+ def _enable_kv_secrets(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
202
+ """Habilita KV v2 secrets engine."""
203
+ typer.echo("\nHabilitando KV v2 secrets engine...")
204
+
205
+ run_cmd(
206
+ [
207
+ "kubectl", "-n", vault_ns, "exec", "vault-0", "--",
208
+ "vault", "secrets", "enable", "-path=secret", "kv-v2"
209
+ ],
210
+ ctx,
211
+ env={"VAULT_TOKEN": root_token},
212
+ check=False, # Pode já estar habilitado
213
+ )
214
+
215
+ typer.secho("✓ KV v2 habilitado em path 'secret'.", fg=typer.colors.GREEN)
145
216
 
146
- try:
147
- import base64
148
217
 
149
- cert_b64 = result.stdout.strip()
150
- cert_bytes = base64.b64decode(cert_b64)
151
- Path(dest).write_bytes(cert_bytes)
152
- typer.secho(f"✓ Certificado salvo em {dest}", fg=typer.colors.GREEN)
153
- except Exception as exc:
154
- typer.secho(f"Falha ao decodificar/salvar certificado: {exc}", fg=typer.colors.YELLOW)
218
+ def _configure_kubernetes_auth(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
219
+ """Configura autenticação Kubernetes no Vault."""
220
+ typer.echo("\nConfigurando autenticação Kubernetes...")
221
+
222
+ # Habilita kubernetes auth
223
+ run_cmd(
224
+ ["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "auth", "enable", "kubernetes"],
225
+ ctx,
226
+ env={"VAULT_TOKEN": root_token},
227
+ check=False,
228
+ )
229
+
230
+ # Configura kubernetes auth
231
+ run_cmd(
232
+ [
233
+ "kubectl", "-n", vault_ns, "exec", "vault-0", "--",
234
+ "sh", "-c",
235
+ "vault write auth/kubernetes/config " +
236
+ "kubernetes_host=https://$KUBERNETES_PORT_443_TCP_ADDR:443"
237
+ ],
238
+ ctx,
239
+ env={"VAULT_TOKEN": root_token},
240
+ )
241
+
242
+ typer.secho("✓ Autenticação Kubernetes configurada.", fg=typer.colors.GREEN)
243
+
244
+
245
+ def _create_eso_policy_and_role(ctx: ExecutionContext, vault_ns: str, root_token: str, eso_ns: str) -> None:
246
+ """Cria policy e role para External Secrets Operator."""
247
+ typer.echo("\nCriando policy e role para ESO...")
248
+
249
+ # Policy para ler todos os secrets
250
+ policy = """path "secret/data/*" {
251
+ capabilities = ["read"]
252
+ }"""
253
+
254
+ run_cmd(
255
+ ["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "policy", "write", "eso-policy", "-"],
256
+ ctx,
257
+ env={"VAULT_TOKEN": root_token},
258
+ input=policy,
259
+ )
260
+
261
+ # Role vinculando serviceaccount do ESO
262
+ run_cmd(
263
+ [
264
+ "kubectl", "-n", vault_ns, "exec", "vault-0", "--",
265
+ "vault", "write", "auth/kubernetes/role/eso-role",
266
+ "bound_service_account_names=external-secrets",
267
+ f"bound_service_account_namespaces={eso_ns}",
268
+ "policies=eso-policy",
269
+ "ttl=24h"
270
+ ],
271
+ ctx,
272
+ env={"VAULT_TOKEN": root_token},
273
+ )
274
+
275
+ typer.secho("✓ Policy 'eso-policy' e role 'eso-role' criadas.", fg=typer.colors.GREEN)
276
+
277
+
278
+ def _create_secretstore_example(ctx: ExecutionContext, vault_ns: str, eso_ns: str, node_ip: str) -> None:
279
+ """Cria exemplo de ClusterSecretStore e ExternalSecret."""
280
+ typer.echo("\nCriando exemplo de ClusterSecretStore...")
281
+
282
+ secretstore_yaml = f"""apiVersion: external-secrets.io/v1beta1
283
+ kind: ClusterSecretStore
284
+ metadata:
285
+ name: vault-backend
286
+ spec:
287
+ provider:
288
+ vault:
289
+ server: "http://vault.{vault_ns}.svc.cluster.local:8200"
290
+ path: "secret"
291
+ version: "v2"
292
+ auth:
293
+ kubernetes:
294
+ mountPath: "kubernetes"
295
+ role: "eso-role"
296
+ serviceAccountRef:
297
+ name: "external-secrets"
298
+ namespace: "{eso_ns}"
299
+ """
300
+
301
+ secretstore_path = Path("/tmp/raijin-vault-secretstore.yaml")
302
+ write_file(secretstore_path, secretstore_yaml, ctx)
303
+
304
+ run_cmd(
305
+ ["kubectl", "apply", "-f", str(secretstore_path)],
306
+ ctx,
307
+ )
308
+
309
+ typer.secho("✓ ClusterSecretStore 'vault-backend' criado.", fg=typer.colors.GREEN)
310
+
311
+
312
+ def _create_example_secret(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
313
+ """Cria um secret de exemplo no Vault."""
314
+ typer.echo("\nCriando secret de exemplo no Vault...")
315
+
316
+ run_cmd(
317
+ [
318
+ "kubectl", "-n", vault_ns, "exec", "vault-0", "--",
319
+ "vault", "kv", "put", "secret/example",
320
+ "username=admin",
321
+ "password=supersecret123"
322
+ ],
323
+ ctx,
324
+ env={"VAULT_TOKEN": root_token},
325
+ )
326
+
327
+ typer.secho("✓ Secret 'secret/example' criado no Vault.", fg=typer.colors.GREEN)
328
+
329
+ # Cria ExternalSecret de exemplo
330
+ external_secret_yaml = """apiVersion: external-secrets.io/v1beta1
331
+ kind: ExternalSecret
332
+ metadata:
333
+ name: example-secret
334
+ namespace: default
335
+ spec:
336
+ refreshInterval: 1h
337
+ secretStoreRef:
338
+ name: vault-backend
339
+ kind: ClusterSecretStore
340
+ target:
341
+ name: example-secret
342
+ creationPolicy: Owner
343
+ data:
344
+ - secretKey: username
345
+ remoteRef:
346
+ key: secret/example
347
+ property: username
348
+ - secretKey: password
349
+ remoteRef:
350
+ key: secret/example
351
+ property: password
352
+ """
353
+
354
+ external_secret_path = Path("/tmp/raijin-vault-externalsecret.yaml")
355
+ write_file(external_secret_path, external_secret_yaml, ctx)
356
+
357
+ run_cmd(
358
+ ["kubectl", "apply", "-f", str(external_secret_path)],
359
+ ctx,
360
+ )
361
+
362
+ typer.secho("✓ ExternalSecret 'example-secret' criado no namespace default.", fg=typer.colors.GREEN)
363
+
364
+ # Aguarda sincronização
365
+ time.sleep(5)
366
+
367
+ # Verifica se o Secret foi criado
368
+ result = run_cmd(
369
+ ["kubectl", "-n", "default", "get", "secret", "example-secret"],
370
+ ctx,
371
+ check=False,
372
+ )
373
+
374
+ if result.returncode == 0:
375
+ typer.secho("\n✓ Secret sincronizado com sucesso! Teste com:", fg=typer.colors.GREEN)
376
+ typer.echo(" kubectl -n default get secret example-secret -o yaml")
155
377
 
156
378
 
157
379
  def run(ctx: ExecutionContext) -> None:
@@ -159,69 +381,132 @@ def run(ctx: ExecutionContext) -> None:
159
381
  ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou habilite dry-run.")
160
382
  ensure_tool("helm", ctx, install_hint="Instale helm ou habilite dry-run.")
161
383
 
162
- typer.echo("Instalando sealed-secrets e external-secrets...")
384
+ typer.echo("Instalando HashiCorp Vault + External Secrets Operator...")
163
385
 
164
- sealed_ns = typer.prompt("Namespace para sealed-secrets", default=SEALED_NAMESPACE)
165
- eso_ns = typer.prompt("Namespace para external-secrets", default=ESO_NAMESPACE)
386
+ vault_ns = typer.prompt("Namespace para Vault", default=VAULT_NAMESPACE)
387
+ eso_ns = typer.prompt("Namespace para External Secrets", default=ESO_NAMESPACE)
166
388
 
167
389
  node_name = _detect_node_name(ctx)
390
+
391
+ # Detecta IP do node para acesso ao MinIO
392
+ result = run_cmd(
393
+ ["kubectl", "get", "nodes", "-o", "jsonpath={.items[0].status.addresses[?(@.type=='InternalIP')].address}"],
394
+ ctx,
395
+ check=False,
396
+ )
397
+ node_ip = result.stdout.strip() if result.returncode == 0 else "192.168.1.81"
398
+
399
+ minio_host = typer.prompt("MinIO host", default=f"{node_ip}:30900")
400
+ access_key, secret_key = _get_minio_credentials(ctx)
168
401
 
169
- # sealed-secrets
170
- typer.secho("\n== Sealed Secrets ==", fg=typer.colors.CYAN, bold=True)
402
+ # ========== HashiCorp Vault ==========
403
+ typer.secho("\n== HashiCorp Vault ==", fg=typer.colors.CYAN, bold=True)
171
404
 
172
- # Prompt opcional de limpeza
173
- if _check_existing_sealed_secrets(ctx, sealed_ns):
405
+ if _check_existing_vault(ctx, vault_ns):
174
406
  cleanup = typer.confirm(
175
- "Instalacao anterior do Sealed Secrets detectada. Limpar antes de reinstalar?",
407
+ "Instalacao anterior do Vault detectada. Limpar antes de reinstalar?",
176
408
  default=False,
177
409
  )
178
410
  if cleanup:
179
- _uninstall_sealed_secrets(ctx, sealed_ns)
411
+ _uninstall_vault(ctx, vault_ns)
180
412
 
181
- sealed_values_yaml = f"""tolerations:
182
- - key: node-role.kubernetes.io/control-plane
183
- operator: Exists
184
- effect: NoSchedule
185
- - key: node-role.kubernetes.io/master
186
- operator: Exists
187
- effect: NoSchedule
188
- nodeSelector:
189
- kubernetes.io/hostname: {node_name}
190
- resources:
191
- requests:
192
- memory: 64Mi
193
- cpu: 50m
194
- limits:
195
- memory: 128Mi
413
+ # Cria bucket no MinIO para Vault storage
414
+ typer.echo("\nCriando bucket 'vault-storage' no MinIO...")
415
+ run_cmd(
416
+ [
417
+ "mc", "mb", "--ignore-existing",
418
+ f"minio/vault-storage"
419
+ ],
420
+ ctx,
421
+ check=False,
422
+ )
423
+
424
+ vault_values_yaml = f"""server:
425
+ ha:
426
+ enabled: true
427
+ replicas: 1
428
+ raft:
429
+ enabled: false
430
+
431
+ standalone:
432
+ enabled: true
433
+ config: |
434
+ ui = true
435
+
436
+ listener "tcp" {{
437
+ tls_disable = 1
438
+ address = "[::]:8200"
439
+ cluster_address = "[::]:8201"
440
+ }}
441
+
442
+ storage "s3" {{
443
+ endpoint = "http://{minio_host}"
444
+ bucket = "vault-storage"
445
+ access_key = "{access_key}"
446
+ secret_key = "{secret_key}"
447
+ s3_force_path_style = true
448
+ }}
449
+
450
+ api_addr = "http://vault.{vault_ns}.svc.cluster.local:8200"
451
+ cluster_addr = "http://vault-0.vault-internal:8201"
452
+
453
+ tolerations:
454
+ - key: node-role.kubernetes.io/control-plane
455
+ operator: Exists
456
+ effect: NoSchedule
457
+ - key: node-role.kubernetes.io/master
458
+ operator: Exists
459
+ effect: NoSchedule
460
+
461
+ nodeSelector:
462
+ kubernetes.io/hostname: {node_name}
463
+
464
+ resources:
465
+ requests:
466
+ memory: 256Mi
467
+ cpu: 250m
468
+ limits:
469
+ memory: 512Mi
470
+
471
+ ui:
472
+ enabled: true
473
+ serviceType: "NodePort"
474
+ serviceNodePort: 30820
475
+
476
+ injector:
477
+ enabled: false
196
478
  """
197
479
 
198
- sealed_values_path = Path("/tmp/raijin-sealed-secrets-values.yaml")
199
- write_file(sealed_values_path, sealed_values_yaml, ctx)
480
+ vault_values_path = Path("/tmp/raijin-vault-values.yaml")
481
+ write_file(vault_values_path, vault_values_yaml, ctx)
200
482
 
201
483
  helm_upgrade_install(
202
- "sealed-secrets",
203
- "sealed-secrets",
204
- sealed_ns,
484
+ "vault",
485
+ "vault",
486
+ vault_ns,
205
487
  ctx,
206
- repo="bitnami-labs",
207
- repo_url="https://bitnami-labs.github.io/sealed-secrets",
488
+ repo="hashicorp",
489
+ repo_url="https://helm.releases.hashicorp.com",
208
490
  create_namespace=True,
209
- extra_args=["-f", str(sealed_values_path)],
491
+ extra_args=["-f", str(vault_values_path)],
210
492
  )
211
493
 
212
494
  if not ctx.dry_run:
213
- _wait_for_sealed_secrets_ready(ctx, sealed_ns)
214
-
215
- typer.echo(
216
- "Para criar sealed-secrets a partir do seu desktop, exporte o certificado publico e use kubeseal."
217
- )
218
- if typer.confirm("Exportar certificado publico agora?", default=True):
219
- _export_sealed_cert(sealed_ns, ctx)
495
+ _wait_for_pods_ready(ctx, vault_ns, "app.kubernetes.io/name=vault", timeout=180)
496
+
497
+ # Inicializa Vault
498
+ root_token, unseal_keys = _initialize_vault(ctx, vault_ns, node_ip)
499
+
500
+ # Destrava Vault
501
+ _unseal_vault(ctx, vault_ns, unseal_keys)
502
+
503
+ # Configura Vault
504
+ _enable_kv_secrets(ctx, vault_ns, root_token)
505
+ _configure_kubernetes_auth(ctx, vault_ns, root_token)
220
506
 
221
- # external-secrets
507
+ # ========== External Secrets Operator ==========
222
508
  typer.secho("\n== External Secrets Operator ==", fg=typer.colors.CYAN, bold=True)
223
509
 
224
- # Prompt opcional de limpeza
225
510
  if _check_existing_external_secrets(ctx, eso_ns):
226
511
  cleanup = typer.confirm(
227
512
  "Instalacao anterior do External Secrets detectada. Limpar antes de reinstalar?",
@@ -282,12 +567,48 @@ resources:
282
567
  extra_args=["-f", str(eso_values_path)],
283
568
  )
284
569
 
285
- typer.secho("\n✓ Secrets management instalado com sucesso.", fg=typer.colors.GREEN, bold=True)
286
- typer.echo(
287
- "\nExternal Secrets Operator instalado. Configure um SecretStore/ClusterSecretStore conforme seu provedor (AWS/GCP/Vault)."
288
- )
570
+ if not ctx.dry_run:
571
+ _wait_for_pods_ready(ctx, eso_ns, "app.kubernetes.io/name=external-secrets", timeout=120)
572
+
573
+ # Configura integração Vault + ESO
574
+ _create_eso_policy_and_role(ctx, vault_ns, root_token, eso_ns)
575
+ _create_secretstore_example(ctx, vault_ns, eso_ns, node_ip)
576
+ _create_example_secret(ctx, vault_ns, root_token)
289
577
 
290
- typer.secho("\nDicas rapidas:", fg=typer.colors.GREEN)
291
- typer.echo(f"- Gere sealed-secrets localmente: kubeseal --controller-namespace {sealed_ns} --controller-name sealed-secrets < secret.yaml > sealed.yaml")
292
- typer.echo("- Para ESO: crie um SecretStore apontando para seu backend e um ExternalSecret referenciando os keys.")
578
+ typer.secho("\n✓ Vault + External Secrets Operator instalado com sucesso!", fg=typer.colors.GREEN, bold=True)
579
+
580
+ typer.secho("\n=== Acesso ao Vault UI ===", fg=typer.colors.CYAN)
581
+ typer.echo(f"URL: http://{node_ip}:30820")
582
+ typer.echo(f"Token: {root_token if not ctx.dry_run else '<root-token>'}")
583
+
584
+ typer.secho("\n=== Como usar ===", fg=typer.colors.CYAN)
585
+ typer.echo("1. Criar segredo no Vault:")
586
+ typer.echo(f" kubectl -n {vault_ns} exec vault-0 -- vault kv put secret/myapp username=admin password=secret123")
587
+
588
+ typer.echo("\n2. Criar ExternalSecret:")
589
+ typer.echo(" kubectl apply -f - <<EOF")
590
+ typer.echo(" apiVersion: external-secrets.io/v1beta1")
591
+ typer.echo(" kind: ExternalSecret")
592
+ typer.echo(" metadata:")
593
+ typer.echo(" name: myapp-secret")
594
+ typer.echo(" spec:")
595
+ typer.echo(" secretStoreRef:")
596
+ typer.echo(" name: vault-backend")
597
+ typer.echo(" kind: ClusterSecretStore")
598
+ typer.echo(" target:")
599
+ typer.echo(" name: myapp-secret")
600
+ typer.echo(" data:")
601
+ typer.echo(" - secretKey: username")
602
+ typer.echo(" remoteRef:")
603
+ typer.echo(" key: secret/myapp")
604
+ typer.echo(" property: username")
605
+ typer.echo(" EOF")
606
+
607
+ typer.echo("\n3. Secret será sincronizado automaticamente!")
608
+ typer.echo(" kubectl get secret myapp-secret -o yaml")
609
+
610
+ typer.secho("\n⚠️ IMPORTANTE:", fg=typer.colors.YELLOW, bold=True)
611
+ typer.echo(f"- Root token e unseal keys salvos em: /etc/vault/keys.json")
612
+ typer.echo("- Faça backup dessas keys em local seguro!")
613
+ typer.echo("- Após reboot do Vault, use: kubectl -n vault exec vault-0 -- vault operator unseal")
293
614
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: CLI para automacao de setup e hardening de servidores Ubuntu Server.
5
5
  Home-page: https://example.com/raijin-server
6
6
  Author: Equipe Raijin
@@ -1,19 +1,20 @@
1
- raijin_server/__init__.py,sha256=PxBToT7REJri8rHFGLoeIfEApLUM9TrOogmxCLdnomI,94
2
- raijin_server/cli.py,sha256=rqkAQCU5imi52YJCIeEuZqWo8bWYkVErOQh3JpKIDok,38149
1
+ raijin_server/__init__.py,sha256=DizNqR7krrm4UEAK6a2xeOG3wuV7xlSekBC-mCSHadw,94
2
+ raijin_server/cli.py,sha256=74ZqEPhg9R9jxNFZRV37CHrwm-dBKmYdT_MxdK4FKKA,37585
3
3
  raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
4
4
  raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
5
5
  raijin_server/module_manager.py,sha256=Wmhj603CN0XGUVr7_Fo8CHzKd9yIbS9x5BJLqDj78kw,10259
6
6
  raijin_server/utils.py,sha256=9RnGnPoUTYOpMVRLNa4P4lIQrJNQLkSkPUxycZRGv78,20827
7
7
  raijin_server/validators.py,sha256=EATYPy2pllAb6IX4gUZKnELvospWwyGV3DHrzxb_RMg,11761
8
- raijin_server/modules/__init__.py,sha256=ojxAdnJfXifNUVa4WuLVh97jHqeVzIi6DZ_fAtXB9tM,984
8
+ raijin_server/modules/__init__.py,sha256=BCTLuNtmvn8IWqGNQZQBRokqAv-KwUZP4_kyxkyHyN4,896
9
9
  raijin_server/modules/apokolips_demo.py,sha256=8ltsXRbVDwlDwLMIvh02NG-FeAfBWw_v6lh7IGOyNqs,13725
10
10
  raijin_server/modules/bootstrap.py,sha256=oVIGNRW_JbgY8zXNHGAIP0vGbbHNHyQexthxo5zhbcw,9762
11
11
  raijin_server/modules/calico.py,sha256=TTPF1bLFdAKb3IVOqFqRxNblULkRmMMRylsIBp4w8I8,6700
12
12
  raijin_server/modules/cert_manager.py,sha256=XkFlXJjiP4_9It_PJaFcVYMS-QKTzzFAt839QQ9qNsg,50223
13
13
  raijin_server/modules/essentials.py,sha256=2xUXCyCQtFGd2DnCKV81N1R6bEJqH8zaet8mLovtQ1I,689
14
14
  raijin_server/modules/firewall.py,sha256=h6AISqiZeTinVT7BjmQIS872qRAFZJLg7meqlth3cfw,757
15
- raijin_server/modules/full_install.py,sha256=xiKe2GLuZ97c4YdTmhP-kwDVuJJ9Xq3dlgcLlqSPeYM,15518
15
+ raijin_server/modules/full_install.py,sha256=M4SV4OA-r41xhpMmZQvUieRugiq0faLa4f0N499ksd4,15035
16
16
  raijin_server/modules/grafana.py,sha256=8YbKG-UL19lwTkH1-ZxpUetOZd-CLI4_kPsuZblNaWI,18080
17
+ raijin_server/modules/harbor.py,sha256=ix0qnu_FugzbFnnTRZ3Dyy6UmKdm3nD0ubZ4SZYHkWE,21940
17
18
  raijin_server/modules/hardening.py,sha256=4hz3ifkMhPlXa2n7gPxN0gitQgzALZ-073vuU3LM4RI,1616
18
19
  raijin_server/modules/harness.py,sha256=uWTxTVJlY_VB6xi4ftMtTSaIb96HA8WJQS-RbyxU45M,5391
19
20
  raijin_server/modules/internal_dns.py,sha256=Jynngq0TEEUo3jkAR4m8F1ihF10rkQuKHVP-gZYyDFY,15191
@@ -25,11 +26,9 @@ raijin_server/modules/loki.py,sha256=fRoXNghwffW6afE_a3sKMhjPJ9DIWhGMTTPM-ltNr2E
25
26
  raijin_server/modules/metallb.py,sha256=uUuklc_RsQ-W2qDVRMQAxQm9HKGEqso444b1IwBpM6w,8554
26
27
  raijin_server/modules/minio.py,sha256=ZoxugJvvuGLzViDfEzrVCRZUevoiFwcEy0PNyn0My4w,18918
27
28
  raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
28
- raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
29
- raijin_server/modules/observability_ingress.py,sha256=S4MtJKahiZ1qSx0P71P3IhKvq4RY-g01Z4IogW3c1hs,7045
30
29
  raijin_server/modules/prometheus.py,sha256=lyhaqLIfMl0GtQ2b2Hre7_A47HrHBB5gspmnWtwXZ4Y,21880
31
30
  raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
32
- raijin_server/modules/secrets.py,sha256=d4j12feQL8m_4-hYN5FfboQHvBc75TFeGno3OzrXokE,9266
31
+ raijin_server/modules/secrets.py,sha256=C5k10ODo6Ai_e6Ei-JiWTOa-cOh-5d2__PhTQA0UmjI,19452
33
32
  raijin_server/modules/ssh_hardening.py,sha256=Zd0dlylUBr01SkrI1CS05-0DB9xIto5rWH1bUVs80ow,5422
34
33
  raijin_server/modules/traefik.py,sha256=omziywss4o-8t64Kj-upLqbXdFYm2JwqOoOukDUmqxY,5008
35
34
  raijin_server/modules/velero.py,sha256=yDtqd6yUu0L5wzLCjYXqvvxB_RyaAoZtntb6HoHVAOo,5642
@@ -40,9 +39,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
40
39
  raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
41
40
  raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
42
41
  raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
43
- raijin_server-0.3.4.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
44
- raijin_server-0.3.4.dist-info/METADATA,sha256=pwLU_oSjSgh599AadAEAoyH-UOVQBs9mhQZef-Bcnzk,8829
45
- raijin_server-0.3.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
- raijin_server-0.3.4.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
47
- raijin_server-0.3.4.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
48
- raijin_server-0.3.4.dist-info/RECORD,,
42
+ raijin_server-0.3.6.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
43
+ raijin_server-0.3.6.dist-info/METADATA,sha256=egpj4eEFQ3JJAA-bT3Iz5ebOc57e5JpyuQ2a8-ruAD4,8829
44
+ raijin_server-0.3.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
45
+ raijin_server-0.3.6.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
46
+ raijin_server-0.3.6.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
47
+ raijin_server-0.3.6.dist-info/RECORD,,