raijin-server 0.3.2__tar.gz → 0.3.3__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.

Potentially problematic release.


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

Files changed (55) hide show
  1. {raijin_server-0.3.2/src/raijin_server.egg-info → raijin_server-0.3.3}/PKG-INFO +1 -1
  2. {raijin_server-0.3.2 → raijin_server-0.3.3}/setup.cfg +1 -1
  3. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/__init__.py +1 -1
  4. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/vpn.py +21 -6
  5. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/vpn_client.py +304 -2
  6. {raijin_server-0.3.2 → raijin_server-0.3.3/src/raijin_server.egg-info}/PKG-INFO +1 -1
  7. {raijin_server-0.3.2 → raijin_server-0.3.3}/LICENSE +0 -0
  8. {raijin_server-0.3.2 → raijin_server-0.3.3}/README.md +0 -0
  9. {raijin_server-0.3.2 → raijin_server-0.3.3}/pyproject.toml +0 -0
  10. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/cli.py +0 -0
  11. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/config.py +0 -0
  12. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/healthchecks.py +0 -0
  13. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/module_manager.py +0 -0
  14. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/__init__.py +0 -0
  15. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/apokolips_demo.py +0 -0
  16. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/bootstrap.py +0 -0
  17. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/calico.py +0 -0
  18. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/cert_manager.py +0 -0
  19. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/essentials.py +0 -0
  20. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/firewall.py +0 -0
  21. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/full_install.py +0 -0
  22. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/grafana.py +0 -0
  23. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/hardening.py +0 -0
  24. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/harness.py +0 -0
  25. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/internal_dns.py +0 -0
  26. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/istio.py +0 -0
  27. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/kafka.py +0 -0
  28. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/kong.py +0 -0
  29. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/kubernetes.py +0 -0
  30. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/loki.py +0 -0
  31. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/metallb.py +0 -0
  32. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/minio.py +0 -0
  33. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/network.py +0 -0
  34. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/observability_dashboards.py +0 -0
  35. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/observability_ingress.py +0 -0
  36. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/prometheus.py +0 -0
  37. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/sanitize.py +0 -0
  38. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/secrets.py +0 -0
  39. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/ssh_hardening.py +0 -0
  40. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/traefik.py +0 -0
  41. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/modules/velero.py +0 -0
  42. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/scripts/__init__.py +0 -0
  43. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/scripts/checklist.sh +0 -0
  44. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/scripts/install.sh +0 -0
  45. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/scripts/log_size_metric.sh +0 -0
  46. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/scripts/pre-deploy-check.sh +0 -0
  47. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/utils.py +0 -0
  48. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server/validators.py +0 -0
  49. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server.egg-info/SOURCES.txt +0 -0
  50. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server.egg-info/dependency_links.txt +0 -0
  51. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server.egg-info/entry_points.txt +0 -0
  52. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server.egg-info/requires.txt +0 -0
  53. {raijin_server-0.3.2 → raijin_server-0.3.3}/src/raijin_server.egg-info/top_level.txt +0 -0
  54. {raijin_server-0.3.2 → raijin_server-0.3.3}/tests/test_full_install_sequence.py +0 -0
  55. {raijin_server-0.3.2 → raijin_server-0.3.3}/tests/test_registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.3.2
3
+ Version: 0.3.3
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,6 +1,6 @@
1
1
  [metadata]
2
2
  name = raijin-server
3
- version = 0.3.2
3
+ version = 0.3.3
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.3.2"
3
+ __version__ = "0.3.3"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -173,12 +173,11 @@ def run(ctx: ExecutionContext) -> None:
173
173
  Address = {server_address}
174
174
  ListenPort = {listen_port}
175
175
  PrivateKey = {server_private}
176
- SaveConfig = true
177
176
  PostUp = iptables -A FORWARD -i {interface} -j ACCEPT; iptables -A FORWARD -o {interface} -j ACCEPT; iptables -t nat -A POSTROUTING -o {egress_iface} -j MASQUERADE
178
177
  PostDown = iptables -D FORWARD -i {interface} -j ACCEPT; iptables -D FORWARD -o {interface} -j ACCEPT; iptables -t nat -D POSTROUTING -o {egress_iface} -j MASQUERADE
179
178
 
180
- [Peer]
181
179
  # {client_name}
180
+ [Peer]
182
181
  PublicKey = {client_public}
183
182
  AllowedIPs = {client_address}
184
183
  """
@@ -210,8 +209,24 @@ def run(ctx: ExecutionContext) -> None:
210
209
  run_cmd(["systemctl", "enable", "--now", f"wg-quick@{interface}"], ctx)
211
210
 
212
211
  typer.secho("\n✓ WireGuard configurado com sucesso!", fg=typer.colors.GREEN, bold=True)
213
- typer.echo(f"Configuracao do servidor: {server_conf_path}")
212
+ typer.echo("\n" + "="*60)
213
+ typer.secho("INFORMAÇÕES IMPORTANTES - GUARDE ESTAS CHAVES:", fg=typer.colors.YELLOW, bold=True)
214
+ typer.echo("="*60)
215
+ typer.echo(f"\nChave Pública do SERVIDOR (use no [Peer] dos clientes):")
216
+ typer.secho(f" {server_public}", fg=typer.colors.CYAN, bold=True)
217
+ typer.echo(f"\nChave Pública do CLIENTE '{client_name}' (já no servidor):")
218
+ typer.secho(f" {client_public}", fg=typer.colors.CYAN)
219
+ typer.echo("\n" + "="*60)
220
+ typer.echo(f"Configuração do servidor: {server_conf_path}")
214
221
  typer.echo(f"Cliente inicial salvo em: {client_conf_path}")
215
- typer.echo("Para gerar QR code no terminal: qrencode -t ansiutf8 < caminho-do-cliente.conf")
216
- typer.echo("Para novos clientes, gere chaves com 'wg genkey' e adicione entradas em ambos os arquivos.")
217
- typer.echo("Clientes Linux/macOS: sudo wg-quick up ./cliente.conf | Windows: importe o arquivo no app WireGuard.")
222
+ typer.echo("\n" + "="*60)
223
+ typer.secho("PRÓXIMOS PASSOS:", fg=typer.colors.GREEN, bold=True)
224
+ typer.echo("="*60)
225
+ typer.echo("1. Copie a configuração do cliente para o dispositivo remoto")
226
+ typer.echo(f" scp root@servidor:{client_conf_path} .")
227
+ typer.echo("\n2. No cliente, verifique se o [Peer].PublicKey é a chave pública do SERVIDOR")
228
+ typer.echo(f" PublicKey = {server_public}")
229
+ typer.echo("\n3. Configure o Endpoint com o IP público do servidor:")
230
+ typer.echo(f" Endpoint = {public_endpoint}:{listen_port}")
231
+ typer.echo("\n4. QR code para celular: qrencode -t ansiutf8 < caminho-do-cliente.conf")
232
+ typer.echo("\n5. Windows/Mac: importe o arquivo .conf no app WireGuard")
@@ -494,6 +494,302 @@ def show_client_config(ctx: ExecutionContext) -> None:
494
494
  typer.echo("")
495
495
 
496
496
 
497
+ def verify_config(ctx: ExecutionContext) -> None:
498
+ """Verifica se a configuração do WireGuard está correta."""
499
+ require_root(ctx)
500
+
501
+ typer.secho("\n🔍 Verificação de Configuração WireGuard", fg=typer.colors.CYAN, bold=True)
502
+ typer.echo("="*60)
503
+
504
+ errors = []
505
+ warnings = []
506
+
507
+ # 1. Verificar se arquivo existe
508
+ if not WG0_CONF.exists():
509
+ typer.secho("✗ Arquivo wg0.conf não encontrado!", fg=typer.colors.RED)
510
+ typer.echo(" Execute 'raijin vpn' primeiro para configurar o servidor.")
511
+ return
512
+
513
+ content = WG0_CONF.read_text()
514
+ typer.secho("✓ Arquivo wg0.conf encontrado", fg=typer.colors.GREEN)
515
+
516
+ # 2. Verificar [Interface]
517
+ typer.echo("\n📋 Verificando [Interface]...")
518
+
519
+ if "[Interface]" not in content:
520
+ errors.append("Falta seção [Interface] no arquivo!")
521
+ else:
522
+ typer.secho(" ✓ Seção [Interface] presente", fg=typer.colors.GREEN)
523
+
524
+ # 3. Verificar ListenPort
525
+ port_match = re.search(r'^ListenPort\s*=\s*(\d+)', content, re.MULTILINE)
526
+ if not port_match:
527
+ errors.append("ListenPort não definida!")
528
+ else:
529
+ port = port_match.group(1)
530
+ if port == "22":
531
+ errors.append(f"ListenPort={port} está conflitando com SSH! Use 51820.")
532
+ elif port != "51820":
533
+ warnings.append(f"ListenPort={port} (padrão é 51820)")
534
+ else:
535
+ typer.secho(f" ✓ ListenPort = {port}", fg=typer.colors.GREEN)
536
+
537
+ # 4. Verificar PrivateKey e calcular PublicKey
538
+ typer.echo("\n🔑 Verificando chaves...")
539
+
540
+ priv_match = re.search(r'^PrivateKey\s*=\s*(\S+)', content, re.MULTILINE)
541
+ if not priv_match:
542
+ errors.append("PrivateKey do servidor não encontrada!")
543
+ else:
544
+ private_key = priv_match.group(1)
545
+ try:
546
+ result = subprocess.run(
547
+ ["wg", "pubkey"],
548
+ input=private_key,
549
+ capture_output=True,
550
+ text=True,
551
+ check=True,
552
+ )
553
+ server_public_key = result.stdout.strip()
554
+ typer.secho(f" ✓ Chave Pública do Servidor:", fg=typer.colors.GREEN)
555
+ typer.secho(f" {server_public_key}", fg=typer.colors.CYAN, bold=True)
556
+ typer.echo(" ↑ USE ESTA CHAVE no [Peer].PublicKey dos CLIENTES!")
557
+ except subprocess.CalledProcessError:
558
+ errors.append("Erro ao calcular chave pública do servidor!")
559
+
560
+ # 5. Verificar peers
561
+ typer.echo("\n👥 Verificando peers...")
562
+
563
+ peers = re.findall(r'\[Peer\].*?(?=\[|$)', content, re.DOTALL)
564
+ if not peers:
565
+ warnings.append("Nenhum peer (cliente) configurado!")
566
+ else:
567
+ typer.echo(f" {len(peers)} peer(s) configurado(s)")
568
+
569
+ for i, peer in enumerate(peers, 1):
570
+ pub_match = re.search(r'PublicKey\s*=\s*(\S+)', peer)
571
+ ip_match = re.search(r'AllowedIPs\s*=\s*(\S+)', peer)
572
+
573
+ if pub_match:
574
+ peer_pubkey = pub_match.group(1)
575
+ # Verificar se a chave do peer é a mesma do servidor (erro comum!)
576
+ if 'server_public_key' in dir() and peer_pubkey == server_public_key:
577
+ errors.append(f"Peer {i}: PublicKey é igual à do servidor! Erro de configuração.")
578
+ else:
579
+ typer.secho(f" ✓ Peer {i}: {peer_pubkey[:20]}...", fg=typer.colors.GREEN)
580
+
581
+ if ip_match:
582
+ peer_ip = ip_match.group(1)
583
+ typer.echo(f" AllowedIPs: {peer_ip}")
584
+
585
+ # 6. Verificar PostUp/PostDown
586
+ typer.echo("\n🔧 Verificando iptables rules...")
587
+
588
+ if "MASQUERADE" not in content:
589
+ errors.append("Regra MASQUERADE não encontrada no PostUp!")
590
+ else:
591
+ typer.secho(" ✓ Regra MASQUERADE presente", fg=typer.colors.GREEN)
592
+
593
+ if "FORWARD" not in content:
594
+ errors.append("Regras FORWARD não encontradas no PostUp!")
595
+ else:
596
+ typer.secho(" ✓ Regras FORWARD presentes", fg=typer.colors.GREEN)
597
+
598
+ # 7. Verificar se WireGuard está rodando
599
+ typer.echo("\n🚀 Verificando serviço...")
600
+
601
+ result = subprocess.run(["systemctl", "is-active", "wg-quick@wg0"], capture_output=True, text=True)
602
+ if result.stdout.strip() == "active":
603
+ typer.secho(" ✓ WireGuard está rodando", fg=typer.colors.GREEN)
604
+
605
+ # Verificar porta real sendo usada
606
+ wg_result = subprocess.run(["wg", "show", "wg0", "listen-port"], capture_output=True, text=True)
607
+ actual_port = wg_result.stdout.strip()
608
+ if actual_port and port_match:
609
+ if actual_port != port_match.group(1):
610
+ errors.append(f"CRÍTICO: Porta configurada ({port_match.group(1)}) difere da porta real ({actual_port})!")
611
+ errors.append("Reinicie o WireGuard: sudo systemctl restart wg-quick@wg0")
612
+ else:
613
+ typer.secho(f" ✓ Escutando na porta {actual_port}", fg=typer.colors.GREEN)
614
+ else:
615
+ errors.append("WireGuard não está rodando!")
616
+
617
+ # 8. Resumo
618
+ typer.echo("\n" + "="*60)
619
+
620
+ if errors:
621
+ typer.secho("❌ ERROS ENCONTRADOS:", fg=typer.colors.RED, bold=True)
622
+ for error in errors:
623
+ typer.secho(f" • {error}", fg=typer.colors.RED)
624
+
625
+ if warnings:
626
+ typer.secho("\n⚠️ AVISOS:", fg=typer.colors.YELLOW, bold=True)
627
+ for warning in warnings:
628
+ typer.secho(f" • {warning}", fg=typer.colors.YELLOW)
629
+
630
+ if not errors and not warnings:
631
+ typer.secho("✅ Configuração do servidor OK!", fg=typer.colors.GREEN, bold=True)
632
+
633
+ typer.echo("\n" + "="*60)
634
+ typer.secho("CHECKLIST PARA CLIENTES:", fg=typer.colors.CYAN, bold=True)
635
+ typer.echo("="*60)
636
+ typer.echo("1. [Peer].PublicKey deve ser a chave pública do SERVIDOR (acima)")
637
+ typer.echo("2. [Peer].Endpoint deve ser IP_PÚBLICO:PORTA (ex: 177.128.86.89:51820)")
638
+ typer.echo("3. [Interface].PrivateKey deve ser a chave privada do CLIENTE (não do servidor!)")
639
+ typer.echo("4. [Interface].Address deve ser único para cada cliente (ex: 10.8.0.2/32)")
640
+ typer.echo("")
641
+
642
+
643
+ def diagnose_and_fix(ctx: ExecutionContext) -> None:
644
+ """Diagnostica e corrige problemas de roteamento da VPN."""
645
+ require_root(ctx)
646
+
647
+ typer.secho("\n🔍 Diagnóstico de VPN", fg=typer.colors.CYAN, bold=True)
648
+ typer.echo("="*60)
649
+
650
+ # 1. Verificar se WireGuard está rodando
651
+ typer.echo("\n1. Verificando status do WireGuard...")
652
+ result = run_cmd(["systemctl", "is-active", "wg-quick@wg0"], ctx, check=False)
653
+
654
+ if result.returncode != 0:
655
+ typer.secho(" ✗ WireGuard não está rodando!", fg=typer.colors.RED)
656
+ typer.echo(" Execute: systemctl start wg-quick@wg0")
657
+ return
658
+
659
+ typer.secho(" ✓ WireGuard ativo", fg=typer.colors.GREEN)
660
+
661
+ # 2. Verificar IP forwarding
662
+ typer.echo("\n2. Verificando IP forwarding...")
663
+ try:
664
+ forward = Path("/proc/sys/net/ipv4/ip_forward").read_text().strip()
665
+ if forward == "1":
666
+ typer.secho(" ✓ IP forwarding habilitado", fg=typer.colors.GREEN)
667
+ else:
668
+ typer.secho(" ✗ IP forwarding desabilitado", fg=typer.colors.YELLOW)
669
+ typer.echo(" Habilitando...")
670
+ run_cmd(["sysctl", "-w", "net.ipv4.ip_forward=1"], ctx)
671
+ typer.secho(" ✓ IP forwarding habilitado", fg=typer.colors.GREEN)
672
+ except Exception as e:
673
+ typer.secho(f" ✗ Erro ao verificar: {e}", fg=typer.colors.RED)
674
+
675
+ # 3. Detectar interface de rede principal
676
+ typer.echo("\n3. Detectando interface de rede...")
677
+ result = run_cmd(["ip", "route", "show", "default"], ctx, check=False)
678
+
679
+ if result.returncode != 0:
680
+ typer.secho(" ✗ Não foi possível detectar interface", fg=typer.colors.RED)
681
+ return
682
+
683
+ match = re.search(r'dev\s+(\S+)', result.stdout)
684
+ if not match:
685
+ typer.secho(" ✗ Interface não encontrada", fg=typer.colors.RED)
686
+ return
687
+
688
+ iface = match.group(1)
689
+ typer.secho(f" ✓ Interface detectada: {iface}", fg=typer.colors.GREEN)
690
+
691
+ # 4. Verificar regra MASQUERADE
692
+ typer.echo("\n4. Verificando regra MASQUERADE...")
693
+ result = run_cmd(
694
+ ["iptables", "-t", "nat", "-L", "POSTROUTING", "-v", "-n"],
695
+ ctx,
696
+ check=False
697
+ )
698
+
699
+ has_masquerade = False
700
+ if "10.8.0.0/24" in result.stdout and "MASQUERADE" in result.stdout:
701
+ has_masquerade = True
702
+ typer.secho(" ✓ Regra MASQUERADE existente", fg=typer.colors.GREEN)
703
+ else:
704
+ typer.secho(" ✗ Regra MASQUERADE não encontrada", fg=typer.colors.YELLOW)
705
+ typer.echo(f" Adicionando regra para interface {iface}...")
706
+
707
+ run_cmd([
708
+ "iptables", "-t", "nat", "-A", "POSTROUTING",
709
+ "-s", "10.8.0.0/24", "-o", iface, "-j", "MASQUERADE"
710
+ ], ctx)
711
+
712
+ typer.secho(" ✓ Regra MASQUERADE adicionada", fg=typer.colors.GREEN)
713
+
714
+ # 5. Verificar UFW routed
715
+ typer.echo("\n5. Verificando UFW routed...")
716
+ result = run_cmd(["ufw", "status", "verbose"], ctx, check=False)
717
+
718
+ if "deny (routed)" in result.stdout.lower():
719
+ typer.secho(" ✗ UFW está bloqueando routed", fg=typer.colors.YELLOW)
720
+ typer.echo(" Configurando UFW para permitir routed...")
721
+
722
+ run_cmd(["ufw", "default", "allow", "routed"], ctx, check=False)
723
+ typer.secho(" ✓ UFW configurado", fg=typer.colors.GREEN)
724
+ else:
725
+ typer.secho(" ✓ UFW permite routed", fg=typer.colors.GREEN)
726
+
727
+ # 6. Verificar regras de FORWARD para wg0
728
+ typer.echo("\n6. Verificando regras FORWARD...")
729
+ result = run_cmd(["iptables", "-L", "FORWARD", "-v", "-n"], ctx, check=False)
730
+
731
+ has_forward_in = "wg0" in result.stdout and "ACCEPT" in result.stdout
732
+
733
+ if not has_forward_in:
734
+ typer.secho(" ✗ Regras FORWARD ausentes", fg=typer.colors.YELLOW)
735
+ typer.echo(" Adicionando regras FORWARD...")
736
+
737
+ run_cmd(["iptables", "-A", "FORWARD", "-i", "wg0", "-j", "ACCEPT"], ctx, check=False)
738
+ run_cmd(["iptables", "-A", "FORWARD", "-o", "wg0", "-j", "ACCEPT"], ctx, check=False)
739
+
740
+ typer.secho(" ✓ Regras FORWARD adicionadas", fg=typer.colors.GREEN)
741
+ else:
742
+ typer.secho(" ✓ Regras FORWARD existentes", fg=typer.colors.GREEN)
743
+
744
+ # 7. Verificar UFW permite wg0
745
+ typer.echo("\n7. Verificando UFW para wg0...")
746
+ result = run_cmd(["ufw", "status"], ctx, check=False)
747
+
748
+ if "wg0" not in result.stdout:
749
+ typer.secho(" ✗ UFW não permite wg0", fg=typer.colors.YELLOW)
750
+ typer.echo(" Adicionando regra UFW...")
751
+
752
+ run_cmd(["ufw", "allow", "in", "on", "wg0"], ctx, check=False)
753
+ typer.secho(" ✓ UFW configurado para wg0", fg=typer.colors.GREEN)
754
+ else:
755
+ typer.secho(" ✓ UFW permite wg0", fg=typer.colors.GREEN)
756
+
757
+ # 8. Reiniciar WireGuard para aplicar mudanças
758
+ typer.echo("\n8. Reiniciando WireGuard...")
759
+ run_cmd(["systemctl", "restart", "wg-quick@wg0"], ctx)
760
+ typer.secho(" ✓ WireGuard reiniciado", fg=typer.colors.GREEN)
761
+
762
+ # 9. Verificar peers conectados
763
+ typer.echo("\n9. Verificando peers conectados...")
764
+ result = run_cmd(["wg", "show"], ctx, check=False)
765
+
766
+ peer_count = result.stdout.count("peer:")
767
+ typer.echo(f" Peers configurados: {peer_count}")
768
+
769
+ if "latest handshake" in result.stdout.lower():
770
+ typer.secho(" ✓ Handshake detectado (cliente conectado)", fg=typer.colors.GREEN)
771
+ else:
772
+ typer.secho(" ⚠ Nenhum handshake recente", fg=typer.colors.YELLOW)
773
+ typer.echo(" Peça ao cliente para reconectar no WireGuard")
774
+
775
+ # 10. Teste básico de conectividade
776
+ typer.echo("\n10. Testando conectividade VPN...")
777
+ result = run_cmd(["ping", "-c", "2", "-W", "1", "10.8.0.1"], ctx, check=False)
778
+
779
+ if result.returncode == 0:
780
+ typer.secho(" ✓ Ping para 10.8.0.1 bem-sucedido", fg=typer.colors.GREEN)
781
+ else:
782
+ typer.secho(" ⚠ Ping falhou (normal se nenhum cliente conectado)", fg=typer.colors.YELLOW)
783
+
784
+ typer.echo("\n" + "="*60)
785
+ typer.secho("✓ Diagnóstico concluído!", fg=typer.colors.GREEN, bold=True)
786
+ typer.echo("\nPróximos passos:")
787
+ typer.echo(" 1. No cliente Windows, desconecte e reconecte o túnel WireGuard")
788
+ typer.echo(" 2. Teste: ping 10.8.0.1")
789
+ typer.echo(" 3. Verifique 'sudo wg show' para ver handshake")
790
+ typer.echo("")
791
+
792
+
497
793
  def run(ctx: ExecutionContext) -> None:
498
794
  """Menu interativo para gerenciar clientes VPN."""
499
795
  require_root(ctx)
@@ -506,9 +802,11 @@ def run(ctx: ExecutionContext) -> None:
506
802
  typer.echo("2. Listar clientes")
507
803
  typer.echo("3. Remover cliente")
508
804
  typer.echo("4. Mostrar configuração de cliente")
509
- typer.echo("5. Sair")
805
+ typer.echo("5. Verificar configuração do servidor")
806
+ typer.echo("6. Diagnosticar e corrigir roteamento")
807
+ typer.echo("7. Sair")
510
808
 
511
- choice = typer.prompt("\nEscolha uma opção", default="5")
809
+ choice = typer.prompt("\nEscolha uma opção", default="7")
512
810
 
513
811
  try:
514
812
  if choice == "1":
@@ -520,6 +818,10 @@ def run(ctx: ExecutionContext) -> None:
520
818
  elif choice == "4":
521
819
  show_client_config(ctx)
522
820
  elif choice == "5":
821
+ verify_config(ctx)
822
+ elif choice == "6":
823
+ diagnose_and_fix(ctx)
824
+ elif choice == "7":
523
825
  typer.echo("Saindo...")
524
826
  break
525
827
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.3.2
3
+ Version: 0.3.3
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
File without changes
File without changes