raijin-server 0.3.4__py3-none-any.whl → 0.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of raijin-server might be problematic. Click here for more details.
- raijin_server/__init__.py +1 -1
- raijin_server/cli.py +4 -17
- raijin_server/modules/__init__.py +4 -5
- raijin_server/modules/full_install.py +11 -19
- raijin_server/modules/harbor.py +685 -0
- raijin_server/modules/secrets.py +416 -95
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/METADATA +1 -1
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/RECORD +12 -13
- raijin_server/modules/observability_dashboards.py +0 -233
- raijin_server/modules/observability_ingress.py +0 -246
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/WHEEL +0 -0
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.3.4.dist-info → raijin_server-0.3.6.dist-info}/top_level.txt +0 -0
raijin_server/modules/secrets.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
"""Automacao de
|
|
1
|
+
"""Automacao de HashiCorp Vault e External Secrets Operator (production-ready).
|
|
2
2
|
|
|
3
|
-
Instala
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Instala Vault com MinIO backend para persistencia e External Secrets Operator
|
|
4
|
+
para sincronizar segredos do Vault para Secrets nativos do Kubernetes.
|
|
5
|
+
|
|
6
|
+
Arquitetura:
|
|
7
|
+
- Vault: Gerenciamento centralizado de segredos com MinIO como storage backend
|
|
8
|
+
- External Secrets Operator: Sincroniza segredos do Vault para K8s Secrets
|
|
9
|
+
- Aplicações: Usam Secrets nativos do K8s (transparente)
|
|
6
10
|
"""
|
|
7
11
|
|
|
12
|
+
import base64
|
|
8
13
|
import socket
|
|
9
14
|
import time
|
|
10
15
|
from pathlib import Path
|
|
@@ -20,7 +25,7 @@ from raijin_server.utils import (
|
|
|
20
25
|
write_file,
|
|
21
26
|
)
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
VAULT_NAMESPACE = "vault"
|
|
24
29
|
ESO_NAMESPACE = "external-secrets"
|
|
25
30
|
|
|
26
31
|
|
|
@@ -36,10 +41,10 @@ def _detect_node_name(ctx: ExecutionContext) -> str:
|
|
|
36
41
|
return socket.gethostname()
|
|
37
42
|
|
|
38
43
|
|
|
39
|
-
def
|
|
40
|
-
"""Verifica se existe instalacao do
|
|
44
|
+
def _check_existing_vault(ctx: ExecutionContext, namespace: str) -> bool:
|
|
45
|
+
"""Verifica se existe instalacao do Vault."""
|
|
41
46
|
result = run_cmd(
|
|
42
|
-
["helm", "status", "
|
|
47
|
+
["helm", "status", "vault", "-n", namespace],
|
|
43
48
|
ctx,
|
|
44
49
|
check=False,
|
|
45
50
|
)
|
|
@@ -56,12 +61,12 @@ def _check_existing_external_secrets(ctx: ExecutionContext, namespace: str) -> b
|
|
|
56
61
|
return result.returncode == 0
|
|
57
62
|
|
|
58
63
|
|
|
59
|
-
def
|
|
60
|
-
"""Remove instalacao anterior do
|
|
61
|
-
typer.echo("Removendo instalacao anterior do
|
|
64
|
+
def _uninstall_vault(ctx: ExecutionContext, namespace: str) -> None:
|
|
65
|
+
"""Remove instalacao anterior do Vault."""
|
|
66
|
+
typer.echo("Removendo instalacao anterior do Vault...")
|
|
62
67
|
|
|
63
68
|
run_cmd(
|
|
64
|
-
["helm", "uninstall", "
|
|
69
|
+
["helm", "uninstall", "vault", "-n", namespace],
|
|
65
70
|
ctx,
|
|
66
71
|
check=False,
|
|
67
72
|
)
|
|
@@ -82,16 +87,16 @@ def _uninstall_external_secrets(ctx: ExecutionContext, namespace: str) -> None:
|
|
|
82
87
|
time.sleep(5)
|
|
83
88
|
|
|
84
89
|
|
|
85
|
-
def
|
|
86
|
-
"""Aguarda pods
|
|
87
|
-
typer.echo("Aguardando pods
|
|
90
|
+
def _wait_for_pods_ready(ctx: ExecutionContext, namespace: str, label: str, timeout: int = 120) -> bool:
|
|
91
|
+
"""Aguarda pods ficarem Ready."""
|
|
92
|
+
typer.echo(f"Aguardando pods com label {label} ficarem Ready...")
|
|
88
93
|
deadline = time.time() + timeout
|
|
89
94
|
|
|
90
95
|
while time.time() < deadline:
|
|
91
96
|
result = run_cmd(
|
|
92
97
|
[
|
|
93
98
|
"kubectl", "-n", namespace, "get", "pods",
|
|
94
|
-
"-l",
|
|
99
|
+
"-l", label,
|
|
95
100
|
"-o", "jsonpath={range .items[*]}{.metadata.name}={.status.phase} {end}",
|
|
96
101
|
],
|
|
97
102
|
ctx,
|
|
@@ -109,49 +114,266 @@ def _wait_for_sealed_secrets_ready(ctx: ExecutionContext, namespace: str, timeou
|
|
|
109
114
|
pods.append((parts[0], parts[1]))
|
|
110
115
|
|
|
111
116
|
if pods and all(phase == "Running" for _, phase in pods):
|
|
112
|
-
typer.secho("
|
|
117
|
+
typer.secho(f" Pods {label} Ready.", fg=typer.colors.GREEN)
|
|
113
118
|
return True
|
|
114
119
|
|
|
115
120
|
time.sleep(5)
|
|
116
121
|
|
|
117
|
-
typer.secho(" Timeout aguardando
|
|
122
|
+
typer.secho(f" Timeout aguardando pods {label}.", fg=typer.colors.YELLOW)
|
|
118
123
|
return False
|
|
119
124
|
|
|
120
125
|
|
|
121
|
-
def
|
|
122
|
-
"""
|
|
126
|
+
def _get_minio_credentials(ctx: ExecutionContext) -> tuple[str, str]:
|
|
127
|
+
"""Obtem credenciais do MinIO do Secret do K8s."""
|
|
128
|
+
typer.echo("Obtendo credenciais do MinIO...")
|
|
129
|
+
|
|
130
|
+
# Tenta obter do secret minio-credentials no namespace minio
|
|
131
|
+
result = run_cmd(
|
|
132
|
+
["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.accesskey}"],
|
|
133
|
+
ctx,
|
|
134
|
+
check=False,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if result.returncode == 0 and result.stdout:
|
|
138
|
+
access_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
|
|
139
|
+
|
|
140
|
+
result = run_cmd(
|
|
141
|
+
["kubectl", "-n", "minio", "get", "secret", "minio-credentials", "-o", "jsonpath={.data.secretkey}"],
|
|
142
|
+
ctx,
|
|
143
|
+
check=False,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if result.returncode == 0 and result.stdout:
|
|
147
|
+
secret_key = base64.b64decode(result.stdout.strip()).decode("utf-8")
|
|
148
|
+
return access_key, secret_key
|
|
149
|
+
|
|
150
|
+
# Fallback para prompt manual
|
|
151
|
+
typer.secho("Não foi possível obter credenciais automaticamente.", fg=typer.colors.YELLOW)
|
|
152
|
+
access_key = typer.prompt("MinIO Access Key", default="thor")
|
|
153
|
+
secret_key = typer.prompt("MinIO Secret Key", default="rebel1on", hide_input=True)
|
|
154
|
+
|
|
155
|
+
return access_key, secret_key
|
|
156
|
+
|
|
123
157
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
def _initialize_vault(ctx: ExecutionContext, vault_ns: str, node_ip: str) -> tuple[str, list[str]]:
|
|
159
|
+
"""Inicializa o Vault e retorna root token e unseal keys."""
|
|
160
|
+
typer.echo("\n Inicializando Vault...")
|
|
161
|
+
|
|
162
|
+
result = run_cmd(
|
|
163
|
+
["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "operator", "init", "-format=json"],
|
|
164
|
+
ctx,
|
|
165
|
+
check=False,
|
|
128
166
|
)
|
|
129
|
-
|
|
130
|
-
cmd = [
|
|
131
|
-
"kubectl",
|
|
132
|
-
"-n",
|
|
133
|
-
namespace,
|
|
134
|
-
"get",
|
|
135
|
-
"secret",
|
|
136
|
-
"-l",
|
|
137
|
-
"sealedsecrets.bitnami.com/sealed-secrets-key",
|
|
138
|
-
"-o",
|
|
139
|
-
r"jsonpath={.items[0].data.tls\.crt}",
|
|
140
|
-
]
|
|
141
|
-
result = run_cmd(cmd, ctx, check=False)
|
|
167
|
+
|
|
142
168
|
if result.returncode != 0:
|
|
143
|
-
typer.secho("
|
|
144
|
-
|
|
169
|
+
typer.secho("Falha ao inicializar Vault.", fg=typer.colors.RED)
|
|
170
|
+
raise typer.Exit(1)
|
|
171
|
+
|
|
172
|
+
import json
|
|
173
|
+
init_data = json.loads(result.stdout)
|
|
174
|
+
root_token = init_data["root_token"]
|
|
175
|
+
unseal_keys = init_data["unseal_keys_b64"]
|
|
176
|
+
|
|
177
|
+
# Salva keys localmente
|
|
178
|
+
vault_keys_path = Path("/etc/vault/keys.json")
|
|
179
|
+
vault_keys_path.parent.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
vault_keys_path.write_text(json.dumps(init_data, indent=2))
|
|
181
|
+
typer.secho(f"\n✓ Vault keys salvas em {vault_keys_path}", fg=typer.colors.GREEN)
|
|
182
|
+
typer.secho("⚠️ IMPORTANTE: Guarde essas keys em local seguro!", fg=typer.colors.YELLOW, bold=True)
|
|
183
|
+
|
|
184
|
+
return root_token, unseal_keys
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _unseal_vault(ctx: ExecutionContext, vault_ns: str, unseal_keys: list[str]) -> None:
|
|
188
|
+
"""Destrava o Vault usando as unseal keys."""
|
|
189
|
+
typer.echo("\nDesbloqueando Vault...")
|
|
190
|
+
|
|
191
|
+
# Precisa de 3 keys das 5 geradas (threshold padrão)
|
|
192
|
+
for i in range(3):
|
|
193
|
+
run_cmd(
|
|
194
|
+
["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "operator", "unseal", unseal_keys[i]],
|
|
195
|
+
ctx,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
typer.secho("✓ Vault desbloqueado.", fg=typer.colors.GREEN)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _enable_kv_secrets(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
|
|
202
|
+
"""Habilita KV v2 secrets engine."""
|
|
203
|
+
typer.echo("\nHabilitando KV v2 secrets engine...")
|
|
204
|
+
|
|
205
|
+
run_cmd(
|
|
206
|
+
[
|
|
207
|
+
"kubectl", "-n", vault_ns, "exec", "vault-0", "--",
|
|
208
|
+
"vault", "secrets", "enable", "-path=secret", "kv-v2"
|
|
209
|
+
],
|
|
210
|
+
ctx,
|
|
211
|
+
env={"VAULT_TOKEN": root_token},
|
|
212
|
+
check=False, # Pode já estar habilitado
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
typer.secho("✓ KV v2 habilitado em path 'secret'.", fg=typer.colors.GREEN)
|
|
145
216
|
|
|
146
|
-
try:
|
|
147
|
-
import base64
|
|
148
217
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
218
|
+
def _configure_kubernetes_auth(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
|
|
219
|
+
"""Configura autenticação Kubernetes no Vault."""
|
|
220
|
+
typer.echo("\nConfigurando autenticação Kubernetes...")
|
|
221
|
+
|
|
222
|
+
# Habilita kubernetes auth
|
|
223
|
+
run_cmd(
|
|
224
|
+
["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "auth", "enable", "kubernetes"],
|
|
225
|
+
ctx,
|
|
226
|
+
env={"VAULT_TOKEN": root_token},
|
|
227
|
+
check=False,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Configura kubernetes auth
|
|
231
|
+
run_cmd(
|
|
232
|
+
[
|
|
233
|
+
"kubectl", "-n", vault_ns, "exec", "vault-0", "--",
|
|
234
|
+
"sh", "-c",
|
|
235
|
+
"vault write auth/kubernetes/config " +
|
|
236
|
+
"kubernetes_host=https://$KUBERNETES_PORT_443_TCP_ADDR:443"
|
|
237
|
+
],
|
|
238
|
+
ctx,
|
|
239
|
+
env={"VAULT_TOKEN": root_token},
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
typer.secho("✓ Autenticação Kubernetes configurada.", fg=typer.colors.GREEN)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _create_eso_policy_and_role(ctx: ExecutionContext, vault_ns: str, root_token: str, eso_ns: str) -> None:
|
|
246
|
+
"""Cria policy e role para External Secrets Operator."""
|
|
247
|
+
typer.echo("\nCriando policy e role para ESO...")
|
|
248
|
+
|
|
249
|
+
# Policy para ler todos os secrets
|
|
250
|
+
policy = """path "secret/data/*" {
|
|
251
|
+
capabilities = ["read"]
|
|
252
|
+
}"""
|
|
253
|
+
|
|
254
|
+
run_cmd(
|
|
255
|
+
["kubectl", "-n", vault_ns, "exec", "vault-0", "--", "vault", "policy", "write", "eso-policy", "-"],
|
|
256
|
+
ctx,
|
|
257
|
+
env={"VAULT_TOKEN": root_token},
|
|
258
|
+
input=policy,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Role vinculando serviceaccount do ESO
|
|
262
|
+
run_cmd(
|
|
263
|
+
[
|
|
264
|
+
"kubectl", "-n", vault_ns, "exec", "vault-0", "--",
|
|
265
|
+
"vault", "write", "auth/kubernetes/role/eso-role",
|
|
266
|
+
"bound_service_account_names=external-secrets",
|
|
267
|
+
f"bound_service_account_namespaces={eso_ns}",
|
|
268
|
+
"policies=eso-policy",
|
|
269
|
+
"ttl=24h"
|
|
270
|
+
],
|
|
271
|
+
ctx,
|
|
272
|
+
env={"VAULT_TOKEN": root_token},
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
typer.secho("✓ Policy 'eso-policy' e role 'eso-role' criadas.", fg=typer.colors.GREEN)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _create_secretstore_example(ctx: ExecutionContext, vault_ns: str, eso_ns: str, node_ip: str) -> None:
|
|
279
|
+
"""Cria exemplo de ClusterSecretStore e ExternalSecret."""
|
|
280
|
+
typer.echo("\nCriando exemplo de ClusterSecretStore...")
|
|
281
|
+
|
|
282
|
+
secretstore_yaml = f"""apiVersion: external-secrets.io/v1beta1
|
|
283
|
+
kind: ClusterSecretStore
|
|
284
|
+
metadata:
|
|
285
|
+
name: vault-backend
|
|
286
|
+
spec:
|
|
287
|
+
provider:
|
|
288
|
+
vault:
|
|
289
|
+
server: "http://vault.{vault_ns}.svc.cluster.local:8200"
|
|
290
|
+
path: "secret"
|
|
291
|
+
version: "v2"
|
|
292
|
+
auth:
|
|
293
|
+
kubernetes:
|
|
294
|
+
mountPath: "kubernetes"
|
|
295
|
+
role: "eso-role"
|
|
296
|
+
serviceAccountRef:
|
|
297
|
+
name: "external-secrets"
|
|
298
|
+
namespace: "{eso_ns}"
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
secretstore_path = Path("/tmp/raijin-vault-secretstore.yaml")
|
|
302
|
+
write_file(secretstore_path, secretstore_yaml, ctx)
|
|
303
|
+
|
|
304
|
+
run_cmd(
|
|
305
|
+
["kubectl", "apply", "-f", str(secretstore_path)],
|
|
306
|
+
ctx,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
typer.secho("✓ ClusterSecretStore 'vault-backend' criado.", fg=typer.colors.GREEN)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _create_example_secret(ctx: ExecutionContext, vault_ns: str, root_token: str) -> None:
|
|
313
|
+
"""Cria um secret de exemplo no Vault."""
|
|
314
|
+
typer.echo("\nCriando secret de exemplo no Vault...")
|
|
315
|
+
|
|
316
|
+
run_cmd(
|
|
317
|
+
[
|
|
318
|
+
"kubectl", "-n", vault_ns, "exec", "vault-0", "--",
|
|
319
|
+
"vault", "kv", "put", "secret/example",
|
|
320
|
+
"username=admin",
|
|
321
|
+
"password=supersecret123"
|
|
322
|
+
],
|
|
323
|
+
ctx,
|
|
324
|
+
env={"VAULT_TOKEN": root_token},
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
typer.secho("✓ Secret 'secret/example' criado no Vault.", fg=typer.colors.GREEN)
|
|
328
|
+
|
|
329
|
+
# Cria ExternalSecret de exemplo
|
|
330
|
+
external_secret_yaml = """apiVersion: external-secrets.io/v1beta1
|
|
331
|
+
kind: ExternalSecret
|
|
332
|
+
metadata:
|
|
333
|
+
name: example-secret
|
|
334
|
+
namespace: default
|
|
335
|
+
spec:
|
|
336
|
+
refreshInterval: 1h
|
|
337
|
+
secretStoreRef:
|
|
338
|
+
name: vault-backend
|
|
339
|
+
kind: ClusterSecretStore
|
|
340
|
+
target:
|
|
341
|
+
name: example-secret
|
|
342
|
+
creationPolicy: Owner
|
|
343
|
+
data:
|
|
344
|
+
- secretKey: username
|
|
345
|
+
remoteRef:
|
|
346
|
+
key: secret/example
|
|
347
|
+
property: username
|
|
348
|
+
- secretKey: password
|
|
349
|
+
remoteRef:
|
|
350
|
+
key: secret/example
|
|
351
|
+
property: password
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
external_secret_path = Path("/tmp/raijin-vault-externalsecret.yaml")
|
|
355
|
+
write_file(external_secret_path, external_secret_yaml, ctx)
|
|
356
|
+
|
|
357
|
+
run_cmd(
|
|
358
|
+
["kubectl", "apply", "-f", str(external_secret_path)],
|
|
359
|
+
ctx,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
typer.secho("✓ ExternalSecret 'example-secret' criado no namespace default.", fg=typer.colors.GREEN)
|
|
363
|
+
|
|
364
|
+
# Aguarda sincronização
|
|
365
|
+
time.sleep(5)
|
|
366
|
+
|
|
367
|
+
# Verifica se o Secret foi criado
|
|
368
|
+
result = run_cmd(
|
|
369
|
+
["kubectl", "-n", "default", "get", "secret", "example-secret"],
|
|
370
|
+
ctx,
|
|
371
|
+
check=False,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if result.returncode == 0:
|
|
375
|
+
typer.secho("\n✓ Secret sincronizado com sucesso! Teste com:", fg=typer.colors.GREEN)
|
|
376
|
+
typer.echo(" kubectl -n default get secret example-secret -o yaml")
|
|
155
377
|
|
|
156
378
|
|
|
157
379
|
def run(ctx: ExecutionContext) -> None:
|
|
@@ -159,69 +381,132 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
159
381
|
ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou habilite dry-run.")
|
|
160
382
|
ensure_tool("helm", ctx, install_hint="Instale helm ou habilite dry-run.")
|
|
161
383
|
|
|
162
|
-
typer.echo("Instalando
|
|
384
|
+
typer.echo("Instalando HashiCorp Vault + External Secrets Operator...")
|
|
163
385
|
|
|
164
|
-
|
|
165
|
-
eso_ns = typer.prompt("Namespace para
|
|
386
|
+
vault_ns = typer.prompt("Namespace para Vault", default=VAULT_NAMESPACE)
|
|
387
|
+
eso_ns = typer.prompt("Namespace para External Secrets", default=ESO_NAMESPACE)
|
|
166
388
|
|
|
167
389
|
node_name = _detect_node_name(ctx)
|
|
390
|
+
|
|
391
|
+
# Detecta IP do node para acesso ao MinIO
|
|
392
|
+
result = run_cmd(
|
|
393
|
+
["kubectl", "get", "nodes", "-o", "jsonpath={.items[0].status.addresses[?(@.type=='InternalIP')].address}"],
|
|
394
|
+
ctx,
|
|
395
|
+
check=False,
|
|
396
|
+
)
|
|
397
|
+
node_ip = result.stdout.strip() if result.returncode == 0 else "192.168.1.81"
|
|
398
|
+
|
|
399
|
+
minio_host = typer.prompt("MinIO host", default=f"{node_ip}:30900")
|
|
400
|
+
access_key, secret_key = _get_minio_credentials(ctx)
|
|
168
401
|
|
|
169
|
-
#
|
|
170
|
-
typer.secho("\n==
|
|
402
|
+
# ========== HashiCorp Vault ==========
|
|
403
|
+
typer.secho("\n== HashiCorp Vault ==", fg=typer.colors.CYAN, bold=True)
|
|
171
404
|
|
|
172
|
-
|
|
173
|
-
if _check_existing_sealed_secrets(ctx, sealed_ns):
|
|
405
|
+
if _check_existing_vault(ctx, vault_ns):
|
|
174
406
|
cleanup = typer.confirm(
|
|
175
|
-
"Instalacao anterior do
|
|
407
|
+
"Instalacao anterior do Vault detectada. Limpar antes de reinstalar?",
|
|
176
408
|
default=False,
|
|
177
409
|
)
|
|
178
410
|
if cleanup:
|
|
179
|
-
|
|
411
|
+
_uninstall_vault(ctx, vault_ns)
|
|
180
412
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
413
|
+
# Cria bucket no MinIO para Vault storage
|
|
414
|
+
typer.echo("\nCriando bucket 'vault-storage' no MinIO...")
|
|
415
|
+
run_cmd(
|
|
416
|
+
[
|
|
417
|
+
"mc", "mb", "--ignore-existing",
|
|
418
|
+
f"minio/vault-storage"
|
|
419
|
+
],
|
|
420
|
+
ctx,
|
|
421
|
+
check=False,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
vault_values_yaml = f"""server:
|
|
425
|
+
ha:
|
|
426
|
+
enabled: true
|
|
427
|
+
replicas: 1
|
|
428
|
+
raft:
|
|
429
|
+
enabled: false
|
|
430
|
+
|
|
431
|
+
standalone:
|
|
432
|
+
enabled: true
|
|
433
|
+
config: |
|
|
434
|
+
ui = true
|
|
435
|
+
|
|
436
|
+
listener "tcp" {{
|
|
437
|
+
tls_disable = 1
|
|
438
|
+
address = "[::]:8200"
|
|
439
|
+
cluster_address = "[::]:8201"
|
|
440
|
+
}}
|
|
441
|
+
|
|
442
|
+
storage "s3" {{
|
|
443
|
+
endpoint = "http://{minio_host}"
|
|
444
|
+
bucket = "vault-storage"
|
|
445
|
+
access_key = "{access_key}"
|
|
446
|
+
secret_key = "{secret_key}"
|
|
447
|
+
s3_force_path_style = true
|
|
448
|
+
}}
|
|
449
|
+
|
|
450
|
+
api_addr = "http://vault.{vault_ns}.svc.cluster.local:8200"
|
|
451
|
+
cluster_addr = "http://vault-0.vault-internal:8201"
|
|
452
|
+
|
|
453
|
+
tolerations:
|
|
454
|
+
- key: node-role.kubernetes.io/control-plane
|
|
455
|
+
operator: Exists
|
|
456
|
+
effect: NoSchedule
|
|
457
|
+
- key: node-role.kubernetes.io/master
|
|
458
|
+
operator: Exists
|
|
459
|
+
effect: NoSchedule
|
|
460
|
+
|
|
461
|
+
nodeSelector:
|
|
462
|
+
kubernetes.io/hostname: {node_name}
|
|
463
|
+
|
|
464
|
+
resources:
|
|
465
|
+
requests:
|
|
466
|
+
memory: 256Mi
|
|
467
|
+
cpu: 250m
|
|
468
|
+
limits:
|
|
469
|
+
memory: 512Mi
|
|
470
|
+
|
|
471
|
+
ui:
|
|
472
|
+
enabled: true
|
|
473
|
+
serviceType: "NodePort"
|
|
474
|
+
serviceNodePort: 30820
|
|
475
|
+
|
|
476
|
+
injector:
|
|
477
|
+
enabled: false
|
|
196
478
|
"""
|
|
197
479
|
|
|
198
|
-
|
|
199
|
-
write_file(
|
|
480
|
+
vault_values_path = Path("/tmp/raijin-vault-values.yaml")
|
|
481
|
+
write_file(vault_values_path, vault_values_yaml, ctx)
|
|
200
482
|
|
|
201
483
|
helm_upgrade_install(
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
|
|
484
|
+
"vault",
|
|
485
|
+
"vault",
|
|
486
|
+
vault_ns,
|
|
205
487
|
ctx,
|
|
206
|
-
repo="
|
|
207
|
-
repo_url="https://
|
|
488
|
+
repo="hashicorp",
|
|
489
|
+
repo_url="https://helm.releases.hashicorp.com",
|
|
208
490
|
create_namespace=True,
|
|
209
|
-
extra_args=["-f", str(
|
|
491
|
+
extra_args=["-f", str(vault_values_path)],
|
|
210
492
|
)
|
|
211
493
|
|
|
212
494
|
if not ctx.dry_run:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
495
|
+
_wait_for_pods_ready(ctx, vault_ns, "app.kubernetes.io/name=vault", timeout=180)
|
|
496
|
+
|
|
497
|
+
# Inicializa Vault
|
|
498
|
+
root_token, unseal_keys = _initialize_vault(ctx, vault_ns, node_ip)
|
|
499
|
+
|
|
500
|
+
# Destrava Vault
|
|
501
|
+
_unseal_vault(ctx, vault_ns, unseal_keys)
|
|
502
|
+
|
|
503
|
+
# Configura Vault
|
|
504
|
+
_enable_kv_secrets(ctx, vault_ns, root_token)
|
|
505
|
+
_configure_kubernetes_auth(ctx, vault_ns, root_token)
|
|
220
506
|
|
|
221
|
-
#
|
|
507
|
+
# ========== External Secrets Operator ==========
|
|
222
508
|
typer.secho("\n== External Secrets Operator ==", fg=typer.colors.CYAN, bold=True)
|
|
223
509
|
|
|
224
|
-
# Prompt opcional de limpeza
|
|
225
510
|
if _check_existing_external_secrets(ctx, eso_ns):
|
|
226
511
|
cleanup = typer.confirm(
|
|
227
512
|
"Instalacao anterior do External Secrets detectada. Limpar antes de reinstalar?",
|
|
@@ -282,12 +567,48 @@ resources:
|
|
|
282
567
|
extra_args=["-f", str(eso_values_path)],
|
|
283
568
|
)
|
|
284
569
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
570
|
+
if not ctx.dry_run:
|
|
571
|
+
_wait_for_pods_ready(ctx, eso_ns, "app.kubernetes.io/name=external-secrets", timeout=120)
|
|
572
|
+
|
|
573
|
+
# Configura integração Vault + ESO
|
|
574
|
+
_create_eso_policy_and_role(ctx, vault_ns, root_token, eso_ns)
|
|
575
|
+
_create_secretstore_example(ctx, vault_ns, eso_ns, node_ip)
|
|
576
|
+
_create_example_secret(ctx, vault_ns, root_token)
|
|
289
577
|
|
|
290
|
-
typer.secho("\
|
|
291
|
-
|
|
292
|
-
typer.
|
|
578
|
+
typer.secho("\n✓ Vault + External Secrets Operator instalado com sucesso!", fg=typer.colors.GREEN, bold=True)
|
|
579
|
+
|
|
580
|
+
typer.secho("\n=== Acesso ao Vault UI ===", fg=typer.colors.CYAN)
|
|
581
|
+
typer.echo(f"URL: http://{node_ip}:30820")
|
|
582
|
+
typer.echo(f"Token: {root_token if not ctx.dry_run else '<root-token>'}")
|
|
583
|
+
|
|
584
|
+
typer.secho("\n=== Como usar ===", fg=typer.colors.CYAN)
|
|
585
|
+
typer.echo("1. Criar segredo no Vault:")
|
|
586
|
+
typer.echo(f" kubectl -n {vault_ns} exec vault-0 -- vault kv put secret/myapp username=admin password=secret123")
|
|
587
|
+
|
|
588
|
+
typer.echo("\n2. Criar ExternalSecret:")
|
|
589
|
+
typer.echo(" kubectl apply -f - <<EOF")
|
|
590
|
+
typer.echo(" apiVersion: external-secrets.io/v1beta1")
|
|
591
|
+
typer.echo(" kind: ExternalSecret")
|
|
592
|
+
typer.echo(" metadata:")
|
|
593
|
+
typer.echo(" name: myapp-secret")
|
|
594
|
+
typer.echo(" spec:")
|
|
595
|
+
typer.echo(" secretStoreRef:")
|
|
596
|
+
typer.echo(" name: vault-backend")
|
|
597
|
+
typer.echo(" kind: ClusterSecretStore")
|
|
598
|
+
typer.echo(" target:")
|
|
599
|
+
typer.echo(" name: myapp-secret")
|
|
600
|
+
typer.echo(" data:")
|
|
601
|
+
typer.echo(" - secretKey: username")
|
|
602
|
+
typer.echo(" remoteRef:")
|
|
603
|
+
typer.echo(" key: secret/myapp")
|
|
604
|
+
typer.echo(" property: username")
|
|
605
|
+
typer.echo(" EOF")
|
|
606
|
+
|
|
607
|
+
typer.echo("\n3. Secret será sincronizado automaticamente!")
|
|
608
|
+
typer.echo(" kubectl get secret myapp-secret -o yaml")
|
|
609
|
+
|
|
610
|
+
typer.secho("\n⚠️ IMPORTANTE:", fg=typer.colors.YELLOW, bold=True)
|
|
611
|
+
typer.echo(f"- Root token e unseal keys salvos em: /etc/vault/keys.json")
|
|
612
|
+
typer.echo("- Faça backup dessas keys em local seguro!")
|
|
613
|
+
typer.echo("- Após reboot do Vault, use: kubectl -n vault exec vault-0 -- vault operator unseal")
|
|
293
614
|
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
raijin_server/__init__.py,sha256=
|
|
2
|
-
raijin_server/cli.py,sha256=
|
|
1
|
+
raijin_server/__init__.py,sha256=DizNqR7krrm4UEAK6a2xeOG3wuV7xlSekBC-mCSHadw,94
|
|
2
|
+
raijin_server/cli.py,sha256=74ZqEPhg9R9jxNFZRV37CHrwm-dBKmYdT_MxdK4FKKA,37585
|
|
3
3
|
raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
|
|
4
4
|
raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
|
|
5
5
|
raijin_server/module_manager.py,sha256=Wmhj603CN0XGUVr7_Fo8CHzKd9yIbS9x5BJLqDj78kw,10259
|
|
6
6
|
raijin_server/utils.py,sha256=9RnGnPoUTYOpMVRLNa4P4lIQrJNQLkSkPUxycZRGv78,20827
|
|
7
7
|
raijin_server/validators.py,sha256=EATYPy2pllAb6IX4gUZKnELvospWwyGV3DHrzxb_RMg,11761
|
|
8
|
-
raijin_server/modules/__init__.py,sha256=
|
|
8
|
+
raijin_server/modules/__init__.py,sha256=BCTLuNtmvn8IWqGNQZQBRokqAv-KwUZP4_kyxkyHyN4,896
|
|
9
9
|
raijin_server/modules/apokolips_demo.py,sha256=8ltsXRbVDwlDwLMIvh02NG-FeAfBWw_v6lh7IGOyNqs,13725
|
|
10
10
|
raijin_server/modules/bootstrap.py,sha256=oVIGNRW_JbgY8zXNHGAIP0vGbbHNHyQexthxo5zhbcw,9762
|
|
11
11
|
raijin_server/modules/calico.py,sha256=TTPF1bLFdAKb3IVOqFqRxNblULkRmMMRylsIBp4w8I8,6700
|
|
12
12
|
raijin_server/modules/cert_manager.py,sha256=XkFlXJjiP4_9It_PJaFcVYMS-QKTzzFAt839QQ9qNsg,50223
|
|
13
13
|
raijin_server/modules/essentials.py,sha256=2xUXCyCQtFGd2DnCKV81N1R6bEJqH8zaet8mLovtQ1I,689
|
|
14
14
|
raijin_server/modules/firewall.py,sha256=h6AISqiZeTinVT7BjmQIS872qRAFZJLg7meqlth3cfw,757
|
|
15
|
-
raijin_server/modules/full_install.py,sha256=
|
|
15
|
+
raijin_server/modules/full_install.py,sha256=M4SV4OA-r41xhpMmZQvUieRugiq0faLa4f0N499ksd4,15035
|
|
16
16
|
raijin_server/modules/grafana.py,sha256=8YbKG-UL19lwTkH1-ZxpUetOZd-CLI4_kPsuZblNaWI,18080
|
|
17
|
+
raijin_server/modules/harbor.py,sha256=ix0qnu_FugzbFnnTRZ3Dyy6UmKdm3nD0ubZ4SZYHkWE,21940
|
|
17
18
|
raijin_server/modules/hardening.py,sha256=4hz3ifkMhPlXa2n7gPxN0gitQgzALZ-073vuU3LM4RI,1616
|
|
18
19
|
raijin_server/modules/harness.py,sha256=uWTxTVJlY_VB6xi4ftMtTSaIb96HA8WJQS-RbyxU45M,5391
|
|
19
20
|
raijin_server/modules/internal_dns.py,sha256=Jynngq0TEEUo3jkAR4m8F1ihF10rkQuKHVP-gZYyDFY,15191
|
|
@@ -25,11 +26,9 @@ raijin_server/modules/loki.py,sha256=fRoXNghwffW6afE_a3sKMhjPJ9DIWhGMTTPM-ltNr2E
|
|
|
25
26
|
raijin_server/modules/metallb.py,sha256=uUuklc_RsQ-W2qDVRMQAxQm9HKGEqso444b1IwBpM6w,8554
|
|
26
27
|
raijin_server/modules/minio.py,sha256=ZoxugJvvuGLzViDfEzrVCRZUevoiFwcEy0PNyn0My4w,18918
|
|
27
28
|
raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
|
|
28
|
-
raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
|
|
29
|
-
raijin_server/modules/observability_ingress.py,sha256=S4MtJKahiZ1qSx0P71P3IhKvq4RY-g01Z4IogW3c1hs,7045
|
|
30
29
|
raijin_server/modules/prometheus.py,sha256=lyhaqLIfMl0GtQ2b2Hre7_A47HrHBB5gspmnWtwXZ4Y,21880
|
|
31
30
|
raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
|
|
32
|
-
raijin_server/modules/secrets.py,sha256=
|
|
31
|
+
raijin_server/modules/secrets.py,sha256=C5k10ODo6Ai_e6Ei-JiWTOa-cOh-5d2__PhTQA0UmjI,19452
|
|
33
32
|
raijin_server/modules/ssh_hardening.py,sha256=Zd0dlylUBr01SkrI1CS05-0DB9xIto5rWH1bUVs80ow,5422
|
|
34
33
|
raijin_server/modules/traefik.py,sha256=omziywss4o-8t64Kj-upLqbXdFYm2JwqOoOukDUmqxY,5008
|
|
35
34
|
raijin_server/modules/velero.py,sha256=yDtqd6yUu0L5wzLCjYXqvvxB_RyaAoZtntb6HoHVAOo,5642
|
|
@@ -40,9 +39,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
|
|
|
40
39
|
raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
|
|
41
40
|
raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
|
|
42
41
|
raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
|
|
43
|
-
raijin_server-0.3.
|
|
44
|
-
raijin_server-0.3.
|
|
45
|
-
raijin_server-0.3.
|
|
46
|
-
raijin_server-0.3.
|
|
47
|
-
raijin_server-0.3.
|
|
48
|
-
raijin_server-0.3.
|
|
42
|
+
raijin_server-0.3.6.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
|
|
43
|
+
raijin_server-0.3.6.dist-info/METADATA,sha256=egpj4eEFQ3JJAA-bT3Iz5ebOc57e5JpyuQ2a8-ruAD4,8829
|
|
44
|
+
raijin_server-0.3.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
45
|
+
raijin_server-0.3.6.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
|
|
46
|
+
raijin_server-0.3.6.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
|
|
47
|
+
raijin_server-0.3.6.dist-info/RECORD,,
|