raijin-server 0.2.2__tar.gz → 0.2.4__tar.gz

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.
Files changed (52) hide show
  1. {raijin_server-0.2.2/src/raijin_server.egg-info → raijin_server-0.2.4}/PKG-INFO +31 -1
  2. {raijin_server-0.2.2 → raijin_server-0.2.4}/README.md +30 -0
  3. {raijin_server-0.2.2 → raijin_server-0.2.4}/setup.cfg +1 -1
  4. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/__init__.py +1 -1
  5. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/cli.py +77 -0
  6. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/healthchecks.py +61 -2
  7. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/apokolips_demo.py +40 -4
  8. raijin_server-0.2.4/src/raijin_server/modules/cert_manager.py +1034 -0
  9. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/full_install.py +44 -1
  10. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/utils.py +57 -1
  11. {raijin_server-0.2.2 → raijin_server-0.2.4/src/raijin_server.egg-info}/PKG-INFO +31 -1
  12. raijin_server-0.2.2/src/raijin_server/modules/cert_manager.py +0 -156
  13. {raijin_server-0.2.2 → raijin_server-0.2.4}/LICENSE +0 -0
  14. {raijin_server-0.2.2 → raijin_server-0.2.4}/pyproject.toml +0 -0
  15. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/config.py +0 -0
  16. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/__init__.py +0 -0
  17. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/bootstrap.py +0 -0
  18. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/calico.py +0 -0
  19. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/essentials.py +0 -0
  20. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/firewall.py +0 -0
  21. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/grafana.py +0 -0
  22. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/hardening.py +0 -0
  23. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/harness.py +0 -0
  24. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/istio.py +0 -0
  25. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/kafka.py +0 -0
  26. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/kong.py +0 -0
  27. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/kubernetes.py +0 -0
  28. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/loki.py +0 -0
  29. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/minio.py +0 -0
  30. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/network.py +0 -0
  31. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/observability_dashboards.py +0 -0
  32. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/observability_ingress.py +0 -0
  33. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/prometheus.py +0 -0
  34. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/sanitize.py +0 -0
  35. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/secrets.py +0 -0
  36. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/ssh_hardening.py +0 -0
  37. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/traefik.py +0 -0
  38. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/velero.py +0 -0
  39. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/modules/vpn.py +0 -0
  40. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/scripts/__init__.py +0 -0
  41. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/scripts/checklist.sh +0 -0
  42. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/scripts/install.sh +0 -0
  43. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/scripts/log_size_metric.sh +0 -0
  44. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/scripts/pre-deploy-check.sh +0 -0
  45. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server/validators.py +0 -0
  46. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server.egg-info/SOURCES.txt +0 -0
  47. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server.egg-info/dependency_links.txt +0 -0
  48. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server.egg-info/entry_points.txt +0 -0
  49. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server.egg-info/requires.txt +0 -0
  50. {raijin_server-0.2.2 → raijin_server-0.2.4}/src/raijin_server.egg-info/top_level.txt +0 -0
  51. {raijin_server-0.2.2 → raijin_server-0.2.4}/tests/test_full_install_sequence.py +0 -0
  52. {raijin_server-0.2.2 → raijin_server-0.2.4}/tests/test_registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.2.2
3
+ Version: 0.2.4
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
@@ -81,6 +81,36 @@ source .venv/bin/activate
81
81
  python -m pip install -e .
82
82
  ```
83
83
 
84
+ ### Instalação em Produção (Recomendado)
85
+
86
+ Para servidores em produção, use um venv isolado e execute com sudo preservando o ambiente:
87
+
88
+ ```bash
89
+ # 1. Sair do venv atual (se estiver ativo)
90
+ deactivate
91
+
92
+ # 2. (Opcional) Remover venv antigo
93
+ rm -rf ~/.venvs/raijin
94
+
95
+ # 3. Criar venv novo
96
+ python3 -m venv ~/.venvs/raijin
97
+ source ~/.venvs/raijin/bin/activate
98
+ pip install -U pip setuptools
99
+
100
+ # 4. Instalar a versão mais recente
101
+ pip install -U raijin-server
102
+
103
+ # 5. Rodar usando root preservando o venv
104
+ sudo -E ~/.venvs/raijin/bin/raijin-server --version
105
+ sudo -E ~/.venvs/raijin/bin/raijin-server validate
106
+ sudo -E ~/.venvs/raijin/bin/raijin-server full-install
107
+
108
+ # 6. Para sair do venv quando terminar
109
+ deactivate
110
+ ```
111
+
112
+ > **Nota**: O `-E` no sudo preserva as variáveis de ambiente, garantindo que o Python use o venv correto mesmo como root.
113
+
84
114
  ## Uso rapido
85
115
 
86
116
  ### Validar Sistema
@@ -48,6 +48,36 @@ source .venv/bin/activate
48
48
  python -m pip install -e .
49
49
  ```
50
50
 
51
+ ### Instalação em Produção (Recomendado)
52
+
53
+ Para servidores em produção, use um venv isolado e execute com sudo preservando o ambiente:
54
+
55
+ ```bash
56
+ # 1. Sair do venv atual (se estiver ativo)
57
+ deactivate
58
+
59
+ # 2. (Opcional) Remover venv antigo
60
+ rm -rf ~/.venvs/raijin
61
+
62
+ # 3. Criar venv novo
63
+ python3 -m venv ~/.venvs/raijin
64
+ source ~/.venvs/raijin/bin/activate
65
+ pip install -U pip setuptools
66
+
67
+ # 4. Instalar a versão mais recente
68
+ pip install -U raijin-server
69
+
70
+ # 5. Rodar usando root preservando o venv
71
+ sudo -E ~/.venvs/raijin/bin/raijin-server --version
72
+ sudo -E ~/.venvs/raijin/bin/raijin-server validate
73
+ sudo -E ~/.venvs/raijin/bin/raijin-server full-install
74
+
75
+ # 6. Para sair do venv quando terminar
76
+ deactivate
77
+ ```
78
+
79
+ > **Nota**: O `-E` no sudo preserva as variáveis de ambiente, garantindo que o Python use o venv correto mesmo como root.
80
+
51
81
  ## Uso rapido
52
82
 
53
83
  ### Validar Sistema
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = raijin-server
3
- version = 0.2.2
3
+ version = 0.2.4
4
4
  description = CLI para automacao de setup e hardening de servidores Ubuntu Server.
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -1,5 +1,5 @@
1
1
  """Pacote principal do CLI Raijin Server."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.4"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -470,6 +470,83 @@ def sanitize(ctx: typer.Context) -> None:
470
470
  _run_module(ctx, "sanitize")
471
471
 
472
472
 
473
+ # ============================================================================
474
+ # Subcomandos Cert-Manager
475
+ # ============================================================================
476
+ cert_app = typer.Typer(help="Comandos para gerenciamento do cert-manager")
477
+ app.add_typer(cert_app, name="cert")
478
+
479
+
480
+ @cert_app.command(name="install")
481
+ def cert_install(ctx: typer.Context) -> None:
482
+ """Instala cert-manager e configura ClusterIssuer interativamente."""
483
+ _run_module(ctx, "cert_manager")
484
+
485
+
486
+ @cert_app.command(name="status")
487
+ def cert_status(ctx: typer.Context) -> None:
488
+ """Exibe status detalhado do cert-manager, pods, webhook e certificados."""
489
+ exec_ctx = ctx.obj or ExecutionContext()
490
+ cert_manager.status(exec_ctx)
491
+
492
+
493
+ @cert_app.command(name="diagnose")
494
+ def cert_diagnose(ctx: typer.Context) -> None:
495
+ """Executa diagnóstico completo para troubleshooting do cert-manager."""
496
+ exec_ctx = ctx.obj or ExecutionContext()
497
+ cert_manager.diagnose(exec_ctx)
498
+
499
+
500
+ @cert_app.command(name="list-certs")
501
+ def cert_list(ctx: typer.Context) -> None:
502
+ """Lista todos os certificados no cluster."""
503
+ import subprocess
504
+
505
+ typer.secho("\n📜 Certificados no Cluster", fg=typer.colors.CYAN, bold=True)
506
+ try:
507
+ result = subprocess.run(
508
+ [
509
+ "kubectl", "get", "certificates", "-A",
510
+ "-o", "wide"
511
+ ],
512
+ capture_output=False,
513
+ timeout=15,
514
+ )
515
+ if result.returncode != 0:
516
+ typer.secho("Nenhum certificado encontrado ou erro ao listar.", fg=typer.colors.YELLOW)
517
+ except Exception as e:
518
+ typer.secho(f"Erro: {e}", fg=typer.colors.RED)
519
+
520
+
521
+ @cert_app.command(name="list-issuers")
522
+ def cert_list_issuers(ctx: typer.Context) -> None:
523
+ """Lista todos os ClusterIssuers e Issuers."""
524
+ import subprocess
525
+
526
+ typer.secho("\n🔐 ClusterIssuers", fg=typer.colors.CYAN, bold=True)
527
+ try:
528
+ subprocess.run(
529
+ ["kubectl", "get", "clusterissuers", "-o", "wide"],
530
+ timeout=15,
531
+ )
532
+ except Exception:
533
+ pass
534
+
535
+ typer.secho("\n🔐 Issuers (por namespace)", fg=typer.colors.CYAN, bold=True)
536
+ try:
537
+ subprocess.run(
538
+ ["kubectl", "get", "issuers", "-A", "-o", "wide"],
539
+ timeout=15,
540
+ )
541
+ except Exception:
542
+ pass
543
+
544
+
545
+ # ============================================================================
546
+ # Comandos Existentes
547
+ # ============================================================================
548
+
549
+
473
550
  @app.command(name="bootstrap")
474
551
  def bootstrap_cmd(ctx: typer.Context) -> None:
475
552
  """Instala todas as ferramentas necessarias: helm, kubectl, istioctl, velero, containerd."""
@@ -264,8 +264,67 @@ def verify_helm_chart(release: str, namespace: str, ctx: ExecutionContext) -> bo
264
264
 
265
265
 
266
266
  def verify_cert_manager(ctx: ExecutionContext) -> bool:
267
- """Health check para cert-manager."""
268
- return verify_helm_chart("cert-manager", CERT_NS, ctx)
267
+ """Health check completo para cert-manager."""
268
+ logger.info("Verificando health check: cert-manager")
269
+ typer.secho("\n=== Health Check: Cert-Manager ===", fg=typer.colors.CYAN)
270
+
271
+ all_ok = True
272
+
273
+ # Verifica release Helm
274
+ ok, status = check_helm_release("cert-manager", CERT_NS, ctx)
275
+ if ok:
276
+ typer.secho(f" ✓ Release cert-manager: {status}", fg=typer.colors.GREEN)
277
+ else:
278
+ typer.secho(f" ✗ Release cert-manager: {status}", fg=typer.colors.RED)
279
+ return False
280
+
281
+ # Verifica pods
282
+ if not check_k8s_pods_in_namespace(CERT_NS, ctx, timeout=180):
283
+ all_ok = False
284
+
285
+ # Verifica CRDs
286
+ if not ctx.dry_run:
287
+ try:
288
+ import subprocess
289
+ result = subprocess.run(
290
+ ["kubectl", "get", "crd", "certificates.cert-manager.io"],
291
+ capture_output=True,
292
+ timeout=10,
293
+ )
294
+ if result.returncode == 0:
295
+ typer.secho(" ✓ CRDs instalados", fg=typer.colors.GREEN)
296
+ else:
297
+ typer.secho(" ✗ CRDs não encontrados", fg=typer.colors.RED)
298
+ all_ok = False
299
+ except Exception as e:
300
+ typer.secho(f" ✗ Erro ao verificar CRDs: {e}", fg=typer.colors.RED)
301
+ all_ok = False
302
+
303
+ # Verifica webhook ready
304
+ if not ctx.dry_run:
305
+ try:
306
+ import subprocess
307
+ result = subprocess.run(
308
+ [
309
+ "kubectl", "get", "deployment", "cert-manager-webhook",
310
+ "-n", CERT_NS,
311
+ "-o", "jsonpath={.status.readyReplicas}"
312
+ ],
313
+ capture_output=True,
314
+ text=True,
315
+ timeout=10,
316
+ )
317
+ ready = result.returncode == 0 and result.stdout.strip() and int(result.stdout.strip()) >= 1
318
+ if ready:
319
+ typer.secho(" ✓ Webhook pronto", fg=typer.colors.GREEN)
320
+ else:
321
+ typer.secho(" ✗ Webhook não está pronto", fg=typer.colors.RED)
322
+ all_ok = False
323
+ except Exception as e:
324
+ typer.secho(f" ✗ Erro ao verificar webhook: {e}", fg=typer.colors.RED)
325
+ all_ok = False
326
+
327
+ return all_ok
269
328
 
270
329
 
271
330
  def verify_secrets(ctx: ExecutionContext) -> bool:
@@ -244,7 +244,15 @@ def _resolve_tls_secret() -> str | None:
244
244
  return secret.strip() or None
245
245
 
246
246
 
247
- def _build_manifest(host: str, tls_secret: str | None) -> str:
247
+ def _resolve_ip_access() -> bool:
248
+ """Pergunta se deseja acesso via IP direto (para testes)."""
249
+ env_ip = os.environ.get("APOKOLIPS_IP_ACCESS")
250
+ if env_ip:
251
+ return env_ip.strip().lower() in ("1", "true", "yes")
252
+ return typer.confirm("Habilitar acesso via IP direto? (apenas para testes)", default=True)
253
+
254
+
255
+ def _build_manifest(host: str, tls_secret: str | None, ip_access: bool = False) -> str:
248
256
  html_block = indent(HTML_TEMPLATE.strip("\n"), " " * 4)
249
257
  tls_block = ""
250
258
  if tls_secret:
@@ -254,6 +262,20 @@ def _build_manifest(host: str, tls_secret: str | None) -> str:
254
262
  f" - {host}\n"
255
263
  f" secretName: {tls_secret}\n"
256
264
  )
265
+
266
+ # Regra adicional para acesso via IP (sem host)
267
+ ip_rule = ""
268
+ if ip_access:
269
+ ip_rule = """
270
+ - http:
271
+ paths:
272
+ - path: /
273
+ pathType: Prefix
274
+ backend:
275
+ service:
276
+ name: apokolips-demo
277
+ port:
278
+ number: 80"""
257
279
 
258
280
  template = """\
259
281
  apiVersion: v1
@@ -340,12 +362,13 @@ spec:
340
362
  service:
341
363
  name: apokolips-demo
342
364
  port:
343
- number: 80
365
+ number: 80__IP_RULE__
344
366
  __TLS__
345
367
  """
346
368
 
347
369
  manifest = template.format(namespace=NAMESPACE, host=host)
348
370
  manifest = manifest.replace("__HTML__", html_block)
371
+ manifest = manifest.replace("__IP_RULE__", ip_rule)
349
372
  manifest = manifest.replace("__TLS__", tls_block.rstrip())
350
373
  return f"{manifest.strip()}\n"
351
374
 
@@ -354,7 +377,8 @@ def run(ctx: ExecutionContext) -> None:
354
377
  ensure_tool("kubectl", ctx, install_hint="Instale kubectl para aplicar o manifesto do site.")
355
378
  host = _resolve_host()
356
379
  tls_secret = _resolve_tls_secret()
357
- manifest = _build_manifest(host, tls_secret)
380
+ ip_access = _resolve_ip_access()
381
+ manifest = _build_manifest(host, tls_secret, ip_access)
358
382
 
359
383
  typer.echo("Gerando manifesto Apokolips...")
360
384
  write_file(TMP_MANIFEST, manifest, ctx)
@@ -369,10 +393,22 @@ def run(ctx: ExecutionContext) -> None:
369
393
  typer.echo(f" • Host: {host}")
370
394
  if tls_secret:
371
395
  typer.echo(f" • Secret TLS: {tls_secret}")
396
+ if ip_access:
397
+ typer.secho(" • Acesso via IP: HABILITADO (apenas para testes)", fg=typer.colors.YELLOW)
398
+
372
399
  typer.echo("\nTestes sugeridos:")
373
- typer.echo(f" curl -H 'Host: {host}' https://<IP_DO_LOAD_BALANCER>/ --insecure")
400
+ if ip_access:
401
+ typer.echo(" # Acesso direto via IP (teste):")
402
+ typer.echo(" curl http://<IP_DO_SERVIDOR>/")
403
+ typer.echo("")
404
+ typer.echo(f" # Acesso via DNS (produção):")
405
+ typer.echo(f" curl -H 'Host: {host}' http://<IP_DO_LOAD_BALANCER>/")
374
406
  typer.echo(f" kubectl -n {NAMESPACE} get ingress {NAMESPACE}")
375
407
  typer.echo(f" kubectl -n {NAMESPACE} get pods")
376
408
 
409
+ if ip_access:
410
+ typer.secho("\n⚠️ Lembre-se de desabilitar o acesso via IP após configurar o DNS!", fg=typer.colors.YELLOW)
411
+ typer.echo(" Rode novamente com APOKOLIPS_IP_ACCESS=false ou responda 'não' na pergunta.")
412
+
377
413
  typer.echo("\nPara remover:")
378
414
  typer.echo(f" kubectl delete namespace {NAMESPACE}")