raijin-server 0.2.4__tar.gz → 0.2.6__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.
- {raijin_server-0.2.4/src/raijin_server.egg-info → raijin_server-0.2.6}/PKG-INFO +35 -1
- {raijin_server-0.2.4 → raijin_server-0.2.6}/README.md +34 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/setup.cfg +1 -1
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/cert_manager.py +333 -24
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/scripts/checklist.sh +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/scripts/install.sh +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/scripts/pre-deploy-check.sh +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/utils.py +227 -2
- {raijin_server-0.2.4 → raijin_server-0.2.6/src/raijin_server.egg-info}/PKG-INFO +35 -1
- {raijin_server-0.2.4 → raijin_server-0.2.6}/LICENSE +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/pyproject.toml +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/__init__.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/cli.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/config.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/healthchecks.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/__init__.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/apokolips_demo.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/bootstrap.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/calico.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/essentials.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/firewall.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/full_install.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/grafana.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/hardening.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/harness.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/istio.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/kafka.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/kong.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/kubernetes.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/loki.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/minio.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/network.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/observability_dashboards.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/observability_ingress.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/prometheus.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/sanitize.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/secrets.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/ssh_hardening.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/traefik.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/velero.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/vpn.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/scripts/__init__.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/scripts/log_size_metric.sh +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/validators.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server.egg-info/SOURCES.txt +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server.egg-info/dependency_links.txt +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server.egg-info/entry_points.txt +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server.egg-info/requires.txt +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server.egg-info/top_level.txt +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/tests/test_full_install_sequence.py +0 -0
- {raijin_server-0.2.4 → raijin_server-0.2.6}/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.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -37,6 +37,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
37
37
|
|
|
38
38
|
**✨ Versão Auditada e Resiliente para Produção**
|
|
39
39
|
|
|
40
|
+
## Links úteis
|
|
41
|
+
|
|
42
|
+
- Repositório: https://github.com/rafaelluisdacostacoelho/raijin-server
|
|
43
|
+
- Documentação completa: [docs/INFRASTRUCTURE_GUIDE.md](docs/INFRASTRUCTURE_GUIDE.md)
|
|
44
|
+
- Arquitetura: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
45
|
+
- Auditoria: [AUDIT.md](AUDIT.md)
|
|
46
|
+
- Segurança: [SECURITY.md](SECURITY.md)
|
|
47
|
+
|
|
40
48
|
## Destaques
|
|
41
49
|
|
|
42
50
|
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
@@ -405,6 +413,32 @@ pytest
|
|
|
405
413
|
ruff check src tests
|
|
406
414
|
```
|
|
407
415
|
|
|
416
|
+
## Publicar no PyPI (Twine)
|
|
417
|
+
|
|
418
|
+
O Twine é a ferramenta oficial para enviar pacotes Python ao PyPI com upload seguro (HTTPS e checagem de hash). Use sempre um token de API.
|
|
419
|
+
|
|
420
|
+
Passo a passo:
|
|
421
|
+
```bash
|
|
422
|
+
# 1) Gere artefatos
|
|
423
|
+
python -m build --sdist --wheel --outdir dist/
|
|
424
|
+
|
|
425
|
+
# 2) Configure o token (crie em https://pypi.org/manage/account/token/)
|
|
426
|
+
export TWINE_USERNAME=__token__
|
|
427
|
+
export TWINE_PASSWORD="<seu-token>"
|
|
428
|
+
|
|
429
|
+
# 3) Envie para o PyPI
|
|
430
|
+
python -m twine upload dist/*
|
|
431
|
+
|
|
432
|
+
# 4) Verifique instalação
|
|
433
|
+
python -m pip install -U raijin-server
|
|
434
|
+
raijin-server --version
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Boas práticas:
|
|
438
|
+
- Use venv dedicado para publicar (`python -m venv ~/.venvs/publish && source ~/.venvs/publish/bin/activate`).
|
|
439
|
+
- Nunca commite ou exponha o token; mantenha em variável de ambiente/secret manager.
|
|
440
|
+
- Sempre suba primeiro para TestPyPI se quiser validar (`--repository testpypi`).
|
|
441
|
+
|
|
408
442
|
## Acesso remoto seguro (VPN + SSH)
|
|
409
443
|
|
|
410
444
|
Execute `raijin-server ssh-hardening` para aplicar as politicas abaixo automaticamente e `raijin-server vpn` para subir o servidor WireGuard com um cliente inicial. Use `--dry-run` se quiser apenas revisar os comandos.
|
|
@@ -4,6 +4,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
4
4
|
|
|
5
5
|
**✨ Versão Auditada e Resiliente para Produção**
|
|
6
6
|
|
|
7
|
+
## Links úteis
|
|
8
|
+
|
|
9
|
+
- Repositório: https://github.com/rafaelluisdacostacoelho/raijin-server
|
|
10
|
+
- Documentação completa: [docs/INFRASTRUCTURE_GUIDE.md](docs/INFRASTRUCTURE_GUIDE.md)
|
|
11
|
+
- Arquitetura: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
12
|
+
- Auditoria: [AUDIT.md](AUDIT.md)
|
|
13
|
+
- Segurança: [SECURITY.md](SECURITY.md)
|
|
14
|
+
|
|
7
15
|
## Destaques
|
|
8
16
|
|
|
9
17
|
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
@@ -372,6 +380,32 @@ pytest
|
|
|
372
380
|
ruff check src tests
|
|
373
381
|
```
|
|
374
382
|
|
|
383
|
+
## Publicar no PyPI (Twine)
|
|
384
|
+
|
|
385
|
+
O Twine é a ferramenta oficial para enviar pacotes Python ao PyPI com upload seguro (HTTPS e checagem de hash). Use sempre um token de API.
|
|
386
|
+
|
|
387
|
+
Passo a passo:
|
|
388
|
+
```bash
|
|
389
|
+
# 1) Gere artefatos
|
|
390
|
+
python -m build --sdist --wheel --outdir dist/
|
|
391
|
+
|
|
392
|
+
# 2) Configure o token (crie em https://pypi.org/manage/account/token/)
|
|
393
|
+
export TWINE_USERNAME=__token__
|
|
394
|
+
export TWINE_PASSWORD="<seu-token>"
|
|
395
|
+
|
|
396
|
+
# 3) Envie para o PyPI
|
|
397
|
+
python -m twine upload dist/*
|
|
398
|
+
|
|
399
|
+
# 4) Verifique instalação
|
|
400
|
+
python -m pip install -U raijin-server
|
|
401
|
+
raijin-server --version
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Boas práticas:
|
|
405
|
+
- Use venv dedicado para publicar (`python -m venv ~/.venvs/publish && source ~/.venvs/publish/bin/activate`).
|
|
406
|
+
- Nunca commite ou exponha o token; mantenha em variável de ambiente/secret manager.
|
|
407
|
+
- Sempre suba primeiro para TestPyPI se quiser validar (`--repository testpypi`).
|
|
408
|
+
|
|
375
409
|
## Acesso remoto seguro (VPN + SSH)
|
|
376
410
|
|
|
377
411
|
Execute `raijin-server ssh-hardening` para aplicar as politicas abaixo automaticamente e `raijin-server vpn` para subir o servidor WireGuard com um cliente inicial. Use `--dry-run` se quiser apenas revisar os comandos.
|
|
@@ -432,38 +432,347 @@ def _wait_for_webhook_ready(ctx: ExecutionContext, timeout: int = WEBHOOK_READY_
|
|
|
432
432
|
# Instalação e Configuração
|
|
433
433
|
# =============================================================================
|
|
434
434
|
|
|
435
|
-
def
|
|
436
|
-
"""
|
|
437
|
-
|
|
435
|
+
def _test_helm_repo_connectivity(ctx: ExecutionContext) -> bool:
|
|
436
|
+
"""Testa conectividade com o repositório Helm antes de instalar."""
|
|
437
|
+
if ctx.dry_run:
|
|
438
|
+
return True
|
|
439
|
+
|
|
440
|
+
logger.info("Testando conectividade com charts.jetstack.io")
|
|
441
|
+
typer.echo(" [1/5] Testando conectividade com charts.jetstack.io...")
|
|
438
442
|
|
|
439
443
|
try:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
repo_url=CHART_REPO,
|
|
447
|
-
create_namespace=True,
|
|
448
|
-
extra_args=[
|
|
449
|
-
"--set", "installCRDs=true",
|
|
450
|
-
"--set", "webhook.timeoutSeconds=30",
|
|
451
|
-
"--set", "startupapicheck.timeout=5m",
|
|
452
|
-
"--set", "startupapicheck.enabled=true",
|
|
453
|
-
# Aumenta recursos para ambientes mais lentos
|
|
454
|
-
"--set", "webhook.replicaCount=1",
|
|
455
|
-
"--set", "cainjector.replicaCount=1",
|
|
456
|
-
"--wait", # Espera o Helm considerar o release deployed
|
|
457
|
-
"--timeout", "10m",
|
|
458
|
-
],
|
|
444
|
+
start = time.time()
|
|
445
|
+
result = subprocess.run(
|
|
446
|
+
["curl", "-sI", "--connect-timeout", "15", f"{CHART_REPO}/index.yaml"],
|
|
447
|
+
capture_output=True,
|
|
448
|
+
text=True,
|
|
449
|
+
timeout=20,
|
|
459
450
|
)
|
|
451
|
+
elapsed = time.time() - start
|
|
452
|
+
|
|
453
|
+
if result.returncode == 0 and "200" in result.stdout:
|
|
454
|
+
logger.info(f"Repositório Helm acessível em {elapsed:.2f}s")
|
|
455
|
+
typer.secho(f" ✓ Repositório Helm acessível ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
456
|
+
return True
|
|
457
|
+
else:
|
|
458
|
+
logger.error(f"Repositório retornou erro: {result.stdout[:200]}")
|
|
459
|
+
typer.secho(f" ✗ Repositório retornou erro: {result.stdout[:100]}", fg=typer.colors.RED)
|
|
460
|
+
return False
|
|
461
|
+
except subprocess.TimeoutExpired:
|
|
462
|
+
logger.error("Timeout ao conectar com charts.jetstack.io")
|
|
463
|
+
typer.secho(" ✗ Timeout ao conectar com charts.jetstack.io (>15s)", fg=typer.colors.RED)
|
|
464
|
+
return False
|
|
465
|
+
except Exception as e:
|
|
466
|
+
logger.error(f"Erro de conectividade: {e}")
|
|
467
|
+
typer.secho(f" ✗ Erro de conectividade: {e}", fg=typer.colors.RED)
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _test_image_registry_connectivity(ctx: ExecutionContext) -> bool:
|
|
472
|
+
"""Testa conectividade com o registry de imagens."""
|
|
473
|
+
if ctx.dry_run:
|
|
460
474
|
return True
|
|
475
|
+
|
|
476
|
+
logger.info("Testando conectividade com quay.io (registry de imagens)")
|
|
477
|
+
typer.echo(" [2/5] Testando conectividade com quay.io (registry de imagens)...")
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
start = time.time()
|
|
481
|
+
result = subprocess.run(
|
|
482
|
+
["curl", "-sI", "--connect-timeout", "15", "https://quay.io/v2/"],
|
|
483
|
+
capture_output=True,
|
|
484
|
+
text=True,
|
|
485
|
+
timeout=20,
|
|
486
|
+
)
|
|
487
|
+
elapsed = time.time() - start
|
|
488
|
+
|
|
489
|
+
# quay.io retorna 401 para /v2/ sem auth, mas isso significa que está acessível
|
|
490
|
+
if result.returncode == 0 and ("200" in result.stdout or "401" in result.stdout):
|
|
491
|
+
logger.info(f"Registry quay.io acessível em {elapsed:.2f}s")
|
|
492
|
+
typer.secho(f" ✓ Registry quay.io acessível ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
493
|
+
return True
|
|
494
|
+
else:
|
|
495
|
+
logger.warning(f"Registry pode estar inacessível: {result.stdout[:100]}")
|
|
496
|
+
typer.secho(f" ⚠ Registry pode estar inacessível", fg=typer.colors.YELLOW)
|
|
497
|
+
return True # Não bloqueia, apenas avisa
|
|
461
498
|
except Exception as e:
|
|
462
|
-
|
|
463
|
-
|
|
499
|
+
logger.warning(f"Não foi possível testar registry: {e}")
|
|
500
|
+
typer.secho(f" ⚠ Não foi possível testar registry: {e}", fg=typer.colors.YELLOW)
|
|
501
|
+
return True # Não bloqueia
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def _add_helm_repo(ctx: ExecutionContext) -> bool:
|
|
505
|
+
"""Adiciona e atualiza o repositório Helm."""
|
|
506
|
+
if ctx.dry_run:
|
|
507
|
+
typer.echo(" [3/5] [dry-run] Adicionando repositório Helm jetstack...")
|
|
508
|
+
return True
|
|
509
|
+
|
|
510
|
+
logger.info("Adicionando repositório Helm jetstack")
|
|
511
|
+
typer.echo(" [3/5] Adicionando repositório Helm jetstack...")
|
|
512
|
+
|
|
513
|
+
try:
|
|
514
|
+
start = time.time()
|
|
515
|
+
|
|
516
|
+
# Adiciona repo
|
|
517
|
+
result = subprocess.run(
|
|
518
|
+
["helm", "repo", "add", "jetstack", CHART_REPO, "--force-update"],
|
|
519
|
+
capture_output=True,
|
|
520
|
+
text=True,
|
|
521
|
+
timeout=60,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
if result.returncode != 0:
|
|
525
|
+
logger.error(f"Falha ao adicionar repo: {result.stderr}")
|
|
526
|
+
typer.secho(f" ✗ Falha ao adicionar repo: {result.stderr[:100]}", fg=typer.colors.RED)
|
|
527
|
+
return False
|
|
528
|
+
|
|
529
|
+
elapsed_add = time.time() - start
|
|
530
|
+
logger.info(f"Repo adicionado em {elapsed_add:.2f}s")
|
|
531
|
+
typer.echo(f" Repo adicionado ({elapsed_add:.2f}s)")
|
|
532
|
+
|
|
533
|
+
# Atualiza repo
|
|
534
|
+
typer.echo(" Atualizando índice do repositório...")
|
|
535
|
+
start = time.time()
|
|
536
|
+
|
|
537
|
+
result = subprocess.run(
|
|
538
|
+
["helm", "repo", "update", "jetstack"],
|
|
539
|
+
capture_output=True,
|
|
540
|
+
text=True,
|
|
541
|
+
timeout=120,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
elapsed_update = time.time() - start
|
|
545
|
+
|
|
546
|
+
if result.returncode != 0:
|
|
547
|
+
logger.error(f"Falha ao atualizar repo: {result.stderr}")
|
|
548
|
+
typer.secho(f" ✗ Falha ao atualizar repo: {result.stderr[:100]}", fg=typer.colors.RED)
|
|
549
|
+
return False
|
|
550
|
+
|
|
551
|
+
logger.info(f"Repo atualizado em {elapsed_update:.2f}s")
|
|
552
|
+
typer.secho(f" ✓ Repositório configurado ({elapsed_add + elapsed_update:.2f}s total)", fg=typer.colors.GREEN)
|
|
553
|
+
return True
|
|
554
|
+
|
|
555
|
+
except subprocess.TimeoutExpired:
|
|
556
|
+
logger.error("Timeout ao configurar repositório Helm")
|
|
557
|
+
typer.secho(" ✗ Timeout ao configurar repositório (>60s)", fg=typer.colors.RED)
|
|
558
|
+
return False
|
|
559
|
+
except Exception as e:
|
|
560
|
+
logger.error(f"Erro ao configurar repo: {e}")
|
|
561
|
+
typer.secho(f" ✗ Erro: {e}", fg=typer.colors.RED)
|
|
464
562
|
return False
|
|
465
563
|
|
|
466
564
|
|
|
565
|
+
def _run_helm_install(ctx: ExecutionContext) -> bool:
|
|
566
|
+
"""Executa o helm upgrade --install."""
|
|
567
|
+
if ctx.dry_run:
|
|
568
|
+
typer.echo(" [4/5] [dry-run] Executando helm upgrade --install...")
|
|
569
|
+
return True
|
|
570
|
+
|
|
571
|
+
logger.info("Executando helm upgrade --install cert-manager")
|
|
572
|
+
typer.echo(" [4/5] Executando helm upgrade --install cert-manager...")
|
|
573
|
+
typer.echo(" (isso pode levar vários minutos)")
|
|
574
|
+
|
|
575
|
+
cmd = [
|
|
576
|
+
"helm", "upgrade", "--install", "cert-manager", "jetstack/cert-manager",
|
|
577
|
+
"-n", NAMESPACE,
|
|
578
|
+
"--create-namespace",
|
|
579
|
+
"--set", "installCRDs=true",
|
|
580
|
+
"--set", "webhook.timeoutSeconds=30",
|
|
581
|
+
"--set", "startupapicheck.timeout=5m",
|
|
582
|
+
"--set", "startupapicheck.enabled=true",
|
|
583
|
+
"--set", "webhook.replicaCount=1",
|
|
584
|
+
"--set", "cainjector.replicaCount=1",
|
|
585
|
+
"--wait",
|
|
586
|
+
"--timeout", "15m",
|
|
587
|
+
"--debug", # Mais logs
|
|
588
|
+
]
|
|
589
|
+
|
|
590
|
+
logger.info(f"Comando: {' '.join(cmd)}")
|
|
591
|
+
|
|
592
|
+
try:
|
|
593
|
+
start = time.time()
|
|
594
|
+
|
|
595
|
+
# Executa com output em tempo real para ver progresso
|
|
596
|
+
process = subprocess.Popen(
|
|
597
|
+
cmd,
|
|
598
|
+
stdout=subprocess.PIPE,
|
|
599
|
+
stderr=subprocess.STDOUT,
|
|
600
|
+
text=True,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
output_lines = []
|
|
604
|
+
last_log_time = time.time()
|
|
605
|
+
|
|
606
|
+
while True:
|
|
607
|
+
line = process.stdout.readline()
|
|
608
|
+
if not line and process.poll() is not None:
|
|
609
|
+
break
|
|
610
|
+
if line:
|
|
611
|
+
output_lines.append(line)
|
|
612
|
+
logger.debug(line.strip())
|
|
613
|
+
|
|
614
|
+
# Mostra progresso a cada 30s
|
|
615
|
+
if time.time() - last_log_time > 30:
|
|
616
|
+
elapsed = int(time.time() - start)
|
|
617
|
+
typer.echo(f" ... ainda instalando ({elapsed}s)")
|
|
618
|
+
last_log_time = time.time()
|
|
619
|
+
|
|
620
|
+
elapsed = time.time() - start
|
|
621
|
+
return_code = process.poll()
|
|
622
|
+
|
|
623
|
+
if return_code == 0:
|
|
624
|
+
logger.info(f"Helm install concluído em {elapsed:.2f}s")
|
|
625
|
+
typer.secho(f" ✓ Helm install concluído ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
626
|
+
return True
|
|
627
|
+
else:
|
|
628
|
+
output = "".join(output_lines[-20:]) # Últimas 20 linhas
|
|
629
|
+
logger.error(f"Helm install falhou (código {return_code}): {output}")
|
|
630
|
+
typer.secho(f" ✗ Helm install falhou (código {return_code})", fg=typer.colors.RED)
|
|
631
|
+
|
|
632
|
+
# Mostra as últimas linhas do erro
|
|
633
|
+
typer.echo("\n Últimas linhas do log:")
|
|
634
|
+
for line in output_lines[-10:]:
|
|
635
|
+
typer.echo(f" {line.strip()}")
|
|
636
|
+
|
|
637
|
+
return False
|
|
638
|
+
|
|
639
|
+
except Exception as e:
|
|
640
|
+
logger.error(f"Erro durante helm install: {e}")
|
|
641
|
+
typer.secho(f" ✗ Erro: {e}", fg=typer.colors.RED)
|
|
642
|
+
return False
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _verify_installation(ctx: ExecutionContext) -> bool:
|
|
646
|
+
"""Verifica se os pods estão rodando."""
|
|
647
|
+
if ctx.dry_run:
|
|
648
|
+
typer.echo(" [5/5] [dry-run] Verificando instalação...")
|
|
649
|
+
return True
|
|
650
|
+
|
|
651
|
+
logger.info("Verificando pods do cert-manager")
|
|
652
|
+
typer.echo(" [5/5] Verificando pods do cert-manager...")
|
|
653
|
+
|
|
654
|
+
try:
|
|
655
|
+
result = subprocess.run(
|
|
656
|
+
["kubectl", "get", "pods", "-n", NAMESPACE, "-o", "wide"],
|
|
657
|
+
capture_output=True,
|
|
658
|
+
text=True,
|
|
659
|
+
timeout=30,
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
if result.returncode == 0:
|
|
663
|
+
typer.echo(f"\n{result.stdout}")
|
|
664
|
+
|
|
665
|
+
# Verifica se todos estão Running
|
|
666
|
+
if "Running" in result.stdout and "0/1" not in result.stdout:
|
|
667
|
+
logger.info("Todos os pods estão Running")
|
|
668
|
+
typer.secho(" ✓ Todos os pods estão Running", fg=typer.colors.GREEN)
|
|
669
|
+
return True
|
|
670
|
+
else:
|
|
671
|
+
logger.warning("Alguns pods podem não estar prontos")
|
|
672
|
+
typer.secho(" ⚠ Alguns pods podem não estar prontos", fg=typer.colors.YELLOW)
|
|
673
|
+
return True # Não falha, o wait_for_webhook vai verificar
|
|
674
|
+
else:
|
|
675
|
+
logger.error(f"Erro ao verificar pods: {result.stderr}")
|
|
676
|
+
return False
|
|
677
|
+
|
|
678
|
+
except Exception as e:
|
|
679
|
+
logger.error(f"Erro ao verificar pods: {e}")
|
|
680
|
+
return False
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def _install_cert_manager_helm(ctx: ExecutionContext) -> bool:
|
|
684
|
+
"""Instala cert-manager via Helm com logs detalhados."""
|
|
685
|
+
typer.secho("\n📦 Instalando cert-manager via Helm...", fg=typer.colors.CYAN, bold=True)
|
|
686
|
+
logger.info("Iniciando instalação do cert-manager")
|
|
687
|
+
|
|
688
|
+
start_total = time.time()
|
|
689
|
+
|
|
690
|
+
# Etapa 1: Testa conectividade com repo Helm
|
|
691
|
+
if not _test_helm_repo_connectivity(ctx):
|
|
692
|
+
typer.secho(
|
|
693
|
+
"\n⚠ Problema de conectividade com o repositório Helm.",
|
|
694
|
+
fg=typer.colors.YELLOW,
|
|
695
|
+
)
|
|
696
|
+
typer.echo(" Teste manual: curl -sI https://charts.jetstack.io/index.yaml")
|
|
697
|
+
if not typer.confirm("Tentar instalar mesmo assim?", default=False):
|
|
698
|
+
return False
|
|
699
|
+
|
|
700
|
+
# Etapa 2: Testa conectividade com registry de imagens
|
|
701
|
+
_test_image_registry_connectivity(ctx)
|
|
702
|
+
|
|
703
|
+
# Etapa 3: Adiciona e atualiza repo Helm
|
|
704
|
+
if not _add_helm_repo(ctx):
|
|
705
|
+
return False
|
|
706
|
+
|
|
707
|
+
# Etapa 4: Executa helm install
|
|
708
|
+
if not _run_helm_install(ctx):
|
|
709
|
+
_show_diagnostic_info(ctx)
|
|
710
|
+
return False
|
|
711
|
+
|
|
712
|
+
# Etapa 5: Verifica instalação
|
|
713
|
+
_verify_installation(ctx)
|
|
714
|
+
|
|
715
|
+
elapsed_total = time.time() - start_total
|
|
716
|
+
logger.info(f"Instalação do cert-manager concluída em {elapsed_total:.2f}s")
|
|
717
|
+
typer.secho(f"\n✓ Instalação concluída em {elapsed_total:.2f}s", fg=typer.colors.GREEN)
|
|
718
|
+
|
|
719
|
+
return True
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def _show_diagnostic_info(ctx: ExecutionContext) -> None:
|
|
723
|
+
"""Mostra informações de diagnóstico quando falha."""
|
|
724
|
+
if ctx.dry_run:
|
|
725
|
+
return
|
|
726
|
+
|
|
727
|
+
typer.secho("\n🔍 Informações de diagnóstico:", fg=typer.colors.YELLOW, bold=True)
|
|
728
|
+
|
|
729
|
+
# Pods
|
|
730
|
+
typer.echo("\n Pods:")
|
|
731
|
+
try:
|
|
732
|
+
result = subprocess.run(
|
|
733
|
+
["kubectl", "get", "pods", "-n", NAMESPACE, "-o", "wide"],
|
|
734
|
+
capture_output=True,
|
|
735
|
+
text=True,
|
|
736
|
+
timeout=15,
|
|
737
|
+
)
|
|
738
|
+
if result.stdout:
|
|
739
|
+
for line in result.stdout.strip().split("\n"):
|
|
740
|
+
typer.echo(f" {line}")
|
|
741
|
+
except Exception:
|
|
742
|
+
typer.echo(" (não foi possível obter pods)")
|
|
743
|
+
|
|
744
|
+
# Eventos recentes
|
|
745
|
+
typer.echo("\n Eventos recentes:")
|
|
746
|
+
try:
|
|
747
|
+
result = subprocess.run(
|
|
748
|
+
["kubectl", "get", "events", "-n", NAMESPACE, "--sort-by=.lastTimestamp"],
|
|
749
|
+
capture_output=True,
|
|
750
|
+
text=True,
|
|
751
|
+
timeout=15,
|
|
752
|
+
)
|
|
753
|
+
if result.stdout:
|
|
754
|
+
lines = result.stdout.strip().split("\n")
|
|
755
|
+
for line in lines[-10:]: # Últimos 10 eventos
|
|
756
|
+
typer.echo(f" {line[:120]}")
|
|
757
|
+
except Exception:
|
|
758
|
+
typer.echo(" (não foi possível obter eventos)")
|
|
759
|
+
|
|
760
|
+
# Helm status
|
|
761
|
+
typer.echo("\n Helm release status:")
|
|
762
|
+
try:
|
|
763
|
+
result = subprocess.run(
|
|
764
|
+
["helm", "status", "cert-manager", "-n", NAMESPACE],
|
|
765
|
+
capture_output=True,
|
|
766
|
+
text=True,
|
|
767
|
+
timeout=15,
|
|
768
|
+
)
|
|
769
|
+
if result.stdout:
|
|
770
|
+
for line in result.stdout.strip().split("\n")[:15]:
|
|
771
|
+
typer.echo(f" {line}")
|
|
772
|
+
except Exception:
|
|
773
|
+
typer.echo(" (não foi possível obter status do Helm)")
|
|
774
|
+
|
|
775
|
+
|
|
467
776
|
def _apply_manifest_with_retry(
|
|
468
777
|
manifest: str,
|
|
469
778
|
ctx: ExecutionContext,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -299,6 +299,210 @@ def helm_repo_update(ctx: ExecutionContext) -> None:
|
|
|
299
299
|
run_cmd(["helm", "repo", "update"], ctx)
|
|
300
300
|
|
|
301
301
|
|
|
302
|
+
def _get_helm_release_status(release: str, namespace: str) -> str:
|
|
303
|
+
"""Retorna status do release Helm (lowercased) ou string vazia se nao existir."""
|
|
304
|
+
try:
|
|
305
|
+
import json
|
|
306
|
+
result = subprocess.run(
|
|
307
|
+
["helm", "status", release, "-n", namespace, "-o", "json"],
|
|
308
|
+
capture_output=True,
|
|
309
|
+
text=True,
|
|
310
|
+
timeout=30,
|
|
311
|
+
)
|
|
312
|
+
if result.returncode != 0 or not result.stdout:
|
|
313
|
+
return ""
|
|
314
|
+
data = json.loads(result.stdout)
|
|
315
|
+
return str(data.get("info", {}).get("status", "")).lower()
|
|
316
|
+
except Exception:
|
|
317
|
+
return ""
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _get_helm_release_history(release: str, namespace: str) -> list:
|
|
321
|
+
"""Retorna histórico do release Helm."""
|
|
322
|
+
try:
|
|
323
|
+
import json
|
|
324
|
+
result = subprocess.run(
|
|
325
|
+
["helm", "history", release, "-n", namespace, "-o", "json"],
|
|
326
|
+
capture_output=True,
|
|
327
|
+
text=True,
|
|
328
|
+
timeout=30,
|
|
329
|
+
)
|
|
330
|
+
if result.returncode != 0 or not result.stdout:
|
|
331
|
+
return []
|
|
332
|
+
return json.loads(result.stdout)
|
|
333
|
+
except Exception:
|
|
334
|
+
return []
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _diagnose_helm_release(release: str, namespace: str) -> None:
|
|
338
|
+
"""Mostra diagnóstico detalhado de um release Helm."""
|
|
339
|
+
typer.secho(f"\n🔍 Diagnóstico do release '{release}':", fg=typer.colors.YELLOW)
|
|
340
|
+
|
|
341
|
+
# Status atual
|
|
342
|
+
status = _get_helm_release_status(release, namespace)
|
|
343
|
+
typer.echo(f" Status atual: {status or '(não encontrado)'}")
|
|
344
|
+
|
|
345
|
+
# Histórico
|
|
346
|
+
history = _get_helm_release_history(release, namespace)
|
|
347
|
+
if history:
|
|
348
|
+
typer.echo(f" Histórico ({len(history)} revisões):")
|
|
349
|
+
for rev in history[-5:]: # Últimas 5 revisões
|
|
350
|
+
typer.echo(f" Rev {rev.get('revision')}: {rev.get('status')} - {rev.get('description', '')[:50]}")
|
|
351
|
+
|
|
352
|
+
# Secrets do Helm (onde guarda estado)
|
|
353
|
+
try:
|
|
354
|
+
result = subprocess.run(
|
|
355
|
+
["kubectl", "get", "secrets", "-n", namespace, "-l", f"name={release},owner=helm", "-o", "name"],
|
|
356
|
+
capture_output=True,
|
|
357
|
+
text=True,
|
|
358
|
+
timeout=15,
|
|
359
|
+
)
|
|
360
|
+
if result.stdout.strip():
|
|
361
|
+
secrets = result.stdout.strip().split("\n")
|
|
362
|
+
typer.echo(f" Secrets do Helm: {len(secrets)}")
|
|
363
|
+
for s in secrets[-5:]:
|
|
364
|
+
typer.echo(f" {s}")
|
|
365
|
+
except Exception:
|
|
366
|
+
pass
|
|
367
|
+
|
|
368
|
+
# Pods relacionados
|
|
369
|
+
try:
|
|
370
|
+
result = subprocess.run(
|
|
371
|
+
["kubectl", "get", "pods", "-n", namespace, "-o", "wide", "--no-headers"],
|
|
372
|
+
capture_output=True,
|
|
373
|
+
text=True,
|
|
374
|
+
timeout=15,
|
|
375
|
+
)
|
|
376
|
+
if result.stdout.strip():
|
|
377
|
+
typer.echo(" Pods:")
|
|
378
|
+
for line in result.stdout.strip().split("\n")[:5]:
|
|
379
|
+
typer.echo(f" {line}")
|
|
380
|
+
except Exception:
|
|
381
|
+
pass
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _force_cleanup_helm_release(release: str, namespace: str) -> bool:
|
|
385
|
+
"""Limpeza forçada de release Helm travado - remove secrets diretamente."""
|
|
386
|
+
typer.secho(f" Limpeza forçada do release '{release}'...", fg=typer.colors.YELLOW)
|
|
387
|
+
logger.warning(f"Executando limpeza forçada do release {release} em {namespace}")
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
# 1. Primeiro tenta uninstall normal com --no-hooks (pula hooks que podem estar travando)
|
|
391
|
+
result = subprocess.run(
|
|
392
|
+
["helm", "uninstall", release, "-n", namespace, "--no-hooks", "--wait", "--timeout", "2m"],
|
|
393
|
+
capture_output=True,
|
|
394
|
+
text=True,
|
|
395
|
+
timeout=150,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
if result.returncode == 0:
|
|
399
|
+
typer.secho(f" ✓ Release removido via helm uninstall", fg=typer.colors.GREEN)
|
|
400
|
+
time.sleep(3)
|
|
401
|
+
return True
|
|
402
|
+
|
|
403
|
+
# 2. Se falhou, remove os secrets do Helm diretamente
|
|
404
|
+
typer.echo(" Helm uninstall falhou, removendo secrets diretamente...")
|
|
405
|
+
logger.warning("Removendo secrets do Helm diretamente")
|
|
406
|
+
|
|
407
|
+
# Lista secrets do Helm para este release
|
|
408
|
+
result = subprocess.run(
|
|
409
|
+
["kubectl", "get", "secrets", "-n", namespace, "-l", f"name={release},owner=helm", "-o", "name"],
|
|
410
|
+
capture_output=True,
|
|
411
|
+
text=True,
|
|
412
|
+
timeout=15,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
if result.stdout.strip():
|
|
416
|
+
secrets = result.stdout.strip().split("\n")
|
|
417
|
+
for secret in secrets:
|
|
418
|
+
secret_name = secret.replace("secret/", "")
|
|
419
|
+
subprocess.run(
|
|
420
|
+
["kubectl", "delete", "secret", secret_name, "-n", namespace],
|
|
421
|
+
capture_output=True,
|
|
422
|
+
timeout=30,
|
|
423
|
+
)
|
|
424
|
+
typer.echo(f" Removido: {secret_name}")
|
|
425
|
+
|
|
426
|
+
time.sleep(3)
|
|
427
|
+
typer.secho(f" ✓ Secrets do Helm removidos", fg=typer.colors.GREEN)
|
|
428
|
+
return True
|
|
429
|
+
else:
|
|
430
|
+
typer.echo(" Nenhum secret do Helm encontrado")
|
|
431
|
+
return True
|
|
432
|
+
|
|
433
|
+
except Exception as e:
|
|
434
|
+
logger.error(f"Erro na limpeza forçada: {e}")
|
|
435
|
+
typer.secho(f" ✗ Erro na limpeza: {e}", fg=typer.colors.RED)
|
|
436
|
+
return False
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _cleanup_pending_helm_release(release: str, namespace: str, ctx: ExecutionContext) -> None:
|
|
440
|
+
"""Remove release Helm em estado pendente que bloqueia novas operacoes."""
|
|
441
|
+
if ctx.dry_run:
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
status = _get_helm_release_status(release, namespace)
|
|
445
|
+
if not status:
|
|
446
|
+
return
|
|
447
|
+
|
|
448
|
+
# Estados que bloqueiam: pending-install, pending-upgrade, pending-rollback
|
|
449
|
+
if not status.startswith("pending"):
|
|
450
|
+
return
|
|
451
|
+
|
|
452
|
+
typer.secho(
|
|
453
|
+
f"\n⚠ Release '{release}' em estado '{status}' - bloqueando novas operações",
|
|
454
|
+
fg=typer.colors.YELLOW,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Mostra diagnóstico
|
|
458
|
+
_diagnose_helm_release(release, namespace)
|
|
459
|
+
|
|
460
|
+
typer.echo("\n Tentando recuperar...")
|
|
461
|
+
|
|
462
|
+
# 1. Tenta rollback primeiro (funciona para pending-upgrade)
|
|
463
|
+
if status == "pending-upgrade":
|
|
464
|
+
typer.echo(" Tentando rollback para versão anterior...")
|
|
465
|
+
result = subprocess.run(
|
|
466
|
+
["helm", "rollback", release, "-n", namespace, "--wait", "--timeout", "2m"],
|
|
467
|
+
capture_output=True,
|
|
468
|
+
text=True,
|
|
469
|
+
timeout=150,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
if result.returncode == 0:
|
|
473
|
+
new_status = _get_helm_release_status(release, namespace)
|
|
474
|
+
if not new_status.startswith("pending"):
|
|
475
|
+
typer.secho(f" ✓ Rollback bem-sucedido (status: {new_status})", fg=typer.colors.GREEN)
|
|
476
|
+
return
|
|
477
|
+
|
|
478
|
+
typer.echo(" Rollback não resolveu...")
|
|
479
|
+
|
|
480
|
+
# 2. Tenta uninstall normal
|
|
481
|
+
typer.echo(" Tentando helm uninstall...")
|
|
482
|
+
result = subprocess.run(
|
|
483
|
+
["helm", "uninstall", release, "-n", namespace, "--wait", "--timeout", "3m"],
|
|
484
|
+
capture_output=True,
|
|
485
|
+
text=True,
|
|
486
|
+
timeout=200,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if result.returncode == 0:
|
|
490
|
+
typer.secho(f" ✓ Release removido com sucesso", fg=typer.colors.GREEN)
|
|
491
|
+
time.sleep(3)
|
|
492
|
+
return
|
|
493
|
+
|
|
494
|
+
# 3. Se ainda falhou, força limpeza
|
|
495
|
+
typer.echo(" Uninstall normal falhou, tentando limpeza forçada...")
|
|
496
|
+
_force_cleanup_helm_release(release, namespace)
|
|
497
|
+
|
|
498
|
+
# Verifica resultado final
|
|
499
|
+
final_status = _get_helm_release_status(release, namespace)
|
|
500
|
+
if final_status:
|
|
501
|
+
typer.secho(f" ⚠ Release ainda existe com status: {final_status}", fg=typer.colors.YELLOW)
|
|
502
|
+
else:
|
|
503
|
+
typer.secho(f" ✓ Release '{release}' limpo com sucesso", fg=typer.colors.GREEN)
|
|
504
|
+
|
|
505
|
+
|
|
302
506
|
def helm_upgrade_install(
|
|
303
507
|
release: str,
|
|
304
508
|
chart: str,
|
|
@@ -311,9 +515,16 @@ def helm_upgrade_install(
|
|
|
311
515
|
create_namespace: bool = True,
|
|
312
516
|
extra_args: list[str] | None = None,
|
|
313
517
|
) -> None:
|
|
314
|
-
"""Executa helm upgrade --install com opcoes comuns.
|
|
518
|
+
"""Executa helm upgrade --install com opcoes comuns.
|
|
519
|
+
|
|
520
|
+
Automaticamente detecta e limpa releases em estado pendente antes de instalar.
|
|
521
|
+
"""
|
|
315
522
|
|
|
316
523
|
ensure_tool("helm", ctx, install_hint="Instale helm ou habilite dry-run para so visualizar.")
|
|
524
|
+
|
|
525
|
+
# Limpa releases pendentes antes de tentar instalar
|
|
526
|
+
_cleanup_pending_helm_release(release, namespace, ctx)
|
|
527
|
+
|
|
317
528
|
if repo and repo_url:
|
|
318
529
|
helm_repo_add(repo, repo_url, ctx)
|
|
319
530
|
helm_repo_update(ctx)
|
|
@@ -328,7 +539,21 @@ def helm_upgrade_install(
|
|
|
328
539
|
cmd.extend(["--set", value])
|
|
329
540
|
if extra_args:
|
|
330
541
|
cmd.extend(extra_args)
|
|
331
|
-
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
run_cmd(cmd, ctx)
|
|
545
|
+
except Exception as e:
|
|
546
|
+
err_text = str(e).lower()
|
|
547
|
+
# Se falhou por operacao em progresso, tenta limpar e reinstalar uma vez
|
|
548
|
+
if "another operation" in err_text and "in progress" in err_text:
|
|
549
|
+
typer.secho(
|
|
550
|
+
f"⚠ Helm detectou operacao pendente em '{release}'. Limpando e tentando novamente...",
|
|
551
|
+
fg=typer.colors.YELLOW,
|
|
552
|
+
)
|
|
553
|
+
_cleanup_pending_helm_release(release, namespace, ctx)
|
|
554
|
+
run_cmd(cmd, ctx)
|
|
555
|
+
else:
|
|
556
|
+
raise
|
|
332
557
|
|
|
333
558
|
|
|
334
559
|
def kubectl_apply(target: str, ctx: ExecutionContext) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: raijin-server
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -37,6 +37,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
37
37
|
|
|
38
38
|
**✨ Versão Auditada e Resiliente para Produção**
|
|
39
39
|
|
|
40
|
+
## Links úteis
|
|
41
|
+
|
|
42
|
+
- Repositório: https://github.com/rafaelluisdacostacoelho/raijin-server
|
|
43
|
+
- Documentação completa: [docs/INFRASTRUCTURE_GUIDE.md](docs/INFRASTRUCTURE_GUIDE.md)
|
|
44
|
+
- Arquitetura: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
45
|
+
- Auditoria: [AUDIT.md](AUDIT.md)
|
|
46
|
+
- Segurança: [SECURITY.md](SECURITY.md)
|
|
47
|
+
|
|
40
48
|
## Destaques
|
|
41
49
|
|
|
42
50
|
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
@@ -405,6 +413,32 @@ pytest
|
|
|
405
413
|
ruff check src tests
|
|
406
414
|
```
|
|
407
415
|
|
|
416
|
+
## Publicar no PyPI (Twine)
|
|
417
|
+
|
|
418
|
+
O Twine é a ferramenta oficial para enviar pacotes Python ao PyPI com upload seguro (HTTPS e checagem de hash). Use sempre um token de API.
|
|
419
|
+
|
|
420
|
+
Passo a passo:
|
|
421
|
+
```bash
|
|
422
|
+
# 1) Gere artefatos
|
|
423
|
+
python -m build --sdist --wheel --outdir dist/
|
|
424
|
+
|
|
425
|
+
# 2) Configure o token (crie em https://pypi.org/manage/account/token/)
|
|
426
|
+
export TWINE_USERNAME=__token__
|
|
427
|
+
export TWINE_PASSWORD="<seu-token>"
|
|
428
|
+
|
|
429
|
+
# 3) Envie para o PyPI
|
|
430
|
+
python -m twine upload dist/*
|
|
431
|
+
|
|
432
|
+
# 4) Verifique instalação
|
|
433
|
+
python -m pip install -U raijin-server
|
|
434
|
+
raijin-server --version
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Boas práticas:
|
|
438
|
+
- Use venv dedicado para publicar (`python -m venv ~/.venvs/publish && source ~/.venvs/publish/bin/activate`).
|
|
439
|
+
- Nunca commite ou exponha o token; mantenha em variável de ambiente/secret manager.
|
|
440
|
+
- Sempre suba primeiro para TestPyPI se quiser validar (`--repository testpypi`).
|
|
441
|
+
|
|
408
442
|
## Acesso remoto seguro (VPN + SSH)
|
|
409
443
|
|
|
410
444
|
Execute `raijin-server ssh-hardening` para aplicar as politicas abaixo automaticamente e `raijin-server vpn` para subir o servidor WireGuard com um cliente inicial. Use `--dry-run` se quiser apenas revisar os comandos.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/observability_dashboards.py
RENAMED
|
File without changes
|
{raijin_server-0.2.4 → raijin_server-0.2.6}/src/raijin_server/modules/observability_ingress.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|