raijin-server 0.2.8__tar.gz → 0.2.11__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.8/src/raijin_server.egg-info → raijin_server-0.2.11}/PKG-INFO +60 -4
- {raijin_server-0.2.8 → raijin_server-0.2.11}/README.md +59 -3
- {raijin_server-0.2.8 → raijin_server-0.2.11}/setup.cfg +1 -1
- raijin_server-0.2.11/src/raijin_server/__init__.py +5 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/cli.py +108 -3
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/kubernetes.py +93 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/traefik.py +0 -2
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/validators.py +23 -22
- {raijin_server-0.2.8 → raijin_server-0.2.11/src/raijin_server.egg-info}/PKG-INFO +60 -4
- raijin_server-0.2.8/src/raijin_server/__init__.py +0 -5
- {raijin_server-0.2.8 → raijin_server-0.2.11}/LICENSE +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/pyproject.toml +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/config.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/healthchecks.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/__init__.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/apokolips_demo.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/bootstrap.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/calico.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/cert_manager.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/essentials.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/firewall.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/full_install.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/grafana.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/hardening.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/harness.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/istio.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/kafka.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/kong.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/loki.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/minio.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/network.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/observability_dashboards.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/observability_ingress.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/prometheus.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/sanitize.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/secrets.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/ssh_hardening.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/velero.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/vpn.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/scripts/__init__.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/scripts/checklist.sh +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/scripts/install.sh +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/scripts/log_size_metric.sh +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/scripts/pre-deploy-check.sh +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/utils.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/SOURCES.txt +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/dependency_links.txt +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/entry_points.txt +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/requires.txt +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/top_level.txt +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/tests/test_full_install_sequence.py +0 -0
- {raijin_server-0.2.8 → raijin_server-0.2.11}/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.11
|
|
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
|
-
|
|
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
|
-
|
|
503
|
+
python3 -m twine upload dist/*
|
|
448
504
|
|
|
449
505
|
# 4) Verifique instalação
|
|
450
|
-
|
|
506
|
+
python3 -m pip install -U raijin-server
|
|
451
507
|
raijin-server --version
|
|
452
508
|
```
|
|
453
509
|
|
|
@@ -11,6 +11,8 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
11
11
|
- Arquitetura: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
12
12
|
- Auditoria: [AUDIT.md](AUDIT.md)
|
|
13
13
|
- Segurança: [SECURITY.md](SECURITY.md)
|
|
14
|
+
- Acesso SSH (Windows): [docs/SSH_WINDOWS.md](docs/SSH_WINDOWS.md)
|
|
15
|
+
- VPN para acesso remoto (WireGuard): [docs/VPN_REMOTE_ACCESS.md](docs/VPN_REMOTE_ACCESS.md)
|
|
14
16
|
|
|
15
17
|
## Destaques
|
|
16
18
|
|
|
@@ -27,6 +29,12 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
|
|
|
27
29
|
- ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
|
|
28
30
|
|
|
29
31
|
## Requisitos
|
|
32
|
+
Ubuntu Server 20.04+ com Python 3 disponível. Se precisar instalar/atualizar no host alvo:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
sudo apt update
|
|
36
|
+
sudo apt install -y python3 python3-venv python3-pip
|
|
37
|
+
```
|
|
30
38
|
|
|
31
39
|
## Instalação (sempre em venv midgard)
|
|
32
40
|
|
|
@@ -77,6 +85,10 @@ sudo -E ~/.venvs/midgard/bin/raijin-server validate
|
|
|
77
85
|
sudo -E ~/.venvs/midgard/bin/raijin-server menu
|
|
78
86
|
```
|
|
79
87
|
|
|
88
|
+
### Rollback de módulos
|
|
89
|
+
- 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.
|
|
90
|
+
- 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.
|
|
91
|
+
|
|
80
92
|
### Execução Direta de Módulos
|
|
81
93
|
```bash
|
|
82
94
|
# Executar módulo específico
|
|
@@ -140,6 +152,9 @@ sudo -E ~/.venvs/midgard/bin/raijin-server debug journal --service containerd --
|
|
|
140
152
|
- **[AUDIT.md](AUDIT.md)**: Relatório completo de auditoria e melhorias implementadas
|
|
141
153
|
- **[ARCHITECTURE.md](ARCHITECTURE.md)**: Arquitetura técnica do ambiente
|
|
142
154
|
- **[SECURITY.md](SECURITY.md)**: Políticas de segurança e reporte de vulnerabilidades
|
|
155
|
+
- **Publicação PyPI**: ver seção "Publicar no PyPI" abaixo
|
|
156
|
+
- **CNI automático**: Calico aplicado automaticamente no passo Kubernetes (override com `RAIJIN_CNI=none`)
|
|
157
|
+
- Para reaplicar CNI (forçar mesmo se já houver): `RAIJIN_FORCE_CNI=1`
|
|
143
158
|
|
|
144
159
|
## Fluxo de Execução Recomendado
|
|
145
160
|
|
|
@@ -296,6 +311,46 @@ bash "$SCRIPT_PATH"
|
|
|
296
311
|
|
|
297
312
|
O helper garante o caminho absoluto correto independentemente de onde o pacote foi instalado.
|
|
298
313
|
|
|
314
|
+
## Publicar no PyPI
|
|
315
|
+
|
|
316
|
+
Use o venv local do repositório (`.venv`) para garantir dependências corretas:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
cd /home/rafael/github/raijin-server
|
|
320
|
+
python3 -m venv .venv
|
|
321
|
+
source .venv/bin/activate
|
|
322
|
+
python -m pip install -U pip build twine
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Gerar artefatos limpos:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
rm -rf dist build
|
|
329
|
+
python -m build --sdist --wheel --outdir dist
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Publicar no PyPI (requere token):
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
export TWINE_USERNAME="__token__"
|
|
336
|
+
export TWINE_PASSWORD="pypi-xxxxx" # token do PyPI
|
|
337
|
+
python -m twine upload dist/*
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Opcional: validar no TestPyPI antes de publicar (precisa token de TestPyPI):
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
export TWINE_USERNAME="__token__"
|
|
344
|
+
export TWINE_PASSWORD="pypi-xxxxx" # token do TestPyPI
|
|
345
|
+
python -m twine upload --repository testpypi dist/*
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Depois de publicar, atualize/instale:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
pip install -U raijin-server
|
|
352
|
+
```
|
|
353
|
+
|
|
299
354
|
## Teste de ingress (Apokolips)
|
|
300
355
|
|
|
301
356
|
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.
|
|
@@ -404,17 +459,18 @@ O Twine é a ferramenta oficial para enviar pacotes Python ao PyPI com upload se
|
|
|
404
459
|
Passo a passo:
|
|
405
460
|
```bash
|
|
406
461
|
# 1) Gere artefatos
|
|
407
|
-
|
|
462
|
+
python3 -m pip install --user build
|
|
463
|
+
python3 -m build --sdist --wheel --outdir dist/
|
|
408
464
|
|
|
409
465
|
# 2) Configure o token (crie em https://pypi.org/manage/account/token/)
|
|
410
466
|
export TWINE_USERNAME=__token__
|
|
411
467
|
export TWINE_PASSWORD="<seu-token>"
|
|
412
468
|
|
|
413
469
|
# 3) Envie para o PyPI
|
|
414
|
-
|
|
470
|
+
python3 -m twine upload dist/*
|
|
415
471
|
|
|
416
472
|
# 4) Verifique instalação
|
|
417
|
-
|
|
473
|
+
python3 -m pip install -U raijin-server
|
|
418
474
|
raijin-server --version
|
|
419
475
|
```
|
|
420
476
|
|
|
@@ -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
|
-
|
|
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")
|
|
@@ -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)...")
|
|
@@ -238,3 +307,27 @@ cgroupDriver: systemd
|
|
|
238
307
|
|
|
239
308
|
typer.echo("Comando de join para workers:")
|
|
240
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)
|
|
@@ -15,8 +15,6 @@ 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",
|
|
20
18
|
"service.type=LoadBalancer",
|
|
21
19
|
f"certificatesResolvers.letsencrypt.acme.email={acme_email}",
|
|
22
20
|
"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
|
-
|
|
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 =
|
|
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.
|
|
3
|
+
Version: 0.2.11
|
|
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
|
-
|
|
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
|
-
|
|
503
|
+
python3 -m twine upload dist/*
|
|
448
504
|
|
|
449
505
|
# 4) Verifique instalação
|
|
450
|
-
|
|
506
|
+
python3 -m pip install -U raijin-server
|
|
451
507
|
raijin-server --version
|
|
452
508
|
```
|
|
453
509
|
|
|
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
|
|
File without changes
|
{raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server/modules/observability_dashboards.py
RENAMED
|
File without changes
|
{raijin_server-0.2.8 → raijin_server-0.2.11}/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
|
{raijin_server-0.2.8 → raijin_server-0.2.11}/src/raijin_server.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|