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

raijin_server/__init__.py CHANGED
@@ -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__"]
@@ -393,9 +393,18 @@ def run(ctx: ExecutionContext) -> None:
393
393
 
394
394
  admin_password = typer.prompt("Senha admin do Grafana", default="admin")
395
395
 
396
+ # NodePort para acesso via VPN (recomendado)
397
+ enable_nodeport = typer.confirm(
398
+ "Habilitar NodePort para acesso via VPN?",
399
+ default=True
400
+ )
401
+ nodeport_port = 30030
402
+ if enable_nodeport:
403
+ nodeport_port = int(typer.prompt("Porta NodePort", default="30030"))
404
+
396
405
  # Ingress público não é recomendado para ferramentas de observabilidade
397
406
  enable_ingress = typer.confirm(
398
- "Habilitar ingress público? (NÃO recomendado - use VPN + port-forward)",
407
+ "Habilitar ingress público? (NÃO recomendado - use VPN + NodePort)",
399
408
  default=False
400
409
  )
401
410
 
@@ -439,9 +448,16 @@ def run(ctx: ExecutionContext) -> None:
439
448
  persistence_yaml += f"""
440
449
  storageClassName: {storage_class}"""
441
450
 
451
+ service_type = "NodePort" if enable_nodeport else "ClusterIP"
442
452
  values_yaml = f"""adminPassword: {admin_password}
443
453
  service:
444
- type: ClusterIP
454
+ type: {service_type}"""
455
+
456
+ if enable_nodeport:
457
+ values_yaml += f"""
458
+ nodePort: {nodeport_port}"""
459
+
460
+ values_yaml += f"""
445
461
  ingress:
446
462
  enabled: {str(enable_ingress).lower()}"""
447
463
 
@@ -540,15 +556,18 @@ dashboards:
540
556
 
541
557
  if enable_ingress:
542
558
  typer.echo(f"\nAcesse: https://{ingress_host}")
543
- else:
544
- typer.secho("\n🔒 Acesso Seguro via VPN + Port-Forward:", fg=typer.colors.CYAN, bold=True)
559
+ elif enable_nodeport:
560
+ typer.secho("\n🔒 Acesso via VPN + NodePort:", fg=typer.colors.CYAN, bold=True)
545
561
  typer.echo("\n1. Configure VPN (se ainda não tiver):")
546
562
  typer.echo(" sudo raijin vpn")
547
563
  typer.echo("\n2. Conecte via WireGuard no seu Windows/Mac")
548
- typer.echo("\n3. Faça port-forward local:")
549
- typer.echo(" kubectl -n observability port-forward svc/grafana 3000:80")
550
- typer.echo("\n4. Acesse no navegador:")
551
- typer.echo(" http://localhost:3000")
564
+ typer.echo("\n3. Acesse no navegador (IP da VPN):")
565
+ typer.echo(f" http://<VPN_SERVER_IP>:{nodeport_port}")
566
+ typer.echo("\n Exemplo: http://10.8.0.1:{}".format(nodeport_port))
567
+ else:
568
+ typer.secho("\n🔒 Acesso via Port-Forward:", fg=typer.colors.CYAN, bold=True)
569
+ typer.echo("\n kubectl -n observability port-forward svc/grafana 3000:80")
570
+ typer.echo("\n Acesse: http://localhost:3000")
552
571
 
553
572
  typer.echo("\nUsuario: admin")
554
573
  typer.echo(f"Senha: {admin_password}")
@@ -103,6 +103,15 @@ def run(ctx: ExecutionContext) -> None:
103
103
 
104
104
  retention_hours = typer.prompt("Retencao de logs em horas", default="168")
105
105
  persistence_size = typer.prompt("Tamanho do storage", default="20Gi")
106
+
107
+ # NodePort para acesso via VPN
108
+ enable_nodeport = typer.confirm(
109
+ "Habilitar NodePort para acesso via VPN?",
110
+ default=True
111
+ )
112
+ nodeport_port = 30310
113
+ if enable_nodeport:
114
+ nodeport_port = int(typer.prompt("Porta NodePort", default="30310"))
106
115
 
107
116
  node_name = _detect_node_name(ctx)
108
117
 
@@ -147,6 +156,15 @@ promtail:
147
156
  memory: 256Mi
148
157
  """
149
158
 
159
+ # Adiciona NodePort se habilitado
160
+ if enable_nodeport:
161
+ values_yaml += f"""
162
+ loki:
163
+ service:
164
+ type: NodePort
165
+ nodePort: {nodeport_port}
166
+ """
167
+
150
168
  values_path = Path("/tmp/raijin-loki-values.yaml")
151
169
  write_file(values_path, values_yaml, ctx)
152
170
 
@@ -167,7 +185,13 @@ promtail:
167
185
  _wait_for_loki_ready(ctx)
168
186
 
169
187
  typer.secho("\n✓ Loki Stack instalado com sucesso.", fg=typer.colors.GREEN, bold=True)
170
- typer.echo("\nPara acessar Loki via port-forward:")
171
- typer.echo(" kubectl -n observability port-forward svc/loki 3100:3100")
172
- typer.echo("\nPara verificar logs:")
173
- typer.echo(" curl http://localhost:3100/ready")
188
+
189
+ if enable_nodeport:
190
+ typer.secho("\n🔒 Acesso via VPN + NodePort:", fg=typer.colors.CYAN, bold=True)
191
+ typer.echo(f"\n curl http://<VPN_SERVER_IP>:{nodeport_port}/ready")
192
+ typer.echo(f"\n Exemplo: curl http://10.8.0.1:{nodeport_port}/ready")
193
+ else:
194
+ typer.echo("\nPara acessar Loki via port-forward:")
195
+ typer.echo(" kubectl -n observability port-forward svc/loki 3100:3100")
196
+ typer.echo("\nPara verificar logs:")
197
+ typer.echo(" curl http://localhost:3100/ready")
@@ -449,6 +449,18 @@ def run(ctx: ExecutionContext) -> None:
449
449
 
450
450
  enable_console = typer.confirm("Habilitar Console Web?", default=True)
451
451
 
452
+ # NodePort para acesso via VPN
453
+ enable_nodeport = typer.confirm(
454
+ "Habilitar NodePort para acesso via VPN?",
455
+ default=True
456
+ )
457
+ api_nodeport = 30900
458
+ console_nodeport = 30901
459
+ if enable_nodeport:
460
+ api_nodeport = int(typer.prompt("Porta NodePort para API S3", default="30900"))
461
+ if enable_console:
462
+ console_nodeport = int(typer.prompt("Porta NodePort para Console", default="30901"))
463
+
452
464
  node_name = _detect_node_name(ctx)
453
465
 
454
466
  values = [
@@ -486,13 +498,27 @@ def run(ctx: ExecutionContext) -> None:
486
498
  if is_distributed:
487
499
  values.append(f"replicas={replicas}")
488
500
 
489
- # Console
490
- if enable_console:
501
+ # Service type (NodePort ou ClusterIP)
502
+ if enable_nodeport:
491
503
  values.extend([
492
- "consoleService.type=ClusterIP",
493
- "consoleIngress.enabled=false",
504
+ "service.type=NodePort",
505
+ f"service.nodePort={api_nodeport}",
494
506
  ])
495
507
 
508
+ # Console
509
+ if enable_console:
510
+ if enable_nodeport:
511
+ values.extend([
512
+ "consoleService.type=NodePort",
513
+ f"consoleService.nodePort={console_nodeport}",
514
+ "consoleIngress.enabled=false",
515
+ ])
516
+ else:
517
+ values.extend([
518
+ "consoleService.type=ClusterIP",
519
+ "consoleIngress.enabled=false",
520
+ ])
521
+
496
522
  helm_upgrade_install(
497
523
  release="minio",
498
524
  chart="minio",
@@ -514,14 +540,21 @@ def run(ctx: ExecutionContext) -> None:
514
540
  typer.echo(f" Root Password: {root_password}")
515
541
 
516
542
  if enable_console:
517
- typer.secho("\n🔒 Acesso Seguro ao MinIO Console via VPN:", fg=typer.colors.CYAN, bold=True)
518
- typer.echo("\n1. Configure VPN (se ainda não tiver):")
519
- typer.echo(" sudo raijin vpn")
520
- typer.echo("\n2. Conecte via WireGuard")
521
- typer.echo("\n3. Faça port-forward:")
522
- typer.echo(" kubectl -n minio port-forward svc/minio-console 9001:9001")
523
- typer.echo("\n4. Acesse no navegador:")
524
- typer.echo(" http://localhost:9001")
525
-
526
- typer.echo("\nPara acessar a API S3 (port-forward):")
527
- typer.echo(" kubectl -n minio port-forward svc/minio 9000:9000")
543
+ if enable_nodeport:
544
+ typer.secho("\n🔒 Acesso ao MinIO Console via VPN:", fg=typer.colors.CYAN, bold=True)
545
+ typer.echo("\n1. Configure VPN (se ainda não tiver):")
546
+ typer.echo(" sudo raijin vpn")
547
+ typer.echo("\n2. Conecte via WireGuard")
548
+ typer.echo("\n3. Acesse no navegador (IP da VPN):")
549
+ typer.echo(f" http://<VPN_SERVER_IP>:{console_nodeport}")
550
+ typer.echo("\n Exemplo: http://10.8.0.1:{}".format(console_nodeport))
551
+ else:
552
+ typer.secho("\n🔒 Acesso via Port-Forward:", fg=typer.colors.CYAN, bold=True)
553
+ typer.echo("\n kubectl -n minio port-forward svc/minio-console 9001:9001")
554
+ typer.echo("\n Acesse: http://localhost:9001")
555
+
556
+ if enable_nodeport:
557
+ typer.echo(f"\nAPI S3 via VPN: http://<VPN_SERVER_IP>:{api_nodeport}")
558
+ else:
559
+ typer.echo("\nPara acessar a API S3 (port-forward):")
560
+ typer.echo(" kubectl -n minio port-forward svc/minio 9000:9000")
@@ -428,6 +428,17 @@ def run(ctx: ExecutionContext) -> None:
428
428
  enable_persistence = typer.confirm(
429
429
  "Habilitar PVC para Prometheus e Alertmanager?", default=bool(default_sc)
430
430
  )
431
+
432
+ # NodePort para acesso via VPN
433
+ enable_nodeport = typer.confirm(
434
+ "Habilitar NodePort para acesso via VPN?",
435
+ default=True
436
+ )
437
+ prometheus_nodeport = 30090
438
+ alertmanager_nodeport = 30093
439
+ if enable_nodeport:
440
+ prometheus_nodeport = int(typer.prompt("Porta NodePort para Prometheus", default="30090"))
441
+ alertmanager_nodeport = int(typer.prompt("Porta NodePort para Alertmanager", default="30093"))
431
442
 
432
443
  # Se habilitou PVC, garante que existe StorageClass disponivel
433
444
  if enable_persistence:
@@ -442,31 +453,42 @@ def run(ctx: ExecutionContext) -> None:
442
453
  "prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false",
443
454
  "prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false",
444
455
  "defaultRules.create=true",
445
- # Tolerations for control-plane nodes
456
+ # Tolerations for control-plane nodes - Prometheus
446
457
  "prometheus.prometheusSpec.tolerations[0].key=node-role.kubernetes.io/control-plane",
447
458
  "prometheus.prometheusSpec.tolerations[0].operator=Exists",
448
459
  "prometheus.prometheusSpec.tolerations[0].effect=NoSchedule",
449
460
  "prometheus.prometheusSpec.tolerations[1].key=node-role.kubernetes.io/master",
450
461
  "prometheus.prometheusSpec.tolerations[1].operator=Exists",
451
462
  "prometheus.prometheusSpec.tolerations[1].effect=NoSchedule",
463
+ # Tolerations - Alertmanager
452
464
  "alertmanager.alertmanagerSpec.tolerations[0].key=node-role.kubernetes.io/control-plane",
453
465
  "alertmanager.alertmanagerSpec.tolerations[0].operator=Exists",
454
466
  "alertmanager.alertmanagerSpec.tolerations[0].effect=NoSchedule",
455
467
  "alertmanager.alertmanagerSpec.tolerations[1].key=node-role.kubernetes.io/master",
456
468
  "alertmanager.alertmanagerSpec.tolerations[1].operator=Exists",
457
469
  "alertmanager.alertmanagerSpec.tolerations[1].effect=NoSchedule",
470
+ # Tolerations - Prometheus Operator
458
471
  "prometheusOperator.tolerations[0].key=node-role.kubernetes.io/control-plane",
459
472
  "prometheusOperator.tolerations[0].operator=Exists",
460
473
  "prometheusOperator.tolerations[0].effect=NoSchedule",
461
474
  "prometheusOperator.tolerations[1].key=node-role.kubernetes.io/master",
462
475
  "prometheusOperator.tolerations[1].operator=Exists",
463
476
  "prometheusOperator.tolerations[1].effect=NoSchedule",
477
+ # Tolerations - Admission Webhooks (Jobs que criam/atualizam webhooks)
478
+ "prometheusOperator.admissionWebhooks.patch.tolerations[0].key=node-role.kubernetes.io/control-plane",
479
+ "prometheusOperator.admissionWebhooks.patch.tolerations[0].operator=Exists",
480
+ "prometheusOperator.admissionWebhooks.patch.tolerations[0].effect=NoSchedule",
481
+ "prometheusOperator.admissionWebhooks.patch.tolerations[1].key=node-role.kubernetes.io/master",
482
+ "prometheusOperator.admissionWebhooks.patch.tolerations[1].operator=Exists",
483
+ "prometheusOperator.admissionWebhooks.patch.tolerations[1].effect=NoSchedule",
484
+ # Tolerations - kube-state-metrics
464
485
  "kube-state-metrics.tolerations[0].key=node-role.kubernetes.io/control-plane",
465
486
  "kube-state-metrics.tolerations[0].operator=Exists",
466
487
  "kube-state-metrics.tolerations[0].effect=NoSchedule",
467
488
  "kube-state-metrics.tolerations[1].key=node-role.kubernetes.io/master",
468
489
  "kube-state-metrics.tolerations[1].operator=Exists",
469
490
  "kube-state-metrics.tolerations[1].effect=NoSchedule",
491
+ # Tolerations - node-exporter
470
492
  "prometheus-node-exporter.tolerations[0].key=node-role.kubernetes.io/control-plane",
471
493
  "prometheus-node-exporter.tolerations[0].operator=Exists",
472
494
  "prometheus-node-exporter.tolerations[0].effect=NoSchedule",
@@ -477,8 +499,15 @@ def run(ctx: ExecutionContext) -> None:
477
499
  f"prometheus.prometheusSpec.nodeSelector.kubernetes\\.io/hostname={node_name}",
478
500
  f"alertmanager.alertmanagerSpec.nodeSelector.kubernetes\\.io/hostname={node_name}",
479
501
  f"prometheusOperator.nodeSelector.kubernetes\\.io/hostname={node_name}",
480
- ]
481
-
502
+ ]
503
+ # NodePort para acesso via VPN
504
+ if enable_nodeport:
505
+ values.extend([
506
+ "prometheus.service.type=NodePort",
507
+ f"prometheus.service.nodePort={prometheus_nodeport}",
508
+ "alertmanager.service.type=NodePort",
509
+ f"alertmanager.service.nodePort={alertmanager_nodeport}",
510
+ ])
482
511
  extra_args = ["--wait", "--timeout", "10m", "--atomic"]
483
512
 
484
513
  chart_version = typer.prompt(
@@ -531,7 +560,18 @@ def run(ctx: ExecutionContext) -> None:
531
560
  _wait_for_prometheus_ready(ctx, namespace)
532
561
 
533
562
  typer.secho("\n✓ kube-prometheus-stack instalado com sucesso.", fg=typer.colors.GREEN, bold=True)
534
- typer.echo("\nPara acessar Prometheus via port-forward:")
535
- typer.echo(f" kubectl -n {namespace} port-forward svc/kube-prometheus-stack-prometheus 9090:9090")
536
- typer.echo("\nPara acessar Alertmanager via port-forward:")
537
- typer.echo(f" kubectl -n {namespace} port-forward svc/kube-prometheus-stack-alertmanager 9093:9093")
563
+
564
+ if enable_nodeport:
565
+ typer.secho("\n🔒 Acesso via VPN + NodePort:", fg=typer.colors.CYAN, bold=True)
566
+ typer.echo("\n1. Configure VPN: sudo raijin vpn")
567
+ typer.echo("2. Conecte via WireGuard")
568
+ typer.echo("\nPrometheus:")
569
+ typer.echo(f" http://<VPN_SERVER_IP>:{prometheus_nodeport}")
570
+ typer.echo("\nAlertmanager:")
571
+ typer.echo(f" http://<VPN_SERVER_IP>:{alertmanager_nodeport}")
572
+ typer.echo("\nExemplo: http://10.8.0.1:{} (Prometheus)".format(prometheus_nodeport))
573
+ else:
574
+ typer.echo("\nPara acessar Prometheus via port-forward:")
575
+ typer.echo(f" kubectl -n {namespace} port-forward svc/kube-prometheus-stack-prometheus 9090:9090")
576
+ typer.echo("\nPara acessar Alertmanager via port-forward:")
577
+ typer.echo(f" kubectl -n {namespace} port-forward svc/kube-prometheus-stack-alertmanager 9093:9093")
@@ -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.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
@@ -1,4 +1,4 @@
1
- raijin_server/__init__.py,sha256=A06gLxft508OGSQ5sPqJhuqoAoEB0hkD0koIF-UP0SQ,94
1
+ raijin_server/__init__.py,sha256=PxBToT7REJri8rHFGLoeIfEApLUM9TrOogmxCLdnomI,94
2
2
  raijin_server/cli.py,sha256=rqkAQCU5imi52YJCIeEuZqWo8bWYkVErOQh3JpKIDok,38149
3
3
  raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
4
4
  raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
@@ -13,7 +13,7 @@ raijin_server/modules/cert_manager.py,sha256=XkFlXJjiP4_9It_PJaFcVYMS-QKTzzFAt83
13
13
  raijin_server/modules/essentials.py,sha256=2xUXCyCQtFGd2DnCKV81N1R6bEJqH8zaet8mLovtQ1I,689
14
14
  raijin_server/modules/firewall.py,sha256=h6AISqiZeTinVT7BjmQIS872qRAFZJLg7meqlth3cfw,757
15
15
  raijin_server/modules/full_install.py,sha256=xiKe2GLuZ97c4YdTmhP-kwDVuJJ9Xq3dlgcLlqSPeYM,15518
16
- raijin_server/modules/grafana.py,sha256=r4U6FJZ9OeTk4d3LDJT0NbN8wumB3REMtd3E3PRL_oE,17383
16
+ raijin_server/modules/grafana.py,sha256=8YbKG-UL19lwTkH1-ZxpUetOZd-CLI4_kPsuZblNaWI,18080
17
17
  raijin_server/modules/hardening.py,sha256=4hz3ifkMhPlXa2n7gPxN0gitQgzALZ-073vuU3LM4RI,1616
18
18
  raijin_server/modules/harness.py,sha256=uWTxTVJlY_VB6xi4ftMtTSaIb96HA8WJQS-RbyxU45M,5391
19
19
  raijin_server/modules/internal_dns.py,sha256=Jynngq0TEEUo3jkAR4m8F1ihF10rkQuKHVP-gZYyDFY,15191
@@ -21,28 +21,28 @@ raijin_server/modules/istio.py,sha256=o0K5-Fw4LRs-kbAVgwzYxHzEt_aPFJG8suqOqvg274
21
21
  raijin_server/modules/kafka.py,sha256=n7ZpLPWv6sKBJhdBiPe7VgeDB24YiCIOWvOQkWwt03Y,5664
22
22
  raijin_server/modules/kong.py,sha256=_w1VIkND6zZuUwIl_CNDxbwWdzaEdXZEO_Iqc1ngPwQ,13654
23
23
  raijin_server/modules/kubernetes.py,sha256=9E6zV0zGQWZW92NVpxwYctpi-4JDmi6YzF3tKRI4HlU,13343
24
- raijin_server/modules/loki.py,sha256=aNiUpnOFppZMXoQwYhn7IoPMzwUz4aHi6pbiqj1PRjc,5022
24
+ raijin_server/modules/loki.py,sha256=fRoXNghwffW6afE_a3sKMhjPJ9DIWhGMTTPM-ltNr2E,5766
25
25
  raijin_server/modules/metallb.py,sha256=uUuklc_RsQ-W2qDVRMQAxQm9HKGEqso444b1IwBpM6w,8554
26
- raijin_server/modules/minio.py,sha256=QbladHGefZBZ8l3f9D7t45nwfwVcuAgHi78E4Ygi300,17614
26
+ raijin_server/modules/minio.py,sha256=ZoxugJvvuGLzViDfEzrVCRZUevoiFwcEy0PNyn0My4w,18918
27
27
  raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
28
28
  raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
29
29
  raijin_server/modules/observability_ingress.py,sha256=S4MtJKahiZ1qSx0P71P3IhKvq4RY-g01Z4IogW3c1hs,7045
30
- raijin_server/modules/prometheus.py,sha256=y5sy_mH1YeQWTOt5CNqqj5JD92-GMxbWQaaZKwx505U,19728
30
+ raijin_server/modules/prometheus.py,sha256=lyhaqLIfMl0GtQ2b2Hre7_A47HrHBB5gspmnWtwXZ4Y,21880
31
31
  raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
32
32
  raijin_server/modules/secrets.py,sha256=d4j12feQL8m_4-hYN5FfboQHvBc75TFeGno3OzrXokE,9266
33
33
  raijin_server/modules/ssh_hardening.py,sha256=Zd0dlylUBr01SkrI1CS05-0DB9xIto5rWH1bUVs80ow,5422
34
34
  raijin_server/modules/traefik.py,sha256=omziywss4o-8t64Kj-upLqbXdFYm2JwqOoOukDUmqxY,5008
35
35
  raijin_server/modules/velero.py,sha256=yDtqd6yUu0L5wzLCjYXqvvxB_RyaAoZtntb6HoHVAOo,5642
36
- raijin_server/modules/vpn.py,sha256=hF-0vA17VKTxhQLDBSEeqI5aPQpiaaj4IpUf9l6lr64,8297
37
- raijin_server/modules/vpn_client.py,sha256=6P5mYLTgQr5fqjTftY6jW3p_iRXy5YVNxfkLfP0mujs,17228
36
+ raijin_server/modules/vpn.py,sha256=qWyROiHx2-FzMqhfpmzslrdfRBTewd53ylUq90wR7SQ,9149
37
+ raijin_server/modules/vpn_client.py,sha256=SjWzSQKLSJCpjz7Y1i1dFaNfPOVIQXFwgD9uM3GYaIY,30035
38
38
  raijin_server/scripts/__init__.py,sha256=deduGfHf8BMVWred4ux5LfBDT2NJ5XYeJAt2sDEU4qs,53
39
39
  raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndyCxWh0,1842
40
40
  raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
41
41
  raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
42
42
  raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
43
- raijin_server-0.3.2.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
44
- raijin_server-0.3.2.dist-info/METADATA,sha256=0YsPOlPDXXMTDqNNWcYfcfiwn-C8mw0spAcAGo-XuTU,8829
45
- raijin_server-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
- raijin_server-0.3.2.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
47
- raijin_server-0.3.2.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
48
- raijin_server-0.3.2.dist-info/RECORD,,
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,,