raijin-server 0.2.7__py3-none-any.whl → 0.2.10__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.
raijin_server/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """Pacote principal do CLI Raijin Server."""
2
2
 
3
- __version__ = "0.2.7"
3
+ __version__ = "0.2.10"
4
4
 
5
5
  __all__ = ["__version__"]
raijin_server/cli.py CHANGED
@@ -45,7 +45,7 @@ from raijin_server.modules import (
45
45
  vpn,
46
46
  )
47
47
  from raijin_server.utils import ExecutionContext, logger, active_log_file, available_log_files, page_text, ensure_tool
48
- from raijin_server.validators import validate_system_requirements, check_module_dependencies
48
+ from raijin_server.validators import validate_system_requirements, check_module_dependencies, MODULE_DEPENDENCIES
49
49
  from raijin_server.healthchecks import run_health_check
50
50
  from raijin_server.config import ConfigManager
51
51
 
@@ -85,9 +85,9 @@ MODULES: Dict[str, Callable[[ExecutionContext], None]] = {
85
85
  "vpn": vpn.run,
86
86
  "kubernetes": kubernetes.run,
87
87
  "calico": calico.run,
88
+ "traefik": traefik.run, # mover antes do cert_manager para refletir dependencia
88
89
  "cert_manager": cert_manager.run,
89
90
  "istio": istio.run,
90
- "traefik": traefik.run,
91
91
  "kong": kong.run,
92
92
  "minio": minio.run,
93
93
  "prometheus": prometheus.run,
@@ -103,6 +103,11 @@ MODULES: Dict[str, Callable[[ExecutionContext], None]] = {
103
103
  "full_install": full_install.run,
104
104
  }
105
105
 
106
+ # Rollbacks sao opcionais; por padrao apenas removem marcador de conclusao e avisam
107
+ ROLLBACK_HANDLERS: Dict[str, Callable[[ExecutionContext], None]] = {
108
+ # Exemplos para futuras customizacoes: "traefik": traefik.rollback
109
+ }
110
+
106
111
  MODULE_DESCRIPTIONS: Dict[str, str] = {
107
112
  "sanitize": "Remove instalacoes antigas de Kubernetes e prepara ambiente",
108
113
  "bootstrap": "Instala ferramentas: helm, kubectl, istioctl, velero, containerd",
@@ -253,6 +258,79 @@ def _is_completed(name: str) -> bool:
253
258
  return _state_file(name).exists()
254
259
 
255
260
 
261
+ def _clear_completed(name: str) -> None:
262
+ try:
263
+ path = _state_file(name)
264
+ if path.exists():
265
+ path.unlink()
266
+ typer.secho(f"Estado removido: {name}", fg=typer.colors.YELLOW)
267
+ except Exception as exc:
268
+ console.print(f"[yellow]Nao foi possivel limpar estado de {name}: {exc}[/yellow]")
269
+
270
+
271
+ def _dependents_of(module: str) -> list[str]:
272
+ dependents = []
273
+ for mod, deps in MODULE_DEPENDENCIES.items():
274
+ if module in deps:
275
+ dependents.append(mod)
276
+ return dependents
277
+
278
+
279
+ def _default_rollback(exec_ctx: ExecutionContext, name: str) -> None:
280
+ """Rollback padrao: nao aplica alteracoes, apenas sinaliza ausencia de implementacao."""
281
+ if exec_ctx.dry_run:
282
+ typer.secho(f"[dry-run] Rollback para '{name}' nao automatizado (necessario reverter manualmente).", fg=typer.colors.YELLOW)
283
+ else:
284
+ typer.secho(
285
+ f"Rollback para '{name}' ainda nao foi automatizado. Reverta recursos manualmente e reexecute se necessario.",
286
+ fg=typer.colors.YELLOW,
287
+ )
288
+
289
+
290
+ def _get_rollback_handler(name: str) -> Callable[[ExecutionContext], None]:
291
+ return ROLLBACK_HANDLERS.get(name, lambda ctx: _default_rollback(ctx, name))
292
+
293
+
294
+ def _rollback_module(
295
+ ctx: typer.Context,
296
+ name: str,
297
+ *,
298
+ cascade_prompt: bool = True,
299
+ visited: set[str] | None = None,
300
+ ) -> None:
301
+ handler = _get_rollback_handler(name)
302
+ exec_ctx = ctx.obj or ExecutionContext()
303
+
304
+ visited = visited or set()
305
+ if name in visited:
306
+ return
307
+ visited.add(name)
308
+
309
+ dependents = _dependents_of(name)
310
+ completed_dependents = [dep for dep in dependents if _is_completed(dep)]
311
+
312
+ if completed_dependents and cascade_prompt:
313
+ typer.secho(
314
+ "Dependencias detectadas apos este modulo: " + ", ".join(completed_dependents),
315
+ fg=typer.colors.YELLOW,
316
+ )
317
+ typer.secho(
318
+ "Rollback em cascata vai tentar reverter esses modulos primeiro para evitar estado inconsistente.",
319
+ fg=typer.colors.YELLOW,
320
+ )
321
+ if not typer.confirm("Prosseguir com rollback em cascata?", default=False):
322
+ typer.secho("Rollback cancelado.", fg=typer.colors.RED)
323
+ return
324
+
325
+ for dep in completed_dependents:
326
+ _rollback_module(ctx, dep, cascade_prompt=False, visited=visited)
327
+
328
+ typer.secho(f"\n[ROLLBACK] {name}", fg=typer.colors.CYAN, bold=True)
329
+ handler(exec_ctx)
330
+ _clear_completed(name)
331
+ typer.secho(f"Rollback finalizado (best-effort) para {name}\n", fg=typer.colors.GREEN)
332
+
333
+
256
334
  def _render_menu(dry_run: bool) -> int:
257
335
  table = Table(
258
336
  title="Selecione um modulo para executar",
@@ -326,9 +404,21 @@ def interactive_menu(ctx: typer.Context) -> None:
326
404
  console.print("[red]Opcao invalida[/red]")
327
405
  continue
328
406
 
407
+ action = Prompt.ask(
408
+ "Acao (e=executar, r=rollback, c=cancelar)",
409
+ choices=["e", "r", "c"],
410
+ default="e",
411
+ )
412
+
329
413
  exec_ctx = ExecutionContext(dry_run=current_dry_run)
330
414
  ctx.obj = exec_ctx
331
- _run_module(ctx, name)
415
+
416
+ if action == "e":
417
+ _run_module(ctx, name)
418
+ elif action == "r":
419
+ _rollback_module(ctx, name)
420
+ else:
421
+ console.print("[yellow]Acao cancelada[/yellow]")
332
422
  # Loop continua e menu eh re-renderizado, refletindo status atualizado quando nao eh dry-run.
333
423
 
334
424
 
@@ -375,6 +465,21 @@ def menu(ctx: typer.Context) -> None:
375
465
  interactive_menu(ctx)
376
466
 
377
467
 
468
+ @app.command()
469
+ def rollback(
470
+ ctx: typer.Context,
471
+ module: str = typer.Argument(..., help="Modulo a reverter"),
472
+ cascade: bool = typer.Option(
473
+ True,
474
+ "--cascade/--no-cascade",
475
+ help="Quando habilitado, pergunta e aplica rollback em dependentes concluidos primeiro",
476
+ ),
477
+ ) -> None:
478
+ """Executa rollback best-effort de um modulo (com aviso sobre dependencias)."""
479
+
480
+ _rollback_module(ctx, module, cascade_prompt=cascade)
481
+
482
+
378
483
  @app.command()
379
484
  def hardening(ctx: typer.Context) -> None:
380
485
  _run_module(ctx, "hardening")
raijin_server/config.py CHANGED
@@ -78,15 +78,15 @@ class ConfigManager:
78
78
  "modules": {
79
79
  "network": {
80
80
  "interface": "ens18",
81
- "address": "192.168.0.10/24",
82
- "gateway": "192.168.0.1",
83
- "dns": "1.1.1.1,8.8.8.8",
81
+ "address": "192.168.1.81/24",
82
+ "gateway": "192.168.1.254",
83
+ "dns": "177.128.80.44,177.128.80.45",
84
84
  },
85
85
  "kubernetes": {
86
86
  "pod_cidr": "10.244.0.0/16",
87
87
  "service_cidr": "10.96.0.0/12",
88
88
  "cluster_name": "raijin",
89
- "advertise_address": "0.0.0.0",
89
+ "advertise_address": "192.168.1.81",
90
90
  },
91
91
  "calico": {
92
92
  "pod_cidr": "10.244.0.0/16",
@@ -124,6 +124,21 @@ def check_k8s_pods_in_namespace(namespace: str, ctx: ExecutionContext, timeout:
124
124
  )
125
125
 
126
126
 
127
+ def check_swap_disabled(ctx: ExecutionContext) -> tuple[bool, str]:
128
+ """Confirma que nao ha swap ativa (requisito kubeadm/kubelet)."""
129
+ if ctx.dry_run:
130
+ return True, "dry-run"
131
+ try:
132
+ with open("/proc/swaps") as f:
133
+ lines = f.read().strip().splitlines()
134
+ # /proc/swaps tem header + linhas; se so header, swap esta off
135
+ if len(lines) <= 1:
136
+ return True, "swap desativada"
137
+ return False, "swap ativa (remova entradas do fstab e execute swapoff -a)"
138
+ except Exception as exc:
139
+ return False, f"falha ao verificar swap: {exc}"
140
+
141
+
127
142
  def check_helm_release(release: str, namespace: str, ctx: ExecutionContext) -> Tuple[bool, str]:
128
143
  """Verifica status de um release Helm."""
129
144
  if ctx.dry_run:
@@ -217,6 +232,13 @@ def verify_kubernetes(ctx: ExecutionContext) -> bool:
217
232
  services = ["kubelet", "containerd"]
218
233
  all_ok = True
219
234
 
235
+ swap_ok, swap_msg = check_swap_disabled(ctx)
236
+ if swap_ok:
237
+ typer.secho(f" ✓ Swap: {swap_msg}", fg=typer.colors.GREEN)
238
+ else:
239
+ typer.secho(f" ✗ Swap: {swap_msg}", fg=typer.colors.RED)
240
+ all_ok = False
241
+
220
242
  for service in services:
221
243
  ok, status = check_systemd_service(service, ctx)
222
244
  if ok:
@@ -17,6 +17,12 @@ from raijin_server.utils import (
17
17
  )
18
18
 
19
19
 
20
+ CALICO_VERSION = "v3.28.0"
21
+ CALICO_URL = f"https://raw.githubusercontent.com/projectcalico/calico/{CALICO_VERSION}/manifests/calico.yaml"
22
+ DEFAULT_CNI = os.environ.get("RAIJIN_CNI", "calico").lower() # calico|none
23
+ FORCE_CNI = os.environ.get("RAIJIN_FORCE_CNI", "0") == "1"
24
+
25
+
20
26
  def _cleanup_old_repo(ctx: ExecutionContext) -> None:
21
27
  """Remove repo legado apt.kubernetes.io se existir para evitar erro 404."""
22
28
 
@@ -49,6 +55,69 @@ def _reset_cluster(ctx: ExecutionContext) -> None:
49
55
  typer.secho("✓ Limpeza concluida.", fg=typer.colors.GREEN)
50
56
 
51
57
 
58
+ def _cni_present(ctx: ExecutionContext) -> bool:
59
+ """Detecta se ja existe um CNI aplicado (qualquer DaemonSet tipico)."""
60
+
61
+ result = run_cmd(
62
+ [
63
+ "kubectl",
64
+ "get",
65
+ "daemonset",
66
+ "-n",
67
+ "kube-system",
68
+ "-o",
69
+ "jsonpath={.items[*].metadata.name}",
70
+ ],
71
+ ctx,
72
+ check=False,
73
+ )
74
+ if result.returncode != 0:
75
+ return False
76
+ names = (result.stdout or "").split()
77
+ for name in names:
78
+ if any(token in name for token in ("calico", "cilium", "flannel", "weave", "canal")):
79
+ return True
80
+ return False
81
+
82
+
83
+ def _apply_calico(pod_cidr: str, ctx: ExecutionContext) -> None:
84
+ """Aplica Calico com CIDR alinhado ao podSubnet informado."""
85
+
86
+ typer.echo(f"Aplicando Calico ({CALICO_VERSION}) com pod CIDR {pod_cidr}...")
87
+
88
+ if ctx.dry_run:
89
+ typer.echo("[dry-run] kubectl apply -f <calico.yaml>")
90
+ return
91
+
92
+ cmd = (
93
+ f"curl -fsSL --retry 3 --retry-delay 2 {CALICO_URL} "
94
+ f"| sed 's#192.168.0.0/16#{pod_cidr}#g' "
95
+ f"| kubectl apply -f -"
96
+ )
97
+ run_cmd(cmd, ctx, use_shell=True)
98
+
99
+ # Aguarda o daemonset subir para evitar Node NotReady por falta de CNI
100
+ run_cmd(
101
+ ["kubectl", "-n", "kube-system", "rollout", "status", "daemonset/calico-node", "--timeout", "300s"],
102
+ ctx,
103
+ check=False,
104
+ )
105
+ run_cmd(
106
+ [
107
+ "kubectl",
108
+ "-n",
109
+ "kube-system",
110
+ "rollout",
111
+ "status",
112
+ "deployment/calico-kube-controllers",
113
+ "--timeout",
114
+ "300s",
115
+ ],
116
+ ctx,
117
+ check=False,
118
+ )
119
+
120
+
52
121
  def run(ctx: ExecutionContext) -> None:
53
122
  require_root(ctx)
54
123
  typer.echo("Instalando e preparando Kubernetes (kubeadm/kubelet/kubectl)...")
@@ -146,6 +215,11 @@ def run(ctx: ExecutionContext) -> None:
146
215
  enable_service("containerd", ctx)
147
216
  enable_service("kubelet", ctx)
148
217
 
218
+ # Garante swap off antes de prosseguir (requisito kubeadm)
219
+ typer.echo("Desabilitando swap (requisito Kubernetes)...")
220
+ run_cmd(["swapoff", "-a"], ctx, check=False)
221
+ run_cmd("sed -i '/swap/d' /etc/fstab", ctx, use_shell=True, check=False)
222
+
149
223
  # kubeadm exige ip_forward=1; sobrepoe ajuste de hardening para fase de cluster.
150
224
  # Desabilita IPv6 completamente para evitar erros de preflight e simplificar rede
151
225
  sysctl_k8s = """# Kubernetes network settings
@@ -164,7 +238,19 @@ net.ipv6.conf.lo.disable_ipv6=1
164
238
  pod_cidr = typer.prompt("Pod CIDR", default="10.244.0.0/16")
165
239
  service_cidr = typer.prompt("Service CIDR", default="10.96.0.0/12")
166
240
  cluster_name = typer.prompt("Nome do cluster", default="raijin")
167
- advertise_address = typer.prompt("API advertise address", default="0.0.0.0")
241
+ default_adv = "192.168.1.81"
242
+ advertise_address = typer.prompt("API advertise address", default=default_adv)
243
+ if advertise_address != default_adv:
244
+ typer.secho(
245
+ f"⚠ Para ambiente atual use {default_adv} (IP LAN, evita NAT).", fg=typer.colors.YELLOW
246
+ )
247
+ if not typer.confirm(f"Deseja forcar {default_adv}?", default=True):
248
+ typer.secho(
249
+ f"Usando valor informado: {advertise_address}. Certifique-se que todos os nos alcancem esse IP.",
250
+ fg=typer.colors.YELLOW,
251
+ )
252
+ else:
253
+ advertise_address = default_adv
168
254
 
169
255
  kubeadm_config = f"""apiVersion: kubeadm.k8s.io/v1beta3
170
256
  kind: ClusterConfiguration
@@ -221,3 +307,27 @@ cgroupDriver: systemd
221
307
 
222
308
  typer.echo("Comando de join para workers:")
223
309
  run_cmd(["kubeadm", "token", "create", "--print-join-command"], ctx, check=False)
310
+
311
+ # CNI padrao: Calico (pode ser desabilitado via RAIJIN_CNI=none)
312
+ cni_choice = DEFAULT_CNI
313
+ if cni_choice == "none":
314
+ typer.secho(
315
+ "CNI nao aplicado (RAIJIN_CNI=none). Node permanecera NotReady ate aplicar um CNI manual.",
316
+ fg=typer.colors.YELLOW,
317
+ )
318
+ else:
319
+ if _cni_present(ctx) and not FORCE_CNI:
320
+ typer.secho("CNI ja detectado em kube-system; pulando aplicacao automatica (defina RAIJIN_FORCE_CNI=1 para reaplicar).", fg=typer.colors.YELLOW)
321
+ else:
322
+ _apply_calico(pod_cidr, ctx)
323
+
324
+ # Pequeno health check basico para sinalizar ao usuario
325
+ typer.echo("Validando node apos CNI...")
326
+ run_cmd([
327
+ "kubectl",
328
+ "wait",
329
+ "--for=condition=Ready",
330
+ "nodes",
331
+ "--all",
332
+ "--timeout=180s",
333
+ ], ctx, check=False)
@@ -124,9 +124,9 @@ def run(ctx: ExecutionContext) -> None:
124
124
  )
125
125
 
126
126
  iface = typer.prompt("Interface", default="ens18")
127
- address = typer.prompt("Endereco CIDR", default="192.168.0.10/24")
128
- gateway = typer.prompt("Gateway", default="192.168.0.1")
129
- dns = typer.prompt("DNS (separe por virgula)", default="1.1.1.1,8.8.8.8")
127
+ address = typer.prompt("Endereco CIDR", default="192.168.1.81/24")
128
+ gateway = typer.prompt("Gateway", default="192.168.1.254")
129
+ dns = typer.prompt("DNS (separe por virgula)", default="177.128.80.44,177.128.80.45")
130
130
 
131
131
  dns_list = ",".join([item.strip() for item in dns.split(",") if item.strip()])
132
132
  netplan_content = f"""network:
@@ -7,7 +7,14 @@ from pathlib import Path
7
7
 
8
8
  import typer
9
9
 
10
- from raijin_server.utils import ExecutionContext, require_root, run_cmd
10
+ from raijin_server.utils import ExecutionContext, require_root, run_cmd, write_file
11
+
12
+ # Defaults alinhados com configuracao de rede solicitada
13
+ NETPLAN_IFACE = "ens18"
14
+ NETPLAN_ADDRESS = "192.168.1.81/24"
15
+ NETPLAN_GATEWAY = "192.168.1.254"
16
+ NETPLAN_DNS = "177.128.80.44,177.128.80.45"
17
+ NETPLAN_PATH = Path("/etc/netplan/01-raijin-static.yaml")
11
18
 
12
19
  SYSTEMD_SERVICES = [
13
20
  "kubelet",
@@ -48,6 +55,44 @@ APT_MARKERS = [
48
55
  ]
49
56
 
50
57
 
58
+ def _ensure_netplan(ctx: ExecutionContext) -> None:
59
+ """Garante que o netplan esteja com IP fixo esperado; se ja estiver, mostra OK."""
60
+
61
+ desired = f"""network:
62
+ version: 2
63
+ renderer: networkd
64
+ ethernets:
65
+ {NETPLAN_IFACE}:
66
+ dhcp4: false
67
+ addresses: [{NETPLAN_ADDRESS}]
68
+ gateway4: {NETPLAN_GATEWAY}
69
+ nameservers:
70
+ addresses: [{NETPLAN_DNS}]
71
+ """
72
+
73
+ existing = None
74
+ if NETPLAN_PATH.exists():
75
+ try:
76
+ existing = NETPLAN_PATH.read_text()
77
+ except Exception:
78
+ existing = None
79
+
80
+ if existing and all(x in existing for x in (NETPLAN_ADDRESS, NETPLAN_GATEWAY, NETPLAN_DNS)):
81
+ typer.secho(
82
+ f"\n✓ Netplan ja configurado com {NETPLAN_ADDRESS} / gw {NETPLAN_GATEWAY} / dns {NETPLAN_DNS}",
83
+ fg=typer.colors.GREEN,
84
+ )
85
+ return
86
+
87
+ typer.echo("Aplicando netplan padrao antes da limpeza...")
88
+ write_file(NETPLAN_PATH, desired, ctx)
89
+ run_cmd(["netplan", "apply"], ctx, check=False)
90
+ typer.secho(
91
+ f"✓ Netplan ajustado para {NETPLAN_ADDRESS} (gw {NETPLAN_GATEWAY}, dns {NETPLAN_DNS})",
92
+ fg=typer.colors.GREEN,
93
+ )
94
+
95
+
51
96
  def _stop_services(ctx: ExecutionContext) -> None:
52
97
  typer.echo("Parando serviços relacionados (kubelet, containerd)...")
53
98
  for service in SYSTEMD_SERVICES:
@@ -131,6 +176,9 @@ def run(ctx: ExecutionContext) -> None:
131
176
  typer.echo("Sanitizacao cancelada pelo usuario.")
132
177
  return
133
178
 
179
+ # Primeiro passo: garantir netplan consistente, sem quebrar ao limpar
180
+ _ensure_netplan(ctx)
181
+
134
182
  _stop_services(ctx)
135
183
  _kubeadm_reset(ctx)
136
184
  _flush_iptables(ctx)
@@ -15,8 +15,7 @@ def run(ctx: ExecutionContext) -> None:
15
15
  values = [
16
16
  "ingressClass.enabled=true",
17
17
  "ingressClass.isDefaultClass=true",
18
- "ports.web.redirectTo=websecure=true",
19
- "ports.websecure.tls.enabled=true",
18
+ "ports.web.redirectTo=websecure", # valor esperado é o nome da porta de destino
20
19
  "service.type=LoadBalancer",
21
20
  f"certificatesResolvers.letsencrypt.acme.email={acme_email}",
22
21
  "certificatesResolvers.letsencrypt.acme.storage=/data/acme.json",
@@ -15,6 +15,27 @@ import typer
15
15
 
16
16
  from raijin_server.utils import ExecutionContext, logger
17
17
 
18
+ # Grafo de dependencias entre modulos (usado por validacoes e funcoes de rollback)
19
+ MODULE_DEPENDENCIES = {
20
+ "kubernetes": ["essentials", "network", "firewall"],
21
+ "calico": ["kubernetes"],
22
+ "cert_manager": ["kubernetes", "traefik"],
23
+ "istio": ["kubernetes", "calico"],
24
+ "traefik": ["kubernetes"],
25
+ "kong": ["kubernetes"],
26
+ "minio": ["kubernetes"],
27
+ "prometheus": ["kubernetes"],
28
+ "grafana": ["kubernetes", "prometheus"],
29
+ "loki": ["kubernetes"],
30
+ "secrets": ["kubernetes"],
31
+ "harness": ["kubernetes"],
32
+ "velero": ["kubernetes"],
33
+ "kafka": ["kubernetes"],
34
+ "observability_ingress": ["traefik", "prometheus", "grafana"],
35
+ "observability_dashboards": ["prometheus", "grafana"],
36
+ "apokolips_demo": ["kubernetes", "traefik"],
37
+ }
38
+
18
39
 
19
40
  class ValidationError(Exception):
20
41
  """Erro de validacao de pre-requisitos."""
@@ -215,30 +236,10 @@ def check_module_dependencies(module: str, ctx: ExecutionContext) -> bool:
215
236
  Returns:
216
237
  True se todas as dependencias foram satisfeitas
217
238
  """
218
- dependencies = {
219
- "kubernetes": ["essentials", "network", "firewall"],
220
- "calico": ["kubernetes"],
221
- "cert_manager": ["kubernetes", "traefik"],
222
- "istio": ["kubernetes", "calico"],
223
- "traefik": ["kubernetes"],
224
- "kong": ["kubernetes"],
225
- "minio": ["kubernetes"],
226
- "prometheus": ["kubernetes"],
227
- "grafana": ["kubernetes", "prometheus"],
228
- "loki": ["kubernetes"],
229
- "secrets": ["kubernetes"],
230
- "harness": ["kubernetes"],
231
- "velero": ["kubernetes"],
232
- "kafka": ["kubernetes"],
233
- "observability_ingress": ["traefik", "prometheus", "grafana"],
234
- "observability_dashboards": ["prometheus", "grafana"],
235
- "apokolips_demo": ["kubernetes", "traefik"],
236
- }
237
-
238
- if module not in dependencies:
239
+ if module not in MODULE_DEPENDENCIES:
239
240
  return True
240
241
 
241
- required = dependencies[module]
242
+ required = MODULE_DEPENDENCIES[module]
242
243
  missing = []
243
244
 
244
245
  # Verifica arquivos de estado
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.2.7
3
+ Version: 0.2.10
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
@@ -44,6 +44,8 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
44
44
  - Arquitetura: [ARCHITECTURE.md](ARCHITECTURE.md)
45
45
  - Auditoria: [AUDIT.md](AUDIT.md)
46
46
  - Segurança: [SECURITY.md](SECURITY.md)
47
+ - Acesso SSH (Windows): [docs/SSH_WINDOWS.md](docs/SSH_WINDOWS.md)
48
+ - VPN para acesso remoto (WireGuard): [docs/VPN_REMOTE_ACCESS.md](docs/VPN_REMOTE_ACCESS.md)
47
49
 
48
50
  ## Destaques
49
51
 
@@ -60,6 +62,12 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
60
62
  - ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
61
63
 
62
64
  ## Requisitos
65
+ Ubuntu Server 20.04+ com Python 3 disponível. Se precisar instalar/atualizar no host alvo:
66
+
67
+ ```bash
68
+ sudo apt update
69
+ sudo apt install -y python3 python3-venv python3-pip
70
+ ```
63
71
 
64
72
  ## Instalação (sempre em venv midgard)
65
73
 
@@ -110,6 +118,10 @@ sudo -E ~/.venvs/midgard/bin/raijin-server validate
110
118
  sudo -E ~/.venvs/midgard/bin/raijin-server menu
111
119
  ```
112
120
 
121
+ ### Rollback de módulos
122
+ - No menu, após escolher o módulo, selecione `r` para rollback; se houver dependentes já executados, será pedido confirmacao para rollback em cascata dos dependentes antes.
123
+ - Linha de comando: `sudo -E ~/.venvs/midgard/bin/raijin-server rollback <modulo> --cascade/--no-cascade` (best-effort; alguns módulos podem exigir limpeza manual). Caso seja apenas para revisar o efeito, use `-n/--dry-run` no comando principal para não aplicar.
124
+
113
125
  ### Execução Direta de Módulos
114
126
  ```bash
115
127
  # Executar módulo específico
@@ -173,6 +185,9 @@ sudo -E ~/.venvs/midgard/bin/raijin-server debug journal --service containerd --
173
185
  - **[AUDIT.md](AUDIT.md)**: Relatório completo de auditoria e melhorias implementadas
174
186
  - **[ARCHITECTURE.md](ARCHITECTURE.md)**: Arquitetura técnica do ambiente
175
187
  - **[SECURITY.md](SECURITY.md)**: Políticas de segurança e reporte de vulnerabilidades
188
+ - **Publicação PyPI**: ver seção "Publicar no PyPI" abaixo
189
+ - **CNI automático**: Calico aplicado automaticamente no passo Kubernetes (override com `RAIJIN_CNI=none`)
190
+ - Para reaplicar CNI (forçar mesmo se já houver): `RAIJIN_FORCE_CNI=1`
176
191
 
177
192
  ## Fluxo de Execução Recomendado
178
193
 
@@ -329,6 +344,46 @@ bash "$SCRIPT_PATH"
329
344
 
330
345
  O helper garante o caminho absoluto correto independentemente de onde o pacote foi instalado.
331
346
 
347
+ ## Publicar no PyPI
348
+
349
+ Use o venv local do repositório (`.venv`) para garantir dependências corretas:
350
+
351
+ ```bash
352
+ cd /home/rafael/github/raijin-server
353
+ python3 -m venv .venv
354
+ source .venv/bin/activate
355
+ python -m pip install -U pip build twine
356
+ ```
357
+
358
+ Gerar artefatos limpos:
359
+
360
+ ```bash
361
+ rm -rf dist build
362
+ python -m build --sdist --wheel --outdir dist
363
+ ```
364
+
365
+ Publicar no PyPI (requere token):
366
+
367
+ ```bash
368
+ export TWINE_USERNAME="__token__"
369
+ export TWINE_PASSWORD="pypi-xxxxx" # token do PyPI
370
+ python -m twine upload dist/*
371
+ ```
372
+
373
+ Opcional: validar no TestPyPI antes de publicar (precisa token de TestPyPI):
374
+
375
+ ```bash
376
+ export TWINE_USERNAME="__token__"
377
+ export TWINE_PASSWORD="pypi-xxxxx" # token do TestPyPI
378
+ python -m twine upload --repository testpypi dist/*
379
+ ```
380
+
381
+ Depois de publicar, atualize/instale:
382
+
383
+ ```bash
384
+ pip install -U raijin-server
385
+ ```
386
+
332
387
  ## Teste de ingress (Apokolips)
333
388
 
334
389
  O módulo [src/raijin_server/modules/apokolips_demo.py](src/raijin_server/modules/apokolips_demo.py) cria um namespace dedicado, ConfigMap com HTML, Deployment NGINX, Service e Ingress Traefik com uma landing page "Apokolips" para validar o tráfego externo.
@@ -437,17 +492,18 @@ O Twine é a ferramenta oficial para enviar pacotes Python ao PyPI com upload se
437
492
  Passo a passo:
438
493
  ```bash
439
494
  # 1) Gere artefatos
440
- python -m build --sdist --wheel --outdir dist/
495
+ python3 -m pip install --user build
496
+ python3 -m build --sdist --wheel --outdir dist/
441
497
 
442
498
  # 2) Configure o token (crie em https://pypi.org/manage/account/token/)
443
499
  export TWINE_USERNAME=__token__
444
500
  export TWINE_PASSWORD="<seu-token>"
445
501
 
446
502
  # 3) Envie para o PyPI
447
- python -m twine upload dist/*
503
+ python3 -m twine upload dist/*
448
504
 
449
505
  # 4) Verifique instalação
450
- python -m pip install -U raijin-server
506
+ python3 -m pip install -U raijin-server
451
507
  raijin-server --version
452
508
  ```
453
509
 
@@ -1,9 +1,9 @@
1
- raijin_server/__init__.py,sha256=30PUXP9hr-N0U9chGsPaORRkJKEeGnKMrcXhWTwR054,94
2
- raijin_server/cli.py,sha256=aQxew8FCN-mZoN-ghBasm97gLk5WkOaIcpeucTpXpXY,24821
3
- raijin_server/config.py,sha256=Dta2CS1d6RgNiQ84P6dTXk98boFrjzuvhs_fCdlm0I4,4810
4
- raijin_server/healthchecks.py,sha256=BJyWyUDtEswEblvGwWMejtMnsUb8kJcULVdS9iycrcc,14565
1
+ raijin_server/__init__.py,sha256=xZWt85TH3yzTcBTI8N0qtHFiSRHQ8nXGICr5curtX6E,95
2
+ raijin_server/cli.py,sha256=DwzUP5Ps-vgiOwXRjACukgHWFo-7FAnDrILbgWhs2ys,28475
3
+ raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
4
+ raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
5
5
  raijin_server/utils.py,sha256=9RnGnPoUTYOpMVRLNa4P4lIQrJNQLkSkPUxycZRGv78,20827
6
- raijin_server/validators.py,sha256=qOZMHgwjHogVf17UPlxfUCpQd9qAGQW7tycd8mUvnEs,9404
6
+ raijin_server/validators.py,sha256=-V3BX1hF_i2on7v-7Gu6u4ilhQaVJX4jTnwqvR3uEng,9432
7
7
  raijin_server/modules/__init__.py,sha256=e_IbkhLGPcF8to9QUmIESP6fpcTOYcIhaXLKIvqRJMY,920
8
8
  raijin_server/modules/apokolips_demo.py,sha256=8ltsXRbVDwlDwLMIvh02NG-FeAfBWw_v6lh7IGOyNqs,13725
9
9
  raijin_server/modules/bootstrap.py,sha256=oVIGNRW_JbgY8zXNHGAIP0vGbbHNHyQexthxo5zhbcw,9762
@@ -18,17 +18,17 @@ raijin_server/modules/harness.py,sha256=dhZ89YIhlkuxiRU1deN6wXVWnXm0xeI03PwYf_qg
18
18
  raijin_server/modules/istio.py,sha256=761FOGEzEXWlTLYApQxUWY8l4cnEbnIXbIHK3itk_AQ,522
19
19
  raijin_server/modules/kafka.py,sha256=bp8k_IhuAIO6dL0IpK1UxxLZoGih6nJp0ZnzwmiZEj8,950
20
20
  raijin_server/modules/kong.py,sha256=2EZKYBmBhm_7Nduw9PWrvrekp0VCxQbc2gElpAJqKfg,491
21
- raijin_server/modules/kubernetes.py,sha256=zHbgCYzzdJJwUGsxJoiyT4HCeJz2HmDSeBR88KP-v4Y,8286
21
+ raijin_server/modules/kubernetes.py,sha256=waSf2cCVnLicN5o3M47MzMzmHHtvKeFXm1__8ynQzA0,11871
22
22
  raijin_server/modules/loki.py,sha256=erwFfSiSFOv-Ul3nFdrI2RElPYuqqBPBBa_MJAwyLys,676
23
23
  raijin_server/modules/minio.py,sha256=BVvsEaJlJUV92_ep7pKsBhSYPjWZrDOB3J6XAWYAHYg,486
24
- raijin_server/modules/network.py,sha256=bwVljaVvTc6FbbD-XtDpqqNL-fXMB9-iWVWsXToBvt4,4804
24
+ raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
25
25
  raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
26
26
  raijin_server/modules/observability_ingress.py,sha256=Fh1rlFWueBNHnOkHuoHYyhILmpO-iQXINybSUYbYsHQ,5738
27
27
  raijin_server/modules/prometheus.py,sha256=Rs9BREmaoKlyteNdAQZnSIeJfsRO0RQKyyL2gTnXyCw,3716
28
- raijin_server/modules/sanitize.py,sha256=eytL_mCYF57qnjf6g752VRC4Yl27dDJ0OQP2rjxaR70,4523
28
+ raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
29
29
  raijin_server/modules/secrets.py,sha256=xpV3gIMnwQdAI2j69Ck5daIK4wlYJA_1rkWTtSfVNk0,3715
30
30
  raijin_server/modules/ssh_hardening.py,sha256=oQdk-EVnEHNMKIWvoFuZzI4jK0nNO8IAY4hkB4pj8zw,4025
31
- raijin_server/modules/traefik.py,sha256=DCyh9dOvryoPR8qKzvvvvZcMTBYsiTtcKXdselto9gQ,1412
31
+ raijin_server/modules/traefik.py,sha256=e7Rog5fQvbCHI4WkljUUa6g0Alx2V5ZiVZboHWmMCxY,1411
32
32
  raijin_server/modules/velero.py,sha256=_CV0QQnWr5L-CWXDOiD9Ef4J7GaQT-s9yNBwqp_FLOY,1395
33
33
  raijin_server/modules/vpn.py,sha256=hF-0vA17VKTxhQLDBSEeqI5aPQpiaaj4IpUf9l6lr64,8297
34
34
  raijin_server/scripts/__init__.py,sha256=deduGfHf8BMVWred4ux5LfBDT2NJ5XYeJAt2sDEU4qs,53
@@ -36,9 +36,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
36
36
  raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
37
37
  raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
38
38
  raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
39
- raijin_server-0.2.7.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
40
- raijin_server-0.2.7.dist-info/METADATA,sha256=YpgpUhp0TYGWYwEkKd8nDpCLY0MfyWsCWPq7D0zTrJQ,20362
41
- raijin_server-0.2.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
42
- raijin_server-0.2.7.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
43
- raijin_server-0.2.7.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
44
- raijin_server-0.2.7.dist-info/RECORD,,
39
+ raijin_server-0.2.10.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
40
+ raijin_server-0.2.10.dist-info/METADATA,sha256=_oOzaANRnNg3d3SIcyKxk0rMzwwdbhcMPNs2c7Oyptg,22276
41
+ raijin_server-0.2.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
42
+ raijin_server-0.2.10.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
43
+ raijin_server-0.2.10.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
44
+ raijin_server-0.2.10.dist-info/RECORD,,