docs: Actualización de contexto IA, Drones, Candados y nuevos nodos
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"rsa": "eyJpdiI6IkpoTmpZRWZMeE12eWhXSVMiLCJ0YWciOiJUUnJGY05qRlVoOTl2REplTms0ZU5RPT0iLCJkYXRhIjoiNkFIWCsyYm9ndzZKbGM3c0hRPT0ifQ==",
|
||||
"domain_admin": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"admindasu": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"win_domain": "eyJpdiI6IitrY2V0eEQxektEMFk2cGgiLCJ0YWciOiJtQmdIYU90ZTFFWUlGZElsMXBIOTJBPT0iLCJkYXRhIjoiNVVFanhaN2tIdHViZDRDRnlnPT0ifQ==",
|
||||
"deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=",
|
||||
"srv-dasu:rmonla": "eyJpdiI6IkFoczJZWUIwQ2piZzdJNzgiLCJ0YWciOiJJdDhCZndRc1RoZFhVMVQ4OEhaN1J3PT0iLCJkYXRhIjoiaUQycmxndGFsUkE2dVRaTEgrTT0ifQ==",
|
||||
"dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==",
|
||||
"ns8:rmonla:sudo": "eyJpdiI6IkRqbTBhVlZSYUpVdno3TFkiLCJ0YWciOiJJSnE2ei8zZHIrV2hFQks1VjNpVGN3PT0iLCJkYXRhIjoiT3JxTjRhTmlQK2R3TlJZZk9BPT0ifQ==",
|
||||
"srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=",
|
||||
"srv-ns8:rmonla:sudo": "eyJpdiI6InJDOUp2STdYR1crb2NkMTgiLCJ0YWciOiJHSUZVbXM5VnVmZ3d1NXQ3SFVwOUdRPT0iLCJkYXRhIjoib1ZVTzdaLzk1WGdwZDhKREM3aWpnV3c9In0=",
|
||||
"dasu-sql2:Administrator": "eyJpdiI6IlJ0VWJzblVGM2NhWEtvNlgiLCJ0YWciOiJtM0llb2luUmRUZng0NnBsL1JtKzl3PT0iLCJkYXRhIjoiSHVhaklOTFlsSm1PbDJkTjNoYlF0UT09In0=",
|
||||
"srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ==",
|
||||
"srv-ns8:root": "eyJpdiI6Ik9UbHBieDJhU0JZbFFEM0wiLCJ0YWciOiJZeWJ5K0lUUFdOcEQwTmJ5UzhwcVFRPT0iLCJkYXRhIjoiNUE1QW5yenZ0QldjNEdVNXVBPT0ifQ==",
|
||||
"srv-ns8:rsa:passphrase": "eyJpdiI6ImpwRnFPYTZJdEtnUXdxT00iLCJ0YWciOiJMcXRBQ0lOT0paUE50Qjgva3lHSElRPT0iLCJkYXRhIjoiRDUzOFNuY3V1RUZXVHV4U3NnPT0ifQ==",
|
||||
"srv-ns8:rmonla:sudo": "eyJpdiI6Ilg2VzhIMWN6Y2xYVzR3ZzgiLCJ0YWciOiJuWDk0SDcraVhJQTQxd3I3bEpLRFBnPT0iLCJkYXRhIjoiSllyRy9ScXFBRFBFc0l5emRRPT0ifQ==",
|
||||
"srv-ns8:rmonla:rsa": "eyJpdiI6IkpoTmpZRWZMeE12eWhXSVMiLCJ0YWciOiJUUnJGY05qRlVoOTl2REplTms0ZU5RPT0iLCJkYXRhIjoiNkFIWCsyYm9ndzZKbGM3c0hRPT0ifQ==",
|
||||
"srvv-dns:rmonla:sudo": "eyJpdiI6InkyY01pSHlNeGFzZ2lDcnkiLCJ0YWciOiJqYTdkaFM0bTBNMWZhTWxJRUtGTWtBPT0iLCJkYXRhIjoiWmVYbzRWRjNNS244UktuaFVnPT0ifQ==",
|
||||
"srvv-DNS:rmonla:sudo": "eyJpdiI6InkyY01pSHlNeGFzZ2lDcnkiLCJ0YWciOiJqYTdkaFM0bTBNMWZhTWxJRUtGTWtBPT0iLCJkYXRhIjoiWmVYbzRWRjNNS244UktuaFVnPT0ifQ=="
|
||||
"srv-dasu:rmonla": "eyJpdiI6IkFoczJZWUIwQ2piZzdJNzgiLCJ0YWciOiJJdDhCZndRc1RoZFhVMVQ4OEhaN1J3PT0iLCJkYXRhIjoiaUQycmxndGFsUkE2dVRaTEgrTT0ifQ==",
|
||||
"srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=",
|
||||
"dasu-dc:admindasu": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ==",
|
||||
"dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==",
|
||||
"deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=",
|
||||
"srv-ns8:rmonla": "eyJpdiI6IlJ4cHZkbFpJQWVsTTl3T1YiLCJ0YWciOiJ3WTZGMDEvNHNsZm1wZm5jMEF4Q1lBPT0iLCJkYXRhIjoibFljUHRYTm44bUxvQjhCa2tBPT0ifQ==",
|
||||
"dasu-sql3:Administrador": "eyJpdiI6IlRiMDh3U1N2aVhTcDYzbzciLCJ0YWciOiI5NVNyUDFlMXlrM0dGRlNqUXhLbEt3PT0iLCJkYXRhIjoiVkp2V09zSjczcS96UktTSlZTOFdBdz09In0="
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1775576757
|
||||
1775682230
|
||||
@@ -1,31 +1,43 @@
|
||||
# 🔐 candados: Gestión Segura de Secretos
|
||||
# 🔐 candados: Acceso Seguro a Contraseñas (Sin Exponer)
|
||||
|
||||
## Resumen Ejecutivo
|
||||
`candados` es la herramienta central de seguridad del ecosistema ADN. Cifra y gestiona secretos con **AES-256-GCM**, implementando un sistema de **Candado Temporal** que requiere autorización explícita (5 min) antes de liberar credenciales.
|
||||
`candados` es la herramienta central de seguridad del ecosistema ADN. Cifra y gestiona secretos con **AES-256-GCM**, implementando un sistema de **Candado Temporal** que requiere autorización explícita (30 min) antes de liberar credenciales.
|
||||
|
||||
**Principio fundamental:** Las contraseñas **NUNCA** deben aparecer en pantalla, logs ni archivos temporales.
|
||||
**Principio fundamental:** Las contraseñas **NUNCA** deben aparecer en pantalla, logs ni archivos temporales visibles.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Arquitectura
|
||||
- **Cripto**: Motor AES-256-GCM con IV aleatorio y autenticación.
|
||||
- **Bóveda**: Persistencia cifrada en `.boveda.json` (permisos 600).
|
||||
- **MFA Temporal**: Sesiones de 5 min via `.session` (permisos 600).
|
||||
- **Master Key**: `.master.key` local (permisos 600, nunca se comparte).
|
||||
## ⚠️ REGLA DE ORO — NUNCA MOSTRAR CONTRASEÑAS
|
||||
|
||||
## 🚀 Comando Primario: `run` ★
|
||||
Esta herramienta está diseñada para que las contraseñas **NUNCA** se muestren en pantalla ni queden registradas en logs, históricos de shell o archivos temporales visibles.
|
||||
|
||||
El comando `run` es la forma **recomendada** de usar credenciales. Inyecta `USR` y `PASS` directamente en el entorno del subproceso sin imprimir nada:
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🛡️ NUNCA uses `get` en comandos automatizados o logs │
|
||||
│ │
|
||||
│ ✅ CORRECTO: ruby candados.rb run <clave> 'ssh $USR@host' │
|
||||
│ ❌ INCORRECTO: ruby candados.rb get <clave> | algo │
|
||||
│ │
|
||||
│ El comando `get` imprime la contraseña a stdout → queda en logs │
|
||||
│ El comando `run` inyecta USR/PASS en el entorno → no deja rastro │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📥 MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES
|
||||
|
||||
### ★ MÉTODO 1: `run` (PRIMARIO - Recomendado para comandos)
|
||||
|
||||
Ejecuta un comando con `USR` y `PASS` inyectadas en su entorno. La contraseña **NUNCA** se imprime ni queda en logs.
|
||||
|
||||
```bash
|
||||
# 1. Autorizar (dura 5 min)
|
||||
ruby candados.rb authorize
|
||||
# Sintaxis
|
||||
ruby candados.rb run <clave> 'comando que usa $USR y $PASS'
|
||||
|
||||
# 2. Ejecutar comando con credenciales inyectadas
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host "comando"'
|
||||
|
||||
# Retrocompatible con variable legacy (SSHPASS, etc.)
|
||||
ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
# Ejemplos
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
ruby candados.rb run srv-ns8:root 'ssh $USR@$HOSTNAME "comando"'
|
||||
```
|
||||
|
||||
**¿Qué pasa internamente?**
|
||||
@@ -35,6 +47,30 @@ ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
4. Limpia las variables al terminar
|
||||
5. **Nada se imprime en pantalla ni queda en logs**
|
||||
|
||||
### ★ MÉTODO 2: `load` (Para shell interactivo)
|
||||
|
||||
Carga `USR` y `PASS` en variables de entorno del shell actual. Usa un archivo temporal (600) que se auto-elimina tras el source.
|
||||
|
||||
```bash
|
||||
# Sintaxis
|
||||
eval $(ruby candados.rb load <clave>)
|
||||
|
||||
# Ejemplo
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
# → Carga $USR y $PASS en tu shell actual
|
||||
sshpass -p $PASS ssh $USR@servidor
|
||||
```
|
||||
|
||||
**Seguridad:** El archivo temporal tiene permisos `600` y se auto-borra inmediatamente después del `source`.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Arquitectura
|
||||
- **Cripto**: Motor AES-256-GCM con IV aleatorio y autenticación.
|
||||
- **Bóveda**: Persistencia cifrada en `.boveda.json` (permisos 600).
|
||||
- **MFA Temporal**: Sesiones de 30 min via `.session` (permisos 600).
|
||||
- **Master Key**: `.master.key` local (permisos 600, nunca se comparte).
|
||||
|
||||
## 🔒 Comando: `sudo`
|
||||
|
||||
Ejecuta comandos con privilegios, resolviendo la contraseña automáticamente:
|
||||
@@ -49,20 +85,7 @@ La clave se busca automáticamente:
|
||||
2. Variantes normalizadas del hostname
|
||||
3. Fallback genérico: `sudo`
|
||||
|
||||
## 📦 Comando Secundario: `load`
|
||||
|
||||
Para uso interactivo cuando se necesitan las variables en el shell actual. **Nunca imprime la contraseña:**
|
||||
|
||||
```bash
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
# USR y PASS están disponibles en el shell
|
||||
```
|
||||
|
||||
Internamente escribe los exports a un archivo temporal (`tmp/.candados_env_<hex>`) con permisos `600`, y a stdout solo emite el comando `source` + autoborrado.
|
||||
|
||||
**Nota:** Los temporales se guardan en `tmp/` del repositorio (no en `/tmp/`) para cumplir con el principio 11 de `05_ia.md` (Workspace Bounds).
|
||||
|
||||
## 🔑 Resolución Automática de Pares
|
||||
## 🔑 Resolución Automática de Claves
|
||||
|
||||
Los comandos `run` y `load` detectan automáticamente el par usuario/contraseña:
|
||||
|
||||
@@ -77,12 +100,13 @@ Los comandos `run` y `load` detectan automáticamente el par usuario/contraseña
|
||||
| Comando | Seguridad | Descripción |
|
||||
|---|---|---|
|
||||
| `run <clave> <cmd>` | ✅ Nada expuesto | **★ PRIMARIO** - Ejecuta con USR/PASS inyectados |
|
||||
| `sudo <cmd>` | ✅ Nada expuesto | Ejecuta con privilegios desde bóveda |
|
||||
| `load <clave>` | ✅ Archivo temporal 600 | Carga en shell (usar con `eval`) |
|
||||
| `get <clave>` | ⚠ Imprime a stdout | Obtener valor directo (usar con cuidado) |
|
||||
| `authorize` | ✅ | Abrir candado (5 min) |
|
||||
| `sudo <cmd>` | ✅ Nada expuesto | Ejecuta con privilegios desde bóveda |
|
||||
| `authorize` | ✅ | Abrir candado (30 min) |
|
||||
| `cerrar` | ✅ | Cerrar sesión manualmente |
|
||||
| `set <clave>` | ✅ Input sin eco | Guardar secreto |
|
||||
| `list` | ✅ | Listar claves |
|
||||
| `list` | ✅ | Listar claves (no muestra valores) |
|
||||
| `get <clave>` | ⚠ Imprime a stdout | Obtener valor directo (⚠ solo uso manual) |
|
||||
|
||||
## 🛡️ Auditoría
|
||||
|
||||
@@ -93,12 +117,14 @@ Archivo `access.log` (permisos 600) registra:
|
||||
|
||||
## 🧠 Contexto para IA
|
||||
|
||||
1. **Siempre** ejecutar `authorize` antes de operar
|
||||
2. **Usar `run`** como método primario (inyecta, no imprime)
|
||||
3. **Evitar `get`** en comandos automatizados (filtra en logs)
|
||||
4. Cerrar sesión con `cerrar` cuando ya no se necesite
|
||||
1. **Siempre** ejecutar `authorize` antes de operar (sesión de 30 min)
|
||||
2. **Usar `run`** como método primario (inyecta USR/PASS, no imprime)
|
||||
3. **Usar `load`** para shell interactivo (eval + source)
|
||||
4. **Evitar `get`** en automatizaciones (imprime la contraseña)
|
||||
5. **Cerrar sesión** con `cerrar` cuando ya no se necesite
|
||||
|
||||
```bash
|
||||
# Flujo típico
|
||||
ruby candados.rb authorize
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host "ls"'
|
||||
ruby candados.rb cerrar
|
||||
|
||||
+185
-41
@@ -258,7 +258,14 @@ def cmd_run(cripto, boveda, clave, *args)
|
||||
token_user = boveda.get("#{prefijo}_user")
|
||||
user_value = cripto.decrypt(token_user) if token_user
|
||||
elsif clave.include?(':')
|
||||
user_value = clave.split(':').last
|
||||
# Formato: <nodo>:<usuario>[:<tipo>]
|
||||
# Ej: "srv-ns8:rmonla" → usuario="rmonla"
|
||||
# "srv-ns8:rmonla:sudo" → usuario="rmonla" (no "sudo")
|
||||
partes = clave.split(':')
|
||||
if partes.size >= 2
|
||||
# El usuario es siempre la segunda parte (índice 1)
|
||||
user_value = partes[1]
|
||||
end
|
||||
else
|
||||
token_user = boveda.get("#{clave}_user")
|
||||
user_value = cripto.decrypt(token_user) if token_user
|
||||
@@ -366,6 +373,64 @@ def cmd_load(cripto, boveda, clave)
|
||||
$stderr.puts "#{C::GREEN}✓ Credenciales cargadas en variables USR y PASS (archivo temporal seguro).#{C::RESET}"
|
||||
end
|
||||
|
||||
# ─── Comando ssh (ejecutar comando remoto con password de bóveda) ─────
|
||||
# Uso: candados ssh <clave> <usuario@host> '<comando>'
|
||||
# - clave: <nodo>:<usuario> para SSH (busca automáticamente <nodo>:<usuario>:sudo para sudo)
|
||||
# - Si existe clave :sudo separada, la usa para comandos con sudo
|
||||
def cmd_ssh(cripto, boveda, clave, usuario_host, *cmd_args)
|
||||
abort "#{C::RED}✗ Falta nombre de clave.#{C::RESET}" if clave.nil?
|
||||
abort "#{C::RED}✗ Falta usuario@host.#{C::RESET}" if usuario_host.nil? || usuario_host.empty?
|
||||
abort "#{C::RED}✗ Falta comando a ejecutar.#{C::RESET}" if cmd_args.nil? || cmd_args.empty?
|
||||
|
||||
verificar_sesion!
|
||||
|
||||
# Obtener password SSH
|
||||
token_ssh = boveda.get(clave)
|
||||
abort "#{C::RED}✗ Clave '#{clave}' no encontrada.#{C::RESET}" if token_ssh.nil?
|
||||
ssh_pass = cripto.decrypt(token_ssh)
|
||||
safe_ssh_pass = ssh_pass.gsub("'", "'\\''")
|
||||
|
||||
# Buscar automáticamente clave :sudo si existe
|
||||
comando = cmd_args.join(' ')
|
||||
sudo_pass = ssh_pass
|
||||
|
||||
# Intentar buscar clave :sudo basada en la clave SSH
|
||||
if clave.include?(':') && !clave.end_with?(':sudo')
|
||||
# Construir clave sudo: <nodo>:<usuario>:sudo
|
||||
partes = clave.split(':')
|
||||
if partes.size >= 2
|
||||
clave_sudo = "#{partes[0]}:#{partes[1]}:sudo"
|
||||
token_sudo = boveda.get(clave_sudo)
|
||||
if token_sudo
|
||||
sudo_pass = cripto.decrypt(token_sudo)
|
||||
$stderr.puts "#{C::DIM}ℹ Usando clave sudo: #{clave_sudo}#{C::RESET}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
safe_sudo_pass = sudo_pass.gsub("'", "'\\''")
|
||||
|
||||
registrar_acceso('SSH', "Clave: #{clave}, Host: #{usuario_host}, Cmd: #{comando}")
|
||||
|
||||
# Escapar comando para bash remoto: reemplazar ' con '\''
|
||||
comando_escaped = comando.gsub("'", "'\\''")
|
||||
|
||||
# Crear script temporal con el comando y password para sudo
|
||||
require 'securerandom'
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p(TMP_DIR) unless File.exist?(TMP_DIR)
|
||||
script_path = File.join(TMP_DIR, ".ssh_sudo_#{SecureRandom.hex(4)}")
|
||||
File.write(script_path, "#!/bin/bash\nexport DEBIAN_FRONTEND=noninteractive\necho '#{safe_sudo_pass}' | sudo -S -p '' bash -c '#{comando_escaped}'\n")
|
||||
File.chmod(0700, script_path)
|
||||
|
||||
# Ejecutar: sshpass para SSH, luego script remoto que hace sudo
|
||||
result = system("sshpass -p '#{safe_ssh_pass}' ssh -o StrictHostKeyChecking=no -tt #{usuario_host} 'bash -s' < '#{script_path}'")
|
||||
FileUtils.rm_f(script_path)
|
||||
exit result ? 0 : 1
|
||||
end
|
||||
|
||||
# ─── Ayuda mejorada para cmd_help_resumida ─────────────────────────────
|
||||
|
||||
# ─── Comando sudo (primitiva reutilizable) ─────────────────────
|
||||
# Resolución inteligente de la clave sudo:
|
||||
# 1. <hostname>:<usuario>:sudo (convención específica)
|
||||
@@ -426,58 +491,130 @@ def cmd_sudo(cripto, boveda, comando_args)
|
||||
exit $?.exitstatus || 0
|
||||
end
|
||||
|
||||
def cmd_help
|
||||
def cmd_help_resumida
|
||||
puts <<~HELP
|
||||
#{C::CYAN}🔐 candados — Gestión segura de secretos#{C::RESET}
|
||||
#{C::CYAN}╔═══════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}candados — Acceso Seguro a Contraseñas (Sin Exponer)#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚═══════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
|
||||
#{C::GREEN}Uso:#{C::RESET} ruby adn/tools/seguridad/candados.rb <comando> [args]
|
||||
#{C::RED}⚠️ REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Comandos principales ━━━#{C::RESET}
|
||||
#{C::BOLD}run <clave> <cmd>#{C::RESET} #{C::GREEN}★ PRIMARIO#{C::RESET} Ejecutar comando con USR y PASS inyectados
|
||||
#{C::BOLD}sudo <comando>#{C::RESET} Ejecutar con privilegios (resuelve clave automáticamente)
|
||||
#{C::BOLD}load <clave>#{C::RESET} Cargar USR/PASS en shell actual (usar con eval)
|
||||
#{C::GREEN}┌─────────────────────────────────────────────────────────────────┐#{C::RESET}
|
||||
#{C::GREEN}│#{C::RESET} #{C::BOLD}MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES#{C::RESET} #{C::GREEN}│#{C::RESET}
|
||||
#{C::GREEN}└─────────────────────────────────────────────────────────────────┘#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Gestión de bóveda ━━━#{C::RESET}
|
||||
authorize Abrir candado (autorización temporal 30 min)
|
||||
cerrar Cerrar candado manualmente
|
||||
set <clave> Guardar secreto (input seguro, sin eco)
|
||||
get <clave> Obtener secreto (⚠ imprime a stdout)
|
||||
rm <clave> Eliminar secreto
|
||||
list Listar claves disponibles
|
||||
#{C::BOLD}★ run <clave> 'comando'#{C::RESET} → Ejecuta con $USR y $PASS inyectadas (PRIMARIO)
|
||||
#{C::BOLD}★ load <clave>#{C::RESET} → Carga $USR y $PASS en shell (usar con eval)
|
||||
|
||||
#{C::DIM}Ejemplo: ruby candados.rb run srv-ns8:rmonla 'sshpass -p $PASS ssh $USR@host'#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Comandos ━━━#{C::RESET}
|
||||
#{C::BOLD}run#{C::RESET} <clave> <cmd> #{C::DIM}Ejecutar con credenciales inyectadas#{C::RESET}
|
||||
#{C::BOLD}load#{C::RESET} <clave> #{C::DIM}Cargar en shell actual (eval)#{C::RESET}
|
||||
#{C::BOLD}sudo#{C::RESET} <comando> #{C::DIM}Ejecutar con privilegios#{C::RESET}
|
||||
#{C::BOLD}ssh#{C::RESET} <clave> <u@h> <cmd> #{C::DIM}SSH remoto con password de bóveda#{C::RESET}
|
||||
#{C::BOLD}authorize#{C::RESET} #{C::DIM}Abrir candado (30 min)#{C::RESET}
|
||||
#{C::BOLD}list#{C::RESET} #{C::DIM}Listar claves disponibles#{C::RESET}
|
||||
#{C::BOLD}set/rm#{C::RESET} <clave> #{C::DIM}Guardar/Eliminar secreto#{C::RESET}
|
||||
|
||||
#{C::DIM}Usá#{C::RESET} ruby candados.rb --help #{C::DIM}para ayuda completa y ejemplos detallados.#{C::RESET}
|
||||
HELP
|
||||
end
|
||||
|
||||
def cmd_help_completo
|
||||
puts <<~HELP
|
||||
#{C::CYAN}╔═══════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}candados — Acceso Seguro a Contraseñas (Sin Exponer)#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚═══════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
|
||||
#{C::RED}⚠️ REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET}
|
||||
Esta herramienta está diseñada para que las contraseñas #{C::BOLD}NUNCA#{C::RESET} se muestren en pantalla
|
||||
ni queden registradas en logs, históricos de shell o archivos temporales visibles.
|
||||
|
||||
#{C::GREEN}┌─────────────────────────────────────────────────────────────────┐#{C::RESET}
|
||||
#{C::GREEN}│#{C::RESET} #{C::BOLD}MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES#{C::RESET} #{C::GREEN}│#{C::RESET}
|
||||
#{C::GREEN}└─────────────────────────────────────────────────────────────────┘#{C::RESET}
|
||||
|
||||
#{C::BOLD}★ MÉTODO 1: run (PRIMARIO - Recomendado para comandos)#{C::RESET}
|
||||
Ejecuta un comando con USR y PASS inyectadas en su entorno.
|
||||
La contraseña #{C::BOLD}NUNCA#{C::RESET} se imprime ni queda en logs.
|
||||
|
||||
#{C::DIM}ruby candados.rb run <clave> 'comando que usa $USR y $PASS'#{C::RESET}
|
||||
|
||||
#{C::CYAN}Ejemplos:#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
#{C::GREEN}✓#{C::RESET} ruby candados.rb run srv-ns8:root 'ssh $USR@$HOSTNAME "comando"'
|
||||
|
||||
#{C::BOLD}★ MÉTODO 2: load (Para shell interactivo)#{C::RESET}
|
||||
Carga USR y PASS en variables de entorno del shell actual.
|
||||
Usa un archivo temporal (600) que se auto-elimina tras el source.
|
||||
|
||||
#{C::DIM}eval $(ruby candados.rb load <clave>)#{C::RESET}
|
||||
|
||||
#{C::CYAN}Ejemplo:#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
#{C::DIM} → Carga $USR y $PASS en tu shell actual#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} sshpass -p $PASS ssh $USR@servidor
|
||||
|
||||
#{C::YELLOW}━━━ Otros Comandos ━━━#{C::RESET}
|
||||
#{C::BOLD}sudo <comando>#{C::RESET} Ejecutar con sudo (resuelve contraseña automáticamente)
|
||||
#{C::BOLD}authorize#{C::RESET} Abrir candado (sesión de 30 min, requerida para operar)
|
||||
#{C::BOLD}cerrar#{C::RESET} Cerrar sesión manualmente antes de los 30 min
|
||||
#{C::BOLD}abrir#{C::RESET} Cargar clave SSH en ssh-add (usa passphrase de bóveda)
|
||||
|
||||
#{C::YELLOW}━━━ Gestión de Bóveda ━━━#{C::RESET}
|
||||
#{C::BOLD}set <clave>#{C::RESET} Guardar secreto (input seguro sin eco)
|
||||
#{C::BOLD}list#{C::RESET} Listar claves disponibles (no muestra valores)
|
||||
#{C::BOLD}rm <clave>#{C::RESET} Eliminar una clave de la bóveda
|
||||
|
||||
#{C::YELLOW}━━━ Utilidades ━━━#{C::RESET}
|
||||
abrir Autorizar SSH (cargar passphrase)
|
||||
encrypt <texto> Cifrar texto plano → token
|
||||
decrypt <token> Descifrar token → texto plano
|
||||
#{C::BOLD}encrypt <texto>#{C::RESET} Cifrar texto plano → token (para bóveda)
|
||||
#{C::BOLD}decrypt <token>#{C::RESET} Descifrar token → texto plano
|
||||
|
||||
#{C::YELLOW}Resolución automática de claves:#{C::RESET}
|
||||
#{C::DIM}• run/load: Detecta pares USR/PASS por convención:#{C::RESET}
|
||||
srv-dasu:rmonla → USR=rmonla, PASS=(valor cifrado)
|
||||
tailscale_dasuten → busca _user/_pass automáticamente
|
||||
#{C::DIM}• sudo: Resuelve la clave de sudo automáticamente:#{C::RESET}
|
||||
<hostname>:<usuario>:sudo (ej: ns8:rmonla:sudo)
|
||||
#{C::YELLOW}━━━ Resolución Automática de Claves ━━━#{C::RESET}
|
||||
#{C::DIM}• run/load con formato `servidor:usuario`:#{C::RESET}
|
||||
srv-dasu:rmonla → USR=rmonla, PASS=(descifrado de bóveda)
|
||||
#{C::DIM}• run/load con formato `prefijo_pass`:#{C::RESET}
|
||||
tailscale_dasuten_pass → busca automáticamente tailscale_dasuten_user
|
||||
#{C::DIM}• sudo resuelve la clave automáticamente:#{C::RESET}
|
||||
Busca: <hostname>:<usuario>:sudo → fallback: `sudo`
|
||||
|
||||
#{C::YELLOW}Ejemplos:#{C::RESET}
|
||||
#{C::DIM}# 1. Autorizar (dura 30 min)#{C::RESET}
|
||||
ruby candados.rb authorize
|
||||
#{C::YELLOW}━━━ Flujo de Trabajo Típico ━━━#{C::RESET}
|
||||
#{C::DIM}1. Autorizar (una vez cada 30 min):#{C::RESET}
|
||||
ruby candados.rb authorize
|
||||
|
||||
#{C::GREEN}# 2. Ejecutar comando con credenciales (RECOMENDADO)#{C::RESET}
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
#{C::DIM}2. Ejecutar comandos (Método recomendado):#{C::RESET}
|
||||
ruby candados.rb run srv-ns8:rmonla 'sshpass -p $PASS ssh $USR@host "comando"'
|
||||
|
||||
#{C::DIM}# 3. Ejecutar con sudo#{C::RESET}
|
||||
ruby candados.rb sudo tailscale set --operator=$USER
|
||||
#{C::DIM}3. O cargar en shell interactivo:#{C::RESET}
|
||||
eval $(ruby candados.rb load srv-ns8:rmonla)
|
||||
sshpass -p $PASS ssh $USR@host
|
||||
|
||||
#{C::DIM}# 4. Cargar en shell interactivo (secundario)#{C::RESET}
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
#{C::RED}⚠️ ADVERTENCIAS DE SEGURIDAD:#{C::RESET}
|
||||
#{C::BOLD}• NUNCA usar `get`#{C::RESET} en automatizaciones → imprime la contraseña a stdout
|
||||
#{C::BOLD}• `run` y `sudo`#{C::RESET} → las credenciales no dejan rastro en logs
|
||||
#{C::BOLD}• `load`#{C::RESET} → archivo temporal con permisos 600, auto-borrado
|
||||
#{C::BOLD}• Siempre ejecutar `authorize`#{C::RESET} antes de operar (sesión de 30 min)
|
||||
|
||||
#{C::RED}⚠ Seguridad:#{C::RESET}
|
||||
#{C::DIM}• run/sudo: Las credenciales NUNCA se imprimen en pantalla ni logs.#{C::RESET}
|
||||
#{C::DIM}• load: Usa archivo temporal (600) con autoborrado, nada visible.#{C::RESET}
|
||||
#{C::DIM}• get: ⚠ Imprime el valor a stdout. Usar solo si es necesario.#{C::RESET}
|
||||
#{C::DIM}Documentación completa: adn/tools/candados/README.md#{C::RESET}
|
||||
HELP
|
||||
end
|
||||
|
||||
alias cmd_help cmd_help_completo
|
||||
|
||||
# ─── Recordatorio de Seguridad (solo en ayuda) ──────────────────
|
||||
def mostrar_recordatorio_seguridad
|
||||
puts <<~RECORDATORIO
|
||||
#{C::CYAN}╔════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} #{C::GREEN}✓ CORRECTO:#{C::RESET} ruby candados.rb run <clave> '<cmd>' #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} #{C::RED}✗ INCORRECTO:#{C::RESET} ruby candados.rb get <clave> #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} → `run` inyecta USR/PASS sin mostrar. `get` imprime y deja rastro. #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
RECORDATORIO
|
||||
puts
|
||||
end
|
||||
|
||||
# ─── Main ────────────────────────────────────────────────────────────
|
||||
if __FILE__ == $0
|
||||
comando = ARGV[0]
|
||||
@@ -496,6 +633,10 @@ if __FILE__ == $0
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
cmd_sudo(cripto, boveda, ARGV[1..])
|
||||
when 'ssh'
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
cmd_ssh(cripto, boveda, ARGV[1], ARGV[2], *ARGV[3..])
|
||||
when 'encrypt', 'decrypt', 'get', 'load', 'run', 'set', 'rm', 'list'
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
@@ -509,11 +650,14 @@ if __FILE__ == $0
|
||||
when 'rm' then cmd_rm(boveda, arg)
|
||||
when 'list' then cmd_list(boveda)
|
||||
end
|
||||
when 'help', 'ayuda', '-h', '--help', nil
|
||||
cmd_help
|
||||
when '--help', '-h'
|
||||
cmd_help_completo
|
||||
when 'help', 'ayuda', nil
|
||||
cmd_help_resumida
|
||||
else
|
||||
$stderr.puts "#{C::RED}✗ Comando desconocido: '#{comando}'#{C::RESET}"
|
||||
cmd_help
|
||||
$stderr.puts ""
|
||||
cmd_help_resumida
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user