raijin-server 0.1.0__py3-none-any.whl → 0.2.0__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 +1 -1
- raijin_server/cli.py +58 -4
- raijin_server/healthchecks.py +76 -0
- raijin_server/modules/__init__.py +11 -2
- raijin_server/modules/apokolips_demo.py +378 -0
- raijin_server/modules/bootstrap.py +65 -0
- raijin_server/modules/calico.py +93 -22
- raijin_server/modules/cert_manager.py +127 -0
- raijin_server/modules/full_install.py +54 -19
- raijin_server/modules/network.py +96 -2
- raijin_server/modules/observability_dashboards.py +233 -0
- raijin_server/modules/observability_ingress.py +218 -0
- raijin_server/modules/sanitize.py +142 -0
- raijin_server/modules/secrets.py +109 -0
- raijin_server/modules/ssh_hardening.py +128 -0
- raijin_server/modules/traefik.py +1 -1
- raijin_server/modules/vpn.py +68 -3
- raijin_server/scripts/__init__.py +1 -0
- raijin_server/scripts/checklist.sh +60 -0
- raijin_server/scripts/install.sh +134 -0
- raijin_server/scripts/log_size_metric.sh +31 -0
- raijin_server/scripts/pre-deploy-check.sh +183 -0
- raijin_server/utils.py +45 -12
- raijin_server/validators.py +5 -0
- raijin_server-0.2.0.dist-info/METADATA +407 -0
- raijin_server-0.2.0.dist-info/RECORD +44 -0
- raijin_server-0.1.0.dist-info/METADATA +0 -219
- raijin_server-0.1.0.dist-info/RECORD +0 -32
- {raijin_server-0.1.0.dist-info → raijin_server-0.2.0.dist-info}/WHEEL +0 -0
- {raijin_server-0.1.0.dist-info → raijin_server-0.2.0.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.1.0.dist-info → raijin_server-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.1.0.dist-info → raijin_server-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Hardening de SSH com usuario dedicado e chaves publicas."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import pwd
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from raijin_server.utils import ExecutionContext, apt_install, require_root, run_cmd, write_file
|
|
12
|
+
|
|
13
|
+
SSHD_DROPIN = Path("/etc/ssh/sshd_config.d/99-raijin.conf")
|
|
14
|
+
FAIL2BAN_JAIL = Path("/etc/fail2ban/jail.d/raijin-sshd.conf")
|
|
15
|
+
AUTHORIZED_KEYS_TEMPLATE = "# gerenciado pelo raijin-server\n{key}\n"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _user_exists(username: str) -> bool:
|
|
19
|
+
try:
|
|
20
|
+
pwd.getpwnam(username)
|
|
21
|
+
return True
|
|
22
|
+
except KeyError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _ensure_user(username: str, ctx: ExecutionContext) -> None:
|
|
27
|
+
if _user_exists(username):
|
|
28
|
+
typer.echo(f"Usuario {username} ja existe, reutilizando...")
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
typer.echo(f"Criando usuario {username} sem senha...")
|
|
32
|
+
run_cmd(["useradd", "-m", "-s", "/bin/bash", username], ctx)
|
|
33
|
+
run_cmd(["passwd", "-l", username], ctx, check=False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _write_authorized_keys(username: str, content: str, ctx: ExecutionContext) -> None:
|
|
37
|
+
ssh_dir = Path("/home") / username / ".ssh"
|
|
38
|
+
auth_file = ssh_dir / "authorized_keys"
|
|
39
|
+
|
|
40
|
+
if ctx.dry_run:
|
|
41
|
+
typer.echo(f"[dry-run] escrever {auth_file} com chave publica")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
ssh_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
os.chmod(ssh_dir, 0o700)
|
|
46
|
+
auth_file.write_text(AUTHORIZED_KEYS_TEMPLATE.format(key=content.strip()))
|
|
47
|
+
os.chmod(auth_file, 0o600)
|
|
48
|
+
run_cmd(["chown", "-R", f"{username}:{username}", str(ssh_dir)], ctx)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _load_public_key(path_input: str) -> str:
|
|
52
|
+
path = Path(path_input).expanduser()
|
|
53
|
+
if path.exists():
|
|
54
|
+
return path.read_text().strip()
|
|
55
|
+
typer.echo("Arquivo nao encontrado. Cole a chave publica completa (ssh-ed25519...).")
|
|
56
|
+
key = typer.prompt("Chave publica", default="")
|
|
57
|
+
if not key:
|
|
58
|
+
raise typer.BadParameter("Nenhuma chave publica fornecida.")
|
|
59
|
+
return key.strip()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run(ctx: ExecutionContext) -> None:
|
|
63
|
+
"""Configura SSH seguro com usuario dedicado e chaves publicas."""
|
|
64
|
+
require_root(ctx)
|
|
65
|
+
|
|
66
|
+
typer.echo("Hardening de SSH em andamento...")
|
|
67
|
+
apt_install(["openssh-server", "fail2ban"], ctx)
|
|
68
|
+
|
|
69
|
+
username = typer.prompt("Usuario administrativo para SSH", default="adminops")
|
|
70
|
+
ssh_port = typer.prompt("Porta SSH", default="22")
|
|
71
|
+
sudo_access = typer.confirm("Adicionar usuario ao grupo sudo?", default=True)
|
|
72
|
+
pubkey_path = typer.prompt(
|
|
73
|
+
"Arquivo com chave publica (ENTER para ~/.ssh/id_ed25519.pub)",
|
|
74
|
+
default=str(Path.home() / ".ssh/id_ed25519.pub"),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
public_key = _load_public_key(pubkey_path)
|
|
78
|
+
|
|
79
|
+
_ensure_user(username, ctx)
|
|
80
|
+
if sudo_access:
|
|
81
|
+
run_cmd(["usermod", "-aG", "sudo", username], ctx)
|
|
82
|
+
|
|
83
|
+
_write_authorized_keys(username, public_key, ctx)
|
|
84
|
+
|
|
85
|
+
config = f"""
|
|
86
|
+
# Arquivo gerenciado pelo raijin-server
|
|
87
|
+
Port {ssh_port}
|
|
88
|
+
Protocol 2
|
|
89
|
+
PermitRootLogin no
|
|
90
|
+
PasswordAuthentication no
|
|
91
|
+
PermitEmptyPasswords no
|
|
92
|
+
ChallengeResponseAuthentication no
|
|
93
|
+
UsePAM yes
|
|
94
|
+
AllowUsers {username}
|
|
95
|
+
AuthenticationMethods publickey
|
|
96
|
+
X11Forwarding no
|
|
97
|
+
ClientAliveInterval 300
|
|
98
|
+
ClientAliveCountMax 2
|
|
99
|
+
MaxAuthTries 3
|
|
100
|
+
""".strip() + "\n"
|
|
101
|
+
|
|
102
|
+
write_file(SSHD_DROPIN, config, ctx)
|
|
103
|
+
|
|
104
|
+
fail2ban_jail = f"""
|
|
105
|
+
[sshd-raijin]
|
|
106
|
+
enabled = true
|
|
107
|
+
port = {ssh_port}
|
|
108
|
+
filter = sshd
|
|
109
|
+
logpath = /var/log/auth.log
|
|
110
|
+
maxretry = 5
|
|
111
|
+
findtime = 600
|
|
112
|
+
bantime = 3600
|
|
113
|
+
""".strip() + "\n"
|
|
114
|
+
write_file(FAIL2BAN_JAIL, fail2ban_jail, ctx)
|
|
115
|
+
|
|
116
|
+
run_cmd(["sshd", "-t"], ctx)
|
|
117
|
+
run_cmd(["systemctl", "enable", "ssh"], ctx)
|
|
118
|
+
run_cmd(["systemctl", "restart", "ssh"], ctx)
|
|
119
|
+
run_cmd(["systemctl", "restart", "fail2ban"], ctx, check=False)
|
|
120
|
+
|
|
121
|
+
if ssh_port != "22":
|
|
122
|
+
run_cmd(["ufw", "allow", ssh_port], ctx, check=False)
|
|
123
|
+
run_cmd(["ufw", "delete", "allow", "22"], ctx, check=False)
|
|
124
|
+
|
|
125
|
+
typer.secho("\n✓ SSH hardening concluido com sucesso!", fg=typer.colors.GREEN, bold=True)
|
|
126
|
+
typer.echo(f"Usuario permitido: {username}")
|
|
127
|
+
typer.echo(f"Porta configurada: {ssh_port}")
|
|
128
|
+
typer.echo("Certifique-se de testar a nova sessao antes de encerrar conexoes atuais.")
|
raijin_server/modules/traefik.py
CHANGED
|
@@ -22,7 +22,7 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
22
22
|
"certificatesResolvers.letsencrypt.acme.storage=/data/acme.json",
|
|
23
23
|
"certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web",
|
|
24
24
|
"logs.general.level=INFO",
|
|
25
|
-
|
|
25
|
+
"providers.kubernetesIngress.ingressClass=traefik",
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
if dashboard_host:
|
raijin_server/modules/vpn.py
CHANGED
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import ipaddress
|
|
6
6
|
import os
|
|
7
|
+
import platform
|
|
7
8
|
import subprocess
|
|
8
9
|
import textwrap
|
|
9
10
|
from pathlib import Path
|
|
@@ -24,6 +25,37 @@ CLIENTS_DIR = WIREGUARD_DIR / "clients"
|
|
|
24
25
|
SYSCTL_FILE = Path("/etc/sysctl.d/99-wireguard.conf")
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
def _is_secure_boot_enabled() -> bool:
|
|
29
|
+
"""Retorna True se Secure Boot estiver ativo (EFI)."""
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
sb_path = Path("/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c")
|
|
33
|
+
if not sb_path.exists():
|
|
34
|
+
return False
|
|
35
|
+
data = sb_path.read_bytes()
|
|
36
|
+
# Estrutura: atributos (4 bytes) + valor (1 byte)
|
|
37
|
+
return len(data) >= 5 and data[4] == 1
|
|
38
|
+
except Exception:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _kernel_headers_present() -> bool:
|
|
43
|
+
"""Verifica se os headers do kernel atual estao instalados."""
|
|
44
|
+
|
|
45
|
+
release = platform.uname().release
|
|
46
|
+
return Path(f"/lib/modules/{release}/build").exists()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _modprobe_check(module: str) -> bool:
|
|
50
|
+
"""Tenta carregar o modulo em modo dry-run para validar disponibilidade."""
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
result = subprocess.run(["modprobe", "-n", module], capture_output=True, text=True, timeout=5)
|
|
54
|
+
return result.returncode == 0
|
|
55
|
+
except Exception:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
|
|
27
59
|
def _generate_keypair(
|
|
28
60
|
label: str,
|
|
29
61
|
private_path: Path,
|
|
@@ -69,6 +101,40 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
69
101
|
require_root(ctx)
|
|
70
102
|
typer.echo("Configurando WireGuard (VPN site-to-site)...")
|
|
71
103
|
|
|
104
|
+
if _is_secure_boot_enabled():
|
|
105
|
+
typer.secho(
|
|
106
|
+
"Secure Boot detectado: modulos DKMS (wireguard) podem exigir assinatura.\n"
|
|
107
|
+
"Se o modulo nao carregar, assine-o ou desabilite Secure Boot temporariamente.",
|
|
108
|
+
fg=typer.colors.YELLOW,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# NIC offload pode interferir em VPN/perf em alguns hardwares; aviso leve
|
|
112
|
+
typer.secho(
|
|
113
|
+
"Considere desabilitar offloads problemáticos em NICs (tso/gso/gro) se notar latência ou perda.",
|
|
114
|
+
fg=typer.colors.YELLOW,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if not _kernel_headers_present():
|
|
118
|
+
typer.secho(
|
|
119
|
+
"Headers do kernel nao encontrados; instalando linux-headers-$(uname -r) para suportar WireGuard.",
|
|
120
|
+
fg=typer.colors.YELLOW,
|
|
121
|
+
)
|
|
122
|
+
release = platform.uname().release
|
|
123
|
+
header_pkg = f"linux-headers-{release}"
|
|
124
|
+
apt_install([header_pkg], ctx)
|
|
125
|
+
if not _kernel_headers_present():
|
|
126
|
+
typer.secho(
|
|
127
|
+
f"Headers ainda ausentes apos tentar instalar {header_pkg}. Verifique repos ou kernel custom.",
|
|
128
|
+
fg=typer.colors.RED,
|
|
129
|
+
)
|
|
130
|
+
raise typer.Exit(code=1)
|
|
131
|
+
|
|
132
|
+
if not _modprobe_check("wireguard"):
|
|
133
|
+
typer.secho(
|
|
134
|
+
"Aviso: modulo wireguard pode nao estar disponivel (modprobe -n wireguard falhou).",
|
|
135
|
+
fg=typer.colors.YELLOW,
|
|
136
|
+
)
|
|
137
|
+
|
|
72
138
|
apt_install(["wireguard", "wireguard-tools", "qrencode"], ctx)
|
|
73
139
|
|
|
74
140
|
interface = typer.prompt("Interface WireGuard", default="wg0")
|
|
@@ -146,7 +212,6 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
146
212
|
typer.secho("\n✓ WireGuard configurado com sucesso!", fg=typer.colors.GREEN, bold=True)
|
|
147
213
|
typer.echo(f"Configuracao do servidor: {server_conf_path}")
|
|
148
214
|
typer.echo(f"Cliente inicial salvo em: {client_conf_path}")
|
|
149
|
-
typer.echo("Para gerar QR code no terminal: qrencode -t ansiutf8 <
|
|
215
|
+
typer.echo("Para gerar QR code no terminal: qrencode -t ansiutf8 < caminho-do-cliente.conf")
|
|
150
216
|
typer.echo("Para novos clientes, gere chaves com 'wg genkey' e adicione entradas em ambos os arquivos.")
|
|
151
|
-
typer.echo("Clientes Linux/macOS: sudo wg-quick up ./cliente.conf | Windows: importe o arquivo no app WireGuard.")
|
|
152
|
-
```}```} CPU? Need to ensure string quotes? there is newline with braces? we used f string in echo referencing braces? we inserted literal braces within string? `typer.echo(
|
|
217
|
+
typer.echo("Clientes Linux/macOS: sudo wg-quick up ./cliente.conf | Windows: importe o arquivo no app WireGuard.")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shell helpers empacotados com o raijin-server."""
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Checklist/smoke tests basicos para um nodo provisionado pelo raijin-server.
|
|
5
|
+
# Execute como root (ou sudo) em um nodo control-plane ou worker.
|
|
6
|
+
|
|
7
|
+
log() { printf "[%s] %s\n" "$(date +%H:%M:%S)" "$*"; }
|
|
8
|
+
|
|
9
|
+
require_cmd() {
|
|
10
|
+
command -v "$1" >/dev/null 2>&1 || { echo "Falta comando: $1"; exit 1; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
log "Verificando comandos basicos"
|
|
14
|
+
for c in kubectl helm ufw curl jq; do require_cmd "$c"; done
|
|
15
|
+
|
|
16
|
+
log "Verificando conectividade basica"
|
|
17
|
+
ping -c1 1.1.1.1 >/dev/null && log "Ping OK para 1.1.1.1" || echo "Ping falhou"
|
|
18
|
+
|
|
19
|
+
log "Status de data/hora"
|
|
20
|
+
timedatectl status | head -n 5
|
|
21
|
+
|
|
22
|
+
log "Status fail2ban"
|
|
23
|
+
systemctl is-active --quiet fail2ban && echo "fail2ban ativo" || echo "fail2ban inativo"
|
|
24
|
+
|
|
25
|
+
log "Status firewall (ufw)"
|
|
26
|
+
ufw status numbered || true
|
|
27
|
+
|
|
28
|
+
log "Sysctl rede (hardening)"
|
|
29
|
+
sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.default.rp_filter net.ipv4.conf.all.accept_redirects net.ipv4.conf.default.accept_redirects net.ipv4.conf.all.send_redirects net.ipv4.ip_forward
|
|
30
|
+
|
|
31
|
+
log "Kubernetes: nodes"
|
|
32
|
+
kubectl get nodes -o wide || true
|
|
33
|
+
|
|
34
|
+
log "Kubernetes: pods principais"
|
|
35
|
+
kubectl get pods -A | head -n 40 || true
|
|
36
|
+
|
|
37
|
+
log "Calico: pods"
|
|
38
|
+
kubectl get pods -n kube-system -l k8s-app=calico-node -o wide || true
|
|
39
|
+
|
|
40
|
+
log "Ingress (Traefik): serviços"
|
|
41
|
+
kubectl get svc -n traefik || true
|
|
42
|
+
|
|
43
|
+
log "Prometheus/Grafana/Loki: pods"
|
|
44
|
+
kubectl get pods -n observability || true
|
|
45
|
+
|
|
46
|
+
log "Velero: backups"
|
|
47
|
+
velero backup get || true
|
|
48
|
+
|
|
49
|
+
log "MinIO: serviço"
|
|
50
|
+
kubectl get svc -n minio || true
|
|
51
|
+
|
|
52
|
+
log "Kafka: serviços"
|
|
53
|
+
kubectl get svc -n kafka || true
|
|
54
|
+
|
|
55
|
+
log "Teste HTTP interno (se ingress exposto)"
|
|
56
|
+
if kubectl get svc -n traefik traefik >/dev/null 2>&1; then
|
|
57
|
+
kubectl run curltest --rm -i --restart=Never --image=curlimages/curl -- curl -s -o /dev/null -w "%{http_code}\n" http://traefik.traefik.svc.cluster.local || true
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
log "Checklist finalizado"
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Script de instalacao rapida do Raijin Server CLI
|
|
3
|
+
# Para Ubuntu Server 20.04+
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
RED='\033[0;31m'
|
|
8
|
+
GREEN='\033[0;32m'
|
|
9
|
+
YELLOW='\033[1;33m'
|
|
10
|
+
CYAN='\033[0;36m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
echo -e "${CYAN}=========================================${NC}"
|
|
14
|
+
echo -e "${CYAN} Raijin Server - Instalação Rápida${NC}"
|
|
15
|
+
echo -e "${CYAN}=========================================${NC}"
|
|
16
|
+
echo ""
|
|
17
|
+
|
|
18
|
+
# Verificar Python
|
|
19
|
+
if ! command -v python3 &> /dev/null; then
|
|
20
|
+
echo -e "${RED}✗ Python3 não encontrado${NC}"
|
|
21
|
+
echo "Instale com: sudo apt install python3 python3-pip python3-venv"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
PY_VERSION=$(python3 --version | awk '{print $2}')
|
|
26
|
+
echo -e "${GREEN}✓${NC} Python $PY_VERSION encontrado"
|
|
27
|
+
|
|
28
|
+
# Verificar se está no diretório do projeto
|
|
29
|
+
if [ ! -f "setup.cfg" ]; then
|
|
30
|
+
echo -e "${RED}✗ Não está no diretório do projeto raijin-server${NC}"
|
|
31
|
+
echo "Execute este script no diretório raiz do projeto"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Perguntar tipo de instalação
|
|
36
|
+
echo ""
|
|
37
|
+
echo "Escolha o tipo de instalação:"
|
|
38
|
+
echo " 1) Global (requer sudo, todos os usuários)"
|
|
39
|
+
echo " 2) Virtual env (recomendado para desenvolvimento)"
|
|
40
|
+
echo " 3) User install (apenas usuário atual)"
|
|
41
|
+
read -p "Opção [2]: " INSTALL_TYPE
|
|
42
|
+
INSTALL_TYPE=${INSTALL_TYPE:-2}
|
|
43
|
+
|
|
44
|
+
echo ""
|
|
45
|
+
case $INSTALL_TYPE in
|
|
46
|
+
1)
|
|
47
|
+
echo -e "${YELLOW}Instalando globalmente...${NC}"
|
|
48
|
+
sudo python3 -m pip install .
|
|
49
|
+
BIN_PATH=$(which raijin-server)
|
|
50
|
+
;;
|
|
51
|
+
2)
|
|
52
|
+
echo -e "${YELLOW}Criando virtual environment...${NC}"
|
|
53
|
+
python3 -m venv .venv
|
|
54
|
+
source .venv/bin/activate
|
|
55
|
+
pip install --upgrade pip
|
|
56
|
+
pip install -e .
|
|
57
|
+
BIN_PATH=".venv/bin/raijin-server"
|
|
58
|
+
|
|
59
|
+
# Criar wrapper
|
|
60
|
+
cat > raijin-server-run.sh << 'EOF'
|
|
61
|
+
#!/bin/bash
|
|
62
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
63
|
+
source "$SCRIPT_DIR/.venv/bin/activate"
|
|
64
|
+
raijin-server "$@"
|
|
65
|
+
EOF
|
|
66
|
+
chmod +x raijin-server-run.sh
|
|
67
|
+
echo -e "${GREEN}✓${NC} Wrapper criado: ./raijin-server-run.sh"
|
|
68
|
+
;;
|
|
69
|
+
3)
|
|
70
|
+
echo -e "${YELLOW}Instalando para usuário atual...${NC}"
|
|
71
|
+
python3 -m pip install --user .
|
|
72
|
+
BIN_PATH="$HOME/.local/bin/raijin-server"
|
|
73
|
+
|
|
74
|
+
# Adicionar ao PATH se necessário
|
|
75
|
+
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
76
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
|
77
|
+
echo -e "${YELLOW}⚠${NC} Adicionado $HOME/.local/bin ao PATH"
|
|
78
|
+
echo "Execute: source ~/.bashrc"
|
|
79
|
+
fi
|
|
80
|
+
;;
|
|
81
|
+
*)
|
|
82
|
+
echo -e "${RED}✗ Opção inválida${NC}"
|
|
83
|
+
exit 1
|
|
84
|
+
;;
|
|
85
|
+
esac
|
|
86
|
+
|
|
87
|
+
echo ""
|
|
88
|
+
echo -e "${GREEN}✓ Instalação concluída!${NC}"
|
|
89
|
+
echo ""
|
|
90
|
+
|
|
91
|
+
# Testar instalação
|
|
92
|
+
echo "Testando instalação..."
|
|
93
|
+
if [ "$INSTALL_TYPE" -eq 2 ]; then
|
|
94
|
+
VERSION=$($BIN_PATH version)
|
|
95
|
+
else
|
|
96
|
+
VERSION=$(raijin-server version)
|
|
97
|
+
fi
|
|
98
|
+
echo -e "${GREEN}✓${NC} $VERSION"
|
|
99
|
+
|
|
100
|
+
# Mostrar próximos passos
|
|
101
|
+
echo ""
|
|
102
|
+
echo -e "${CYAN}=========================================${NC}"
|
|
103
|
+
echo -e "${CYAN} Próximos Passos${NC}"
|
|
104
|
+
echo -e "${CYAN}=========================================${NC}"
|
|
105
|
+
echo ""
|
|
106
|
+
|
|
107
|
+
case $INSTALL_TYPE in
|
|
108
|
+
2)
|
|
109
|
+
echo "Para usar o CLI:"
|
|
110
|
+
echo " source .venv/bin/activate"
|
|
111
|
+
echo " sudo raijin-server validate"
|
|
112
|
+
echo ""
|
|
113
|
+
echo "Ou use o wrapper:"
|
|
114
|
+
echo " sudo ./raijin-server-run.sh validate"
|
|
115
|
+
;;
|
|
116
|
+
*)
|
|
117
|
+
echo "Para validar o sistema:"
|
|
118
|
+
echo " sudo raijin-server validate"
|
|
119
|
+
echo ""
|
|
120
|
+
echo "Para ver o menu interativo:"
|
|
121
|
+
echo " sudo raijin-server"
|
|
122
|
+
echo ""
|
|
123
|
+
echo "Para gerar configuração:"
|
|
124
|
+
echo " raijin-server generate-config -o production.yaml"
|
|
125
|
+
;;
|
|
126
|
+
esac
|
|
127
|
+
|
|
128
|
+
echo ""
|
|
129
|
+
echo "Documentação:"
|
|
130
|
+
echo " README.md - Guia principal"
|
|
131
|
+
echo " AUDIT.md - Relatório de auditoria"
|
|
132
|
+
echo " EXAMPLES.md - Exemplos práticos"
|
|
133
|
+
echo ""
|
|
134
|
+
echo -e "${GREEN}Instalação bem-sucedida!${NC} 🚀"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Gera metricas em formato Prometheus para tamanho dos logs do raijin-server.
|
|
3
|
+
# Pode ser usado com node_exporter textfile collector.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
LOG_DIR=${RAIJIN_LOG_DIR:-/var/log/raijin-server}
|
|
8
|
+
LOG_PATTERN=${RAIJIN_LOG_PATTERN:-raijin-server.log*}
|
|
9
|
+
OUTPUT=${RAIJIN_METRIC_FILE:-/var/lib/node_exporter/textfile_collector/raijin_log_size.prom}
|
|
10
|
+
|
|
11
|
+
# Calcula soma de todos os logs (principal + rotações)
|
|
12
|
+
TOTAL_BYTES=0
|
|
13
|
+
shopt -s nullglob
|
|
14
|
+
for f in "$LOG_DIR"/$LOG_PATTERN; do
|
|
15
|
+
size=$(stat -c%s "$f" 2>/dev/null || echo 0)
|
|
16
|
+
TOTAL_BYTES=$((TOTAL_BYTES + size))
|
|
17
|
+
if [[ "$f" =~ raijin-server\.log(\.\d+)?$ ]]; then
|
|
18
|
+
printf "raijin_log_size_bytes{file=\"%s\"} %d\n" "$(basename "$f")" "$size"
|
|
19
|
+
fi
|
|
20
|
+
done | {
|
|
21
|
+
# Escreve métricas no arquivo final
|
|
22
|
+
mkdir -p "$(dirname "$OUTPUT")"
|
|
23
|
+
{
|
|
24
|
+
echo "# HELP raijin_log_size_bytes Tamanho dos logs do raijin-server (bytes)"
|
|
25
|
+
echo "# TYPE raijin_log_size_bytes gauge"
|
|
26
|
+
cat
|
|
27
|
+
echo "# HELP raijin_log_size_total_bytes Soma dos logs do raijin-server (bytes)"
|
|
28
|
+
echo "# TYPE raijin_log_size_total_bytes gauge"
|
|
29
|
+
echo "raijin_log_size_total_bytes ${TOTAL_BYTES}"
|
|
30
|
+
} > "$OUTPUT"
|
|
31
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Checklist de validacao pre-deploy Raijin Server
|
|
3
|
+
# Versao auditada com verificacoes completas
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
RED='\033[0;31m'
|
|
8
|
+
GREEN='\033[0;32m'
|
|
9
|
+
YELLOW='\033[1;33m'
|
|
10
|
+
CYAN='\033[0;36m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
echo -e "${CYAN}==========================================${NC}"
|
|
14
|
+
echo -e "${CYAN} Raijin Server - Checklist Pre-Deploy${NC}"
|
|
15
|
+
echo -e "${CYAN}==========================================${NC}"
|
|
16
|
+
echo ""
|
|
17
|
+
|
|
18
|
+
# Funcao auxiliar para checks
|
|
19
|
+
check_pass() {
|
|
20
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
check_fail() {
|
|
24
|
+
echo -e "${RED}✗${NC} $1"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
check_warn() {
|
|
28
|
+
echo -e "${YELLOW}⚠${NC} $1"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
FAILED=0
|
|
32
|
+
|
|
33
|
+
# 1. Verificar Python
|
|
34
|
+
echo "1. Verificando Python..."
|
|
35
|
+
if command -v python3 &> /dev/null; then
|
|
36
|
+
PY_VERSION=$(python3 --version | awk '{print $2}')
|
|
37
|
+
if [[ $(echo "$PY_VERSION 3.9" | awk '{print ($1 >= $2)}') -eq 1 ]]; then
|
|
38
|
+
check_pass "Python $PY_VERSION >= 3.9"
|
|
39
|
+
else
|
|
40
|
+
check_fail "Python $PY_VERSION < 3.9"
|
|
41
|
+
FAILED=1
|
|
42
|
+
fi
|
|
43
|
+
else
|
|
44
|
+
check_fail "Python3 nao encontrado"
|
|
45
|
+
FAILED=1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# 2. Verificar OS
|
|
49
|
+
echo ""
|
|
50
|
+
echo "2. Verificando Sistema Operacional..."
|
|
51
|
+
if [ -f /etc/os-release ]; then
|
|
52
|
+
. /etc/os-release
|
|
53
|
+
if [[ "$ID" == "ubuntu" ]]; then
|
|
54
|
+
VERSION_NUM=$(echo "$VERSION_ID" | cut -d. -f1)
|
|
55
|
+
if [[ "$VERSION_NUM" -ge 20 ]]; then
|
|
56
|
+
check_pass "Ubuntu $VERSION_ID"
|
|
57
|
+
else
|
|
58
|
+
check_warn "Ubuntu $VERSION_ID (recomendado >= 20.04)"
|
|
59
|
+
fi
|
|
60
|
+
else
|
|
61
|
+
check_warn "Sistema nao e Ubuntu: $ID"
|
|
62
|
+
fi
|
|
63
|
+
else
|
|
64
|
+
check_fail "/etc/os-release nao encontrado"
|
|
65
|
+
FAILED=1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# 3. Verificar root/sudo
|
|
69
|
+
echo ""
|
|
70
|
+
echo "3. Verificando permissoes..."
|
|
71
|
+
if [[ $EUID -eq 0 ]]; then
|
|
72
|
+
check_pass "Executando como root"
|
|
73
|
+
elif groups | grep -q sudo; then
|
|
74
|
+
check_pass "Usuario no grupo sudo"
|
|
75
|
+
else
|
|
76
|
+
check_fail "Usuario nao tem permissoes sudo"
|
|
77
|
+
FAILED=1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# 4. Verificar espaco em disco
|
|
81
|
+
echo ""
|
|
82
|
+
echo "4. Verificando espaco em disco..."
|
|
83
|
+
DISK_AVAIL=$(df / | tail -1 | awk '{print $4}')
|
|
84
|
+
DISK_AVAIL_GB=$((DISK_AVAIL / 1024 / 1024))
|
|
85
|
+
if [[ $DISK_AVAIL_GB -ge 20 ]]; then
|
|
86
|
+
check_pass "Espaco disponivel: ${DISK_AVAIL_GB}GB"
|
|
87
|
+
else
|
|
88
|
+
check_fail "Espaco insuficiente: ${DISK_AVAIL_GB}GB (minimo: 20GB)"
|
|
89
|
+
FAILED=1
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# 5. Verificar memoria
|
|
93
|
+
echo ""
|
|
94
|
+
echo "5. Verificando memoria RAM..."
|
|
95
|
+
MEM_TOTAL=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
96
|
+
MEM_TOTAL_GB=$((MEM_TOTAL / 1024 / 1024))
|
|
97
|
+
if [[ $MEM_TOTAL_GB -ge 4 ]]; then
|
|
98
|
+
check_pass "Memoria RAM: ${MEM_TOTAL_GB}GB"
|
|
99
|
+
else
|
|
100
|
+
check_fail "Memoria insuficiente: ${MEM_TOTAL_GB}GB (minimo: 4GB)"
|
|
101
|
+
FAILED=1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# 6. Verificar conectividade
|
|
105
|
+
echo ""
|
|
106
|
+
echo "6. Verificando conectividade..."
|
|
107
|
+
if ping -c 1 8.8.8.8 &> /dev/null; then
|
|
108
|
+
check_pass "Conectividade com internet OK"
|
|
109
|
+
else
|
|
110
|
+
check_fail "Sem conectividade com internet"
|
|
111
|
+
FAILED=1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# 7. Verificar comandos essenciais
|
|
115
|
+
echo ""
|
|
116
|
+
echo "7. Verificando comandos essenciais..."
|
|
117
|
+
COMMANDS=("curl" "wget" "apt-get" "systemctl" "gpg")
|
|
118
|
+
for cmd in "${COMMANDS[@]}"; do
|
|
119
|
+
if command -v "$cmd" &> /dev/null; then
|
|
120
|
+
check_pass "$cmd instalado"
|
|
121
|
+
else
|
|
122
|
+
check_fail "$cmd nao encontrado"
|
|
123
|
+
FAILED=1
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
|
|
127
|
+
# 8. Verificar instalacao raijin-server
|
|
128
|
+
echo ""
|
|
129
|
+
echo "8. Verificando instalacao raijin-server..."
|
|
130
|
+
if command -v raijin-server &> /dev/null; then
|
|
131
|
+
RAIJIN_VERSION=$(raijin-server version)
|
|
132
|
+
check_pass "$RAIJIN_VERSION instalado"
|
|
133
|
+
else
|
|
134
|
+
check_warn "raijin-server nao instalado (instale com: pip install .)"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# 9. Verificar logs
|
|
138
|
+
echo ""
|
|
139
|
+
echo "9. Verificando diretorio de logs..."
|
|
140
|
+
if [[ -d /var/log/raijin-server ]]; then
|
|
141
|
+
check_pass "Diretorio de logs: /var/log/raijin-server"
|
|
142
|
+
elif [[ -f ~/.raijin-server.log ]]; then
|
|
143
|
+
check_warn "Usando fallback: ~/.raijin-server.log"
|
|
144
|
+
else
|
|
145
|
+
check_warn "Nenhum log encontrado ainda"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# 10. Verificar estado
|
|
149
|
+
echo ""
|
|
150
|
+
echo "10. Verificando estado dos modulos..."
|
|
151
|
+
STATE_DIRS=("/var/lib/raijin-server/state" "$HOME/.local/share/raijin-server/state")
|
|
152
|
+
FOUND_STATE=0
|
|
153
|
+
for dir in "${STATE_DIRS[@]}"; do
|
|
154
|
+
if [[ -d "$dir" ]]; then
|
|
155
|
+
MODULE_COUNT=$(ls -1 "$dir"/*.done 2>/dev/null | wc -l)
|
|
156
|
+
if [[ $MODULE_COUNT -gt 0 ]]; then
|
|
157
|
+
check_pass "$MODULE_COUNT modulos concluidos (em $dir)"
|
|
158
|
+
FOUND_STATE=1
|
|
159
|
+
break
|
|
160
|
+
fi
|
|
161
|
+
fi
|
|
162
|
+
done
|
|
163
|
+
if [[ $FOUND_STATE -eq 0 ]]; then
|
|
164
|
+
check_warn "Nenhum modulo executado ainda"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Resumo
|
|
168
|
+
echo ""
|
|
169
|
+
echo -e "${CYAN}==========================================${NC}"
|
|
170
|
+
if [[ $FAILED -eq 0 ]]; then
|
|
171
|
+
echo -e "${GREEN}✓ Sistema pronto para executar raijin-server${NC}"
|
|
172
|
+
echo ""
|
|
173
|
+
echo "Proximos passos:"
|
|
174
|
+
echo " 1. sudo raijin-server validate"
|
|
175
|
+
echo " 2. sudo raijin-server"
|
|
176
|
+
echo " 3. Ou: raijin-server generate-config -o production.yaml"
|
|
177
|
+
exit 0
|
|
178
|
+
else
|
|
179
|
+
echo -e "${RED}✗ Sistema NAO atende pre-requisitos${NC}"
|
|
180
|
+
echo ""
|
|
181
|
+
echo "Corrija os problemas acima antes de prosseguir."
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|