raijin-server 0.2.0__tar.gz → 0.2.2__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.0/src/raijin_server.egg-info → raijin_server-0.2.2}/PKG-INFO +13 -8
- {raijin_server-0.2.0 → raijin_server-0.2.2}/README.md +12 -7
- {raijin_server-0.2.0 → raijin_server-0.2.2}/setup.cfg +1 -1
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/__init__.py +1 -1
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/cli.py +18 -1
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/bootstrap.py +21 -1
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/calico.py +29 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/cert_manager.py +29 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/kubernetes.py +40 -4
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/utils.py +17 -6
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/validators.py +38 -4
- {raijin_server-0.2.0 → raijin_server-0.2.2/src/raijin_server.egg-info}/PKG-INFO +13 -8
- {raijin_server-0.2.0 → raijin_server-0.2.2}/LICENSE +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/pyproject.toml +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/config.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/healthchecks.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/__init__.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/apokolips_demo.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/essentials.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/firewall.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/full_install.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/grafana.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/hardening.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/harness.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/istio.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/kafka.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/kong.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/loki.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/minio.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/network.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/observability_dashboards.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/observability_ingress.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/prometheus.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/sanitize.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/secrets.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/ssh_hardening.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/traefik.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/velero.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/modules/vpn.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/scripts/__init__.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/scripts/checklist.sh +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/scripts/install.sh +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/scripts/log_size_metric.sh +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server/scripts/pre-deploy-check.sh +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server.egg-info/SOURCES.txt +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server.egg-info/dependency_links.txt +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server.egg-info/entry_points.txt +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server.egg-info/requires.txt +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/src/raijin_server.egg-info/top_level.txt +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/tests/test_full_install_sequence.py +0 -0
- {raijin_server-0.2.0 → raijin_server-0.2.2}/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.2
|
|
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
|
|
@@ -39,11 +39,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
39
39
|
|
|
40
40
|
## Destaques
|
|
41
41
|
|
|
42
|
-
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade
|
|
42
|
+
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
43
43
|
- ✅ **Health Checks Automáticos**: Valida serviços após instalação
|
|
44
|
-
- ✅ **Retry Inteligente**: Resistente a falhas temporárias de rede
|
|
45
|
-
- ✅ **Logging Estruturado**: Logs persistentes
|
|
44
|
+
- ✅ **Retry Inteligente com Backoff**: Resistente a falhas temporárias de rede (5 tentativas, backoff exponencial)
|
|
45
|
+
- ✅ **Logging Estruturado**: Logs persistentes com rotação (20MB, 5 backups)
|
|
46
46
|
- ✅ **Gestão de Dependências**: Garante ordem correta de execução
|
|
47
|
+
- ✅ **Verificação de Cluster**: Módulos que dependem de K8s verificam disponibilidade antes de executar
|
|
48
|
+
- ✅ **Clean Automático**: Opção de limpar instalação anterior ao re-executar kubernetes
|
|
49
|
+
- ✅ **IPv4 Only**: IPv6 desabilitado por padrão para simplificar rede
|
|
47
50
|
- ✅ **Configuração via Arquivo**: Automação completa com YAML/JSON
|
|
48
51
|
- ✅ **Idempotência**: Re-execução segura sem quebrar o sistema
|
|
49
52
|
- ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
|
|
@@ -180,7 +183,9 @@ se deseja pular. Se executar manualmente, basta responder "não" quando pergunta
|
|
|
180
183
|
|
|
181
184
|
### Comandos Úteis
|
|
182
185
|
```bash
|
|
183
|
-
# Versão
|
|
186
|
+
# Versão (flag ou comando)
|
|
187
|
+
raijin-server --version
|
|
188
|
+
raijin-server -V
|
|
184
189
|
raijin-server version
|
|
185
190
|
|
|
186
191
|
# Monitorar logs
|
|
@@ -222,9 +227,9 @@ tail -f /var/log/raijin-server/raijin-server.log
|
|
|
222
227
|
- `network`: Netplan com IP fixo e DNS. **OPCIONAL** se IP já configurado pelo provedor ISP.
|
|
223
228
|
- `firewall`: UFW com regras para SSH/HTTP/HTTPS/K8s.
|
|
224
229
|
- `vpn`: provisiona WireGuard (servidor + cliente inicial) e libera firewall.
|
|
225
|
-
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig.
|
|
226
|
-
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado.
|
|
227
|
-
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01).
|
|
230
|
+
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig. **Oferece limpeza automática** se detectar instalação anterior. IPv6 desabilitado por padrão.
|
|
231
|
+
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado. **Verifica cluster ativo** antes de aplicar.
|
|
232
|
+
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01). **Verifica cluster ativo** antes de instalar.
|
|
228
233
|
- `secrets`: instala sealed-secrets e external-secrets via Helm.
|
|
229
234
|
- `istio`: istioctl install (perfil raijin) e injeção automática.
|
|
230
235
|
- `traefik`: IngressController com TLS/ACME.
|
|
@@ -6,11 +6,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
6
6
|
|
|
7
7
|
## Destaques
|
|
8
8
|
|
|
9
|
-
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade
|
|
9
|
+
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
10
10
|
- ✅ **Health Checks Automáticos**: Valida serviços após instalação
|
|
11
|
-
- ✅ **Retry Inteligente**: Resistente a falhas temporárias de rede
|
|
12
|
-
- ✅ **Logging Estruturado**: Logs persistentes
|
|
11
|
+
- ✅ **Retry Inteligente com Backoff**: Resistente a falhas temporárias de rede (5 tentativas, backoff exponencial)
|
|
12
|
+
- ✅ **Logging Estruturado**: Logs persistentes com rotação (20MB, 5 backups)
|
|
13
13
|
- ✅ **Gestão de Dependências**: Garante ordem correta de execução
|
|
14
|
+
- ✅ **Verificação de Cluster**: Módulos que dependem de K8s verificam disponibilidade antes de executar
|
|
15
|
+
- ✅ **Clean Automático**: Opção de limpar instalação anterior ao re-executar kubernetes
|
|
16
|
+
- ✅ **IPv4 Only**: IPv6 desabilitado por padrão para simplificar rede
|
|
14
17
|
- ✅ **Configuração via Arquivo**: Automação completa com YAML/JSON
|
|
15
18
|
- ✅ **Idempotência**: Re-execução segura sem quebrar o sistema
|
|
16
19
|
- ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
|
|
@@ -147,7 +150,9 @@ se deseja pular. Se executar manualmente, basta responder "não" quando pergunta
|
|
|
147
150
|
|
|
148
151
|
### Comandos Úteis
|
|
149
152
|
```bash
|
|
150
|
-
# Versão
|
|
153
|
+
# Versão (flag ou comando)
|
|
154
|
+
raijin-server --version
|
|
155
|
+
raijin-server -V
|
|
151
156
|
raijin-server version
|
|
152
157
|
|
|
153
158
|
# Monitorar logs
|
|
@@ -189,9 +194,9 @@ tail -f /var/log/raijin-server/raijin-server.log
|
|
|
189
194
|
- `network`: Netplan com IP fixo e DNS. **OPCIONAL** se IP já configurado pelo provedor ISP.
|
|
190
195
|
- `firewall`: UFW com regras para SSH/HTTP/HTTPS/K8s.
|
|
191
196
|
- `vpn`: provisiona WireGuard (servidor + cliente inicial) e libera firewall.
|
|
192
|
-
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig.
|
|
193
|
-
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado.
|
|
194
|
-
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01).
|
|
197
|
+
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig. **Oferece limpeza automática** se detectar instalação anterior. IPv6 desabilitado por padrão.
|
|
198
|
+
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado. **Verifica cluster ativo** antes de aplicar.
|
|
199
|
+
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01). **Verifica cluster ativo** antes de instalar.
|
|
195
200
|
- `secrets`: instala sealed-secrets e external-secrets via Helm.
|
|
196
201
|
- `istio`: istioctl install (perfil raijin) e injeção automática.
|
|
197
202
|
- `traefik`: IngressController com TLS/ACME.
|
|
@@ -265,6 +265,14 @@ def _render_menu(dry_run: bool) -> int:
|
|
|
265
265
|
return exit_idx
|
|
266
266
|
|
|
267
267
|
|
|
268
|
+
def _version_callback(value: bool) -> None:
|
|
269
|
+
"""Imprime a versao e encerra imediatamente."""
|
|
270
|
+
|
|
271
|
+
if value:
|
|
272
|
+
typer.echo(f"raijin-server {__version__}")
|
|
273
|
+
raise typer.Exit()
|
|
274
|
+
|
|
275
|
+
|
|
268
276
|
def interactive_menu(ctx: typer.Context) -> None:
|
|
269
277
|
exec_ctx = ctx.obj or ExecutionContext()
|
|
270
278
|
current_dry_run = exec_ctx.dry_run
|
|
@@ -315,6 +323,15 @@ def main(
|
|
|
315
323
|
module: Optional[str] = typer.Option(None, "-m", "--module", help="Modulo a executar"),
|
|
316
324
|
dry_run: bool = typer.Option(False, "-n", "--dry-run", help="Mostra comandos sem executa-los."),
|
|
317
325
|
skip_validation: bool = typer.Option(False, "--skip-validation", help="Pula validacoes de pre-requisitos"),
|
|
326
|
+
skip_root: bool = typer.Option(False, "--skip-root", help="Permite validar sem exigir root (nao recomendado)"),
|
|
327
|
+
version: Optional[bool] = typer.Option(
|
|
328
|
+
None,
|
|
329
|
+
"--version",
|
|
330
|
+
"-V",
|
|
331
|
+
is_eager=True,
|
|
332
|
+
callback=_version_callback,
|
|
333
|
+
help="Mostra a versao do CLI e sai",
|
|
334
|
+
),
|
|
318
335
|
) -> None:
|
|
319
336
|
"""Mostra um menu simples quando nenhum subcomando e informado."""
|
|
320
337
|
|
|
@@ -322,7 +339,7 @@ def main(
|
|
|
322
339
|
|
|
323
340
|
# Executa validacoes de pre-requisitos
|
|
324
341
|
if not skip_validation and not dry_run:
|
|
325
|
-
if not validate_system_requirements(ctx.obj, skip_root=
|
|
342
|
+
if not validate_system_requirements(ctx.obj, skip_root=skip_root):
|
|
326
343
|
typer.secho("\nAbortando devido a pre-requisitos nao atendidos.", fg=typer.colors.RED)
|
|
327
344
|
typer.echo("Use --skip-validation para pular validacoes (nao recomendado).")
|
|
328
345
|
raise typer.Exit(code=1)
|
|
@@ -87,17 +87,37 @@ def _install_istioctl(ctx: ExecutionContext) -> None:
|
|
|
87
87
|
return
|
|
88
88
|
|
|
89
89
|
typer.echo(f"Instalando istioctl v{ISTIOCTL_VERSION}...")
|
|
90
|
+
|
|
91
|
+
# Limpa tentativas anteriores
|
|
92
|
+
istio_dir = Path(f"/tmp/istio-{ISTIOCTL_VERSION}")
|
|
93
|
+
if istio_dir.exists() and not ctx.dry_run:
|
|
94
|
+
run_cmd(["rm", "-rf", str(istio_dir)], ctx, check=False)
|
|
95
|
+
|
|
96
|
+
# Download do Istio
|
|
90
97
|
run_cmd(
|
|
91
98
|
f"curl -L https://istio.io/downloadIstio | ISTIO_VERSION={ISTIOCTL_VERSION} sh -",
|
|
92
99
|
ctx,
|
|
93
100
|
use_shell=True,
|
|
94
101
|
cwd="/tmp",
|
|
95
102
|
)
|
|
103
|
+
|
|
104
|
+
# Verifica se o download foi bem sucedido
|
|
105
|
+
istioctl_path = Path(f"/tmp/istio-{ISTIOCTL_VERSION}/bin/istioctl")
|
|
106
|
+
if not ctx.dry_run and not istioctl_path.exists():
|
|
107
|
+
typer.secho(
|
|
108
|
+
f"Falha ao baixar istioctl. Arquivo nao encontrado: {istioctl_path}",
|
|
109
|
+
fg=typer.colors.RED,
|
|
110
|
+
)
|
|
111
|
+
typer.secho("Verifique sua conexao de internet e tente novamente.", fg=typer.colors.YELLOW)
|
|
112
|
+
ctx.warnings.append("istioctl nao instalado - download falhou")
|
|
113
|
+
return # Nao falha o modulo inteiro, apenas avisa
|
|
114
|
+
|
|
96
115
|
run_cmd(
|
|
97
|
-
["mv",
|
|
116
|
+
["mv", str(istioctl_path), "/usr/local/bin/istioctl"],
|
|
98
117
|
ctx,
|
|
99
118
|
)
|
|
100
119
|
run_cmd(["chmod", "+x", "/usr/local/bin/istioctl"], ctx)
|
|
120
|
+
typer.secho("✓ istioctl instalado com sucesso.", fg=typer.colors.GREEN)
|
|
101
121
|
|
|
102
122
|
|
|
103
123
|
def _install_velero(ctx: ExecutionContext) -> None:
|
|
@@ -62,11 +62,40 @@ def _split_namespaces(raw_value: str) -> Iterable[str]:
|
|
|
62
62
|
return [ns.strip() for ns in raw_value.split(",") if ns.strip()]
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def _check_cluster_available(ctx: ExecutionContext) -> bool:
|
|
66
|
+
"""Verifica se o cluster Kubernetes esta acessivel."""
|
|
67
|
+
if ctx.dry_run:
|
|
68
|
+
return True
|
|
69
|
+
try:
|
|
70
|
+
result = run_cmd(
|
|
71
|
+
["kubectl", "cluster-info"],
|
|
72
|
+
ctx,
|
|
73
|
+
check=False,
|
|
74
|
+
retries=1,
|
|
75
|
+
)
|
|
76
|
+
return result.returncode == 0
|
|
77
|
+
except Exception:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
65
81
|
def run(ctx: ExecutionContext) -> None:
|
|
66
82
|
require_root(ctx)
|
|
67
83
|
ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou habilite dry-run.")
|
|
68
84
|
ensure_tool("curl", ctx, install_hint="Instale curl.")
|
|
69
85
|
|
|
86
|
+
# Verifica se cluster esta disponivel antes de aplicar
|
|
87
|
+
if not _check_cluster_available(ctx):
|
|
88
|
+
typer.secho(
|
|
89
|
+
"✗ Cluster Kubernetes nao esta acessivel. Execute o modulo 'kubernetes' primeiro.",
|
|
90
|
+
fg=typer.colors.RED,
|
|
91
|
+
)
|
|
92
|
+
typer.secho(
|
|
93
|
+
" Verifique: kubectl cluster-info",
|
|
94
|
+
fg=typer.colors.YELLOW,
|
|
95
|
+
)
|
|
96
|
+
ctx.errors.append("Calico: cluster nao acessivel")
|
|
97
|
+
raise typer.Exit(code=1)
|
|
98
|
+
|
|
70
99
|
typer.echo("Aplicando Calico como CNI...")
|
|
71
100
|
pod_cidr = typer.prompt("Pod CIDR (Calico)", default="10.244.0.0/16")
|
|
72
101
|
|
|
@@ -79,11 +79,40 @@ stringData:
|
|
|
79
79
|
"""
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def _check_cluster_available(ctx: ExecutionContext) -> bool:
|
|
83
|
+
"""Verifica se o cluster Kubernetes esta acessivel."""
|
|
84
|
+
if ctx.dry_run:
|
|
85
|
+
return True
|
|
86
|
+
try:
|
|
87
|
+
import subprocess
|
|
88
|
+
result = subprocess.run(
|
|
89
|
+
["kubectl", "cluster-info"],
|
|
90
|
+
capture_output=True,
|
|
91
|
+
timeout=30,
|
|
92
|
+
)
|
|
93
|
+
return result.returncode == 0
|
|
94
|
+
except Exception:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
82
98
|
def run(ctx: ExecutionContext) -> None:
|
|
83
99
|
require_root(ctx)
|
|
84
100
|
ensure_tool("helm", ctx, install_hint="Instale helm ou use --dry-run para simular.")
|
|
85
101
|
ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou use --dry-run para simular.")
|
|
86
102
|
|
|
103
|
+
# Verifica se cluster esta disponivel antes de instalar
|
|
104
|
+
if not _check_cluster_available(ctx):
|
|
105
|
+
typer.secho(
|
|
106
|
+
"✗ Cluster Kubernetes nao esta acessivel. Execute o modulo 'kubernetes' primeiro.",
|
|
107
|
+
fg=typer.colors.RED,
|
|
108
|
+
)
|
|
109
|
+
typer.secho(
|
|
110
|
+
" Verifique: kubectl cluster-info",
|
|
111
|
+
fg=typer.colors.YELLOW,
|
|
112
|
+
)
|
|
113
|
+
ctx.errors.append("cert-manager: cluster nao acessivel")
|
|
114
|
+
raise typer.Exit(code=1)
|
|
115
|
+
|
|
87
116
|
typer.echo("Instalando cert-manager via Helm...")
|
|
88
117
|
email = typer.prompt("Email para ACME (Let's Encrypt)", default="admin@example.com")
|
|
89
118
|
solver = typer.prompt("Tipo de desafio (http01/dns01)", default="http01")
|
|
@@ -35,6 +35,20 @@ def _cleanup_old_repo(ctx: ExecutionContext) -> None:
|
|
|
35
35
|
typer.echo("Nao foi possivel ler/remover repo antigo apt.kubernetes.io (ok prosseguir).")
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
def _reset_cluster(ctx: ExecutionContext) -> None:
|
|
39
|
+
"""Executa kubeadm reset para limpar instalacao anterior."""
|
|
40
|
+
typer.secho("Limpando instalacao anterior do Kubernetes...", fg=typer.colors.YELLOW)
|
|
41
|
+
run_cmd(["kubeadm", "reset", "-f"], ctx, check=False)
|
|
42
|
+
# Remove configs residuais
|
|
43
|
+
run_cmd(["rm", "-rf", "/etc/kubernetes/manifests", "/etc/kubernetes/pki"], ctx, check=False)
|
|
44
|
+
run_cmd(["rm", "-rf", "/var/lib/etcd"], ctx, check=False)
|
|
45
|
+
run_cmd(["rm", "-rf", "/root/.kube/config"], ctx, check=False)
|
|
46
|
+
# Remove CNI configs
|
|
47
|
+
run_cmd(["rm", "-rf", "/etc/cni/net.d"], ctx, check=False)
|
|
48
|
+
run_cmd(["rm", "-rf", "/var/lib/cni"], ctx, check=False)
|
|
49
|
+
typer.secho("✓ Limpeza concluida.", fg=typer.colors.GREEN)
|
|
50
|
+
|
|
51
|
+
|
|
38
52
|
def run(ctx: ExecutionContext) -> None:
|
|
39
53
|
require_root(ctx)
|
|
40
54
|
typer.echo("Instalando e preparando Kubernetes (kubeadm/kubelet/kubectl)...")
|
|
@@ -43,7 +57,10 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
43
57
|
kubeconfig_exists = Path("/etc/kubernetes/admin.conf").exists()
|
|
44
58
|
if kubeconfig_exists and not ctx.dry_run:
|
|
45
59
|
typer.secho("⚠ Cluster Kubernetes ja parece estar inicializado.", fg=typer.colors.YELLOW)
|
|
46
|
-
|
|
60
|
+
reset_choice = typer.confirm("Deseja limpar e reinstalar? (recomendado)")
|
|
61
|
+
if reset_choice:
|
|
62
|
+
_reset_cluster(ctx)
|
|
63
|
+
elif not typer.confirm("Deseja continuar sem limpar? (pode causar problemas)"):
|
|
47
64
|
typer.echo("Operacao cancelada pelo usuario.")
|
|
48
65
|
return
|
|
49
66
|
|
|
@@ -130,8 +147,18 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
130
147
|
enable_service("kubelet", ctx)
|
|
131
148
|
|
|
132
149
|
# kubeadm exige ip_forward=1; sobrepoe ajuste de hardening para fase de cluster.
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
# Desabilita IPv6 completamente para evitar erros de preflight e simplificar rede
|
|
151
|
+
sysctl_k8s = """# Kubernetes network settings
|
|
152
|
+
net.ipv4.ip_forward=1
|
|
153
|
+
net.bridge.bridge-nf-call-iptables=1
|
|
154
|
+
net.bridge.bridge-nf-call-ip6tables=1
|
|
155
|
+
# Disable IPv6 completely
|
|
156
|
+
net.ipv6.conf.all.disable_ipv6=1
|
|
157
|
+
net.ipv6.conf.default.disable_ipv6=1
|
|
158
|
+
net.ipv6.conf.lo.disable_ipv6=1
|
|
159
|
+
"""
|
|
160
|
+
write_file(Path("/etc/sysctl.d/99-kubernetes.conf"), sysctl_k8s, ctx)
|
|
161
|
+
run_cmd(["sysctl", "--system"], ctx, check=False)
|
|
135
162
|
|
|
136
163
|
# Prompts de configuracao
|
|
137
164
|
pod_cidr = typer.prompt("Pod CIDR", default="10.244.0.0/16")
|
|
@@ -168,7 +195,16 @@ cgroupDriver: systemd
|
|
|
168
195
|
|
|
169
196
|
ensure_tool("kubeadm", ctx, install_hint="Instale kubeadm (apt install kubeadm).")
|
|
170
197
|
run_cmd(["kubeadm", "config", "images", "pull"], ctx, check=False)
|
|
171
|
-
|
|
198
|
+
# Ignora erros de preflight relacionados a IPv6 (desabilitado)
|
|
199
|
+
run_cmd(
|
|
200
|
+
[
|
|
201
|
+
"kubeadm", "init",
|
|
202
|
+
"--config", str(cfg_path),
|
|
203
|
+
"--upload-certs",
|
|
204
|
+
"--ignore-preflight-errors=FileContent--proc-sys-net-ipv6-conf-default-forwarding",
|
|
205
|
+
],
|
|
206
|
+
ctx,
|
|
207
|
+
)
|
|
172
208
|
|
|
173
209
|
# Configura kubeconfig para root e sudoer.
|
|
174
210
|
run_cmd(["mkdir", "-p", "/root/.kube"], ctx)
|
|
@@ -51,9 +51,10 @@ class ExecutionContext:
|
|
|
51
51
|
|
|
52
52
|
dry_run: bool = False
|
|
53
53
|
assume_yes: bool = True
|
|
54
|
-
max_retries: int =
|
|
55
|
-
retry_delay: int =
|
|
56
|
-
|
|
54
|
+
max_retries: int = 5
|
|
55
|
+
retry_delay: int = 10
|
|
56
|
+
retry_backoff: float = 1.5 # Multiplier for exponential backoff
|
|
57
|
+
timeout: int = 600 # 10 min for slow connections
|
|
57
58
|
errors: list = field(default_factory=list)
|
|
58
59
|
warnings: list = field(default_factory=list)
|
|
59
60
|
|
|
@@ -144,14 +145,24 @@ def run_cmd(
|
|
|
144
145
|
logger.warning(msg)
|
|
145
146
|
ctx.warnings.append(msg)
|
|
146
147
|
if attempt < max_attempts:
|
|
147
|
-
|
|
148
|
+
backoff_delay = int(ctx.retry_delay * (ctx.retry_backoff ** (attempt - 1)))
|
|
149
|
+
typer.secho(
|
|
150
|
+
f"Timeout! Aguardando {backoff_delay}s antes de tentar novamente...",
|
|
151
|
+
fg=typer.colors.YELLOW,
|
|
152
|
+
)
|
|
153
|
+
time.sleep(backoff_delay)
|
|
148
154
|
except subprocess.CalledProcessError as e:
|
|
149
155
|
last_error = e
|
|
150
156
|
msg = f"Comando falhou com codigo {e.returncode} (tentativa {attempt}/{max_attempts})"
|
|
151
157
|
logger.error(f"{msg}: {e.stderr if hasattr(e, 'stderr') else ''}")
|
|
152
158
|
if attempt < max_attempts:
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
# Exponential backoff: delay * backoff^(attempt-1)
|
|
160
|
+
backoff_delay = int(ctx.retry_delay * (ctx.retry_backoff ** (attempt - 1)))
|
|
161
|
+
typer.secho(
|
|
162
|
+
f"Tentando novamente em {backoff_delay}s... (possivel instabilidade de rede)",
|
|
163
|
+
fg=typer.colors.YELLOW,
|
|
164
|
+
)
|
|
165
|
+
time.sleep(backoff_delay)
|
|
155
166
|
else:
|
|
156
167
|
ctx.errors.append(msg)
|
|
157
168
|
if check:
|
|
@@ -6,6 +6,8 @@ import os
|
|
|
6
6
|
import platform
|
|
7
7
|
import shutil
|
|
8
8
|
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import urllib.request
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from typing import List, Tuple
|
|
11
13
|
|
|
@@ -77,10 +79,11 @@ def check_memory(min_gb: int = 4) -> Tuple[bool, str]:
|
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
def check_connectivity(hosts: List[str] | None = None) -> Tuple[bool, str]:
|
|
80
|
-
"""Verifica conectividade com internet."""
|
|
82
|
+
"""Verifica conectividade com internet via ICMP e HTTP."""
|
|
81
83
|
if hosts is None:
|
|
82
84
|
hosts = ["8.8.8.8", "1.1.1.1"]
|
|
83
85
|
|
|
86
|
+
# Primeiro tenta ICMP
|
|
84
87
|
for host in hosts:
|
|
85
88
|
try:
|
|
86
89
|
result = subprocess.run(
|
|
@@ -93,7 +96,13 @@ def check_connectivity(hosts: List[str] | None = None) -> Tuple[bool, str]:
|
|
|
93
96
|
except Exception:
|
|
94
97
|
continue
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
# Fallback HTTP (caso ICMP seja bloqueado)
|
|
100
|
+
try:
|
|
101
|
+
req = urllib.request.Request("https://www.google.com", method="HEAD")
|
|
102
|
+
with urllib.request.urlopen(req, timeout=5):
|
|
103
|
+
return True, "Conectividade HTTP OK (https://www.google.com)"
|
|
104
|
+
except Exception:
|
|
105
|
+
return False, "Sem conectividade com internet (ICMP e HTTP falharam)"
|
|
97
106
|
|
|
98
107
|
|
|
99
108
|
def check_required_commands(commands: List[str] | None = None) -> Tuple[bool, List[str]]:
|
|
@@ -111,11 +120,34 @@ def check_required_commands(commands: List[str] | None = None) -> Tuple[bool, Li
|
|
|
111
120
|
return True, []
|
|
112
121
|
|
|
113
122
|
|
|
123
|
+
def check_virtualenv() -> Tuple[bool, str]:
|
|
124
|
+
"""Valida se a execucao esta dentro de um ambiente isolado (venv/pyenv)."""
|
|
125
|
+
|
|
126
|
+
in_venv = sys.prefix != sys.base_prefix or os.environ.get("VIRTUAL_ENV")
|
|
127
|
+
externally_managed = Path(sys.prefix).joinpath("../EXTERNALLY-MANAGED").resolve()
|
|
128
|
+
|
|
129
|
+
if in_venv:
|
|
130
|
+
return True, "Executando em ambiente virtual"
|
|
131
|
+
|
|
132
|
+
if externally_managed.exists():
|
|
133
|
+
return False, (
|
|
134
|
+
"Python gerenciado pelo sistema. Crie um venv: "
|
|
135
|
+
"python3 -m venv .venv && source .venv/bin/activate && pip install -U pip setuptools && "
|
|
136
|
+
"pip install raijin-server"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return False, (
|
|
140
|
+
"Execucao fora de venv detectada. Crie um venv: "
|
|
141
|
+
"python3 -m venv .venv && source .venv/bin/activate && pip install -U pip setuptools && "
|
|
142
|
+
"pip install raijin-server"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
114
146
|
def check_is_root() -> Tuple[bool, str]:
|
|
115
147
|
"""Verifica se esta executando como root."""
|
|
116
148
|
if os.geteuid() == 0:
|
|
117
149
|
return True, "Executando como root"
|
|
118
|
-
return False, "Usuario nao e root (
|
|
150
|
+
return False, "Usuario nao e root (reexecute com: sudo -E raijin-server ...)"
|
|
119
151
|
|
|
120
152
|
|
|
121
153
|
def validate_system_requirements(ctx: ExecutionContext, skip_root: bool = False) -> bool:
|
|
@@ -128,6 +160,7 @@ def validate_system_requirements(ctx: ExecutionContext, skip_root: bool = False)
|
|
|
128
160
|
typer.secho("\n=== Validacao de Pre-requisitos ===", fg=typer.colors.CYAN, bold=True)
|
|
129
161
|
|
|
130
162
|
checks = [
|
|
163
|
+
("Ambiente Python", check_virtualenv()),
|
|
131
164
|
("Sistema Operacional", check_os_version()),
|
|
132
165
|
("Espaco em Disco", check_disk_space()),
|
|
133
166
|
("Memoria RAM", check_memory()),
|
|
@@ -142,7 +175,8 @@ def validate_system_requirements(ctx: ExecutionContext, skip_root: bool = False)
|
|
|
142
175
|
if cmd_ok:
|
|
143
176
|
checks.append(("Comandos Essenciais", (True, "Todos os comandos disponiveis")))
|
|
144
177
|
else:
|
|
145
|
-
|
|
178
|
+
install_hint = "sudo apt-get update && sudo apt-get install -y " + " ".join(missing)
|
|
179
|
+
checks.append(("Comandos Essenciais", (False, f"Faltando: {', '.join(missing)} | Sugestao: {install_hint}")))
|
|
146
180
|
|
|
147
181
|
all_passed = True
|
|
148
182
|
for name, (passed, message) in checks:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: raijin-server
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -39,11 +39,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
39
39
|
|
|
40
40
|
## Destaques
|
|
41
41
|
|
|
42
|
-
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade
|
|
42
|
+
- ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
|
|
43
43
|
- ✅ **Health Checks Automáticos**: Valida serviços após instalação
|
|
44
|
-
- ✅ **Retry Inteligente**: Resistente a falhas temporárias de rede
|
|
45
|
-
- ✅ **Logging Estruturado**: Logs persistentes
|
|
44
|
+
- ✅ **Retry Inteligente com Backoff**: Resistente a falhas temporárias de rede (5 tentativas, backoff exponencial)
|
|
45
|
+
- ✅ **Logging Estruturado**: Logs persistentes com rotação (20MB, 5 backups)
|
|
46
46
|
- ✅ **Gestão de Dependências**: Garante ordem correta de execução
|
|
47
|
+
- ✅ **Verificação de Cluster**: Módulos que dependem de K8s verificam disponibilidade antes de executar
|
|
48
|
+
- ✅ **Clean Automático**: Opção de limpar instalação anterior ao re-executar kubernetes
|
|
49
|
+
- ✅ **IPv4 Only**: IPv6 desabilitado por padrão para simplificar rede
|
|
47
50
|
- ✅ **Configuração via Arquivo**: Automação completa com YAML/JSON
|
|
48
51
|
- ✅ **Idempotência**: Re-execução segura sem quebrar o sistema
|
|
49
52
|
- ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
|
|
@@ -180,7 +183,9 @@ se deseja pular. Se executar manualmente, basta responder "não" quando pergunta
|
|
|
180
183
|
|
|
181
184
|
### Comandos Úteis
|
|
182
185
|
```bash
|
|
183
|
-
# Versão
|
|
186
|
+
# Versão (flag ou comando)
|
|
187
|
+
raijin-server --version
|
|
188
|
+
raijin-server -V
|
|
184
189
|
raijin-server version
|
|
185
190
|
|
|
186
191
|
# Monitorar logs
|
|
@@ -222,9 +227,9 @@ tail -f /var/log/raijin-server/raijin-server.log
|
|
|
222
227
|
- `network`: Netplan com IP fixo e DNS. **OPCIONAL** se IP já configurado pelo provedor ISP.
|
|
223
228
|
- `firewall`: UFW com regras para SSH/HTTP/HTTPS/K8s.
|
|
224
229
|
- `vpn`: provisiona WireGuard (servidor + cliente inicial) e libera firewall.
|
|
225
|
-
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig.
|
|
226
|
-
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado.
|
|
227
|
-
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01).
|
|
230
|
+
- `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig. **Oferece limpeza automática** se detectar instalação anterior. IPv6 desabilitado por padrão.
|
|
231
|
+
- `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado. **Verifica cluster ativo** antes de aplicar.
|
|
232
|
+
- `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01). **Verifica cluster ativo** antes de instalar.
|
|
228
233
|
- `secrets`: instala sealed-secrets e external-secrets via Helm.
|
|
229
234
|
- `istio`: istioctl install (perfil raijin) e injeção automática.
|
|
230
235
|
- `traefik`: IngressController com TLS/ACME.
|
|
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.0 → raijin_server-0.2.2}/src/raijin_server/modules/observability_dashboards.py
RENAMED
|
File without changes
|
{raijin_server-0.2.0 → raijin_server-0.2.2}/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
|
|
File without changes
|
|
File without changes
|