[P2606] WebServer NGINX: plan actualizado, ficha nodo, HTTPS operativo

- P2606: Plan v1.1 EN EJECUCIÓN. Fases 1-3 completadas.
- Ficha nodo srvv-nginx-rm.md. P2606 en adn/07_proyectos.md.
- Incluye cambios acumulados de sesiones anteriores.
This commit is contained in:
Ricardo Monla
2026-03-28 12:18:42 -03:00
parent 97d9b777b4
commit 160da47f96
78 changed files with 6461 additions and 624 deletions
+3 -1
View File
@@ -8,5 +8,7 @@
"dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==",
"ns8:rmonla:sudo": "eyJpdiI6IkRqbTBhVlZSYUpVdno3TFkiLCJ0YWciOiJJSnE2ei8zZHIrV2hFQks1VjNpVGN3PT0iLCJkYXRhIjoiT3JxTjRhTmlQK2R3TlJZZk9BPT0ifQ==",
"srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=",
"srv-ns8:rmonla:sudo": "eyJpdiI6InJDOUp2STdYR1crb2NkMTgiLCJ0YWciOiJHSUZVbXM5VnVmZ3d1NXQ3SFVwOUdRPT0iLCJkYXRhIjoib1ZVTzdaLzk1WGdwZDhKREM3aWpnV3c9In0="
"srv-ns8:rmonla:sudo": "eyJpdiI6InJDOUp2STdYR1crb2NkMTgiLCJ0YWciOiJHSUZVbXM5VnVmZ3d1NXQ3SFVwOUdRPT0iLCJkYXRhIjoib1ZVTzdaLzk1WGdwZDhKREM3aWpnV3c9In0=",
"dasu-sql2:Administrator": "eyJpdiI6IlJ0VWJzblVGM2NhWEtvNlgiLCJ0YWciOiJtM0llb2luUmRUZng0NnBsL1JtKzl3PT0iLCJkYXRhIjoiSHVhaklOTFlsSm1PbDJkTjNoYlF0UT09In0=",
"srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ=="
}
+1 -1
View File
@@ -1 +1 @@
1773874654
1774708556
+3 -1
View File
@@ -58,7 +58,9 @@ 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.
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
+40 -11
View File
@@ -38,11 +38,13 @@ require 'fileutils'
# ─── Rutas ───────────────────────────────────────────────────────────
TOOL_DIR = File.expand_path(__dir__)
PROJECT_ROOT = File.expand_path('../../..', TOOL_DIR) # Desde adn/tools/candados -> adn/tools -> adn -> root
MASTER_KEY = File.join(TOOL_DIR, '.master.key')
OLD_KEY = File.expand_path('../../.master.key', TOOL_DIR)
BOVEDA = File.join(TOOL_DIR, '.boveda.json')
SESION = File.join(TOOL_DIR, '.session')
ACCESS_LOG = File.join(TOOL_DIR, 'access.log')
TMP_DIR = File.join(PROJECT_ROOT, 'tmp') # Directorio para temporales (principio 11 - 05_ia.md)
# ─── Transición de Seguridad ─────────────────────────────────────────
if File.exist?(OLD_KEY) && !File.exist?(MASTER_KEY)
@@ -175,20 +177,41 @@ end
# ─── Comandos ────────────────────────────────────────────────────────
def cmd_abrir
puts "#{C::CYAN}🔐 candados: Autorización SSH#{C::RESET}"
puts "#{C::DIM}Limpiando identidades previas...#{C::RESET}"
def cmd_abrir(cripto, boveda)
verificar_sesion!
token = boveda.get('rsa')
abort "#{C::RED}✗ Clave 'rsa' no encontrada en la bóveda. Guardala con: ruby candados.rb set rsa#{C::RESET}" if token.nil?
passphrase = cripto.decrypt(token)
registrar_acceso('ABRIR', 'SSH key load via ssh-add')
$stderr.puts "#{C::CYAN}🔐 candados: Cargando clave SSH...#{C::RESET}"
system('ssh-add -D > /dev/null 2>&1')
puts "#{C::YELLOW}🔑 Ingresá tu passphrase:#{C::RESET}"
system('ssh-add')
# Crear script temporal para SSH_ASKPASS (no interactivo)
require 'securerandom'
FileUtils.mkdir_p(TMP_DIR) unless File.exist?(TMP_DIR)
askpass_path = File.join(TMP_DIR, ".ssh_askpass_#{SecureRandom.hex(4)}")
safe_pass = passphrase.gsub("'", "'\\''")
File.write(askpass_path, "#!/bin/sh\necho '#{safe_pass}'\n")
File.chmod(0700, askpass_path)
system('ssh-add -l > /dev/null 2>&1')
unless $?.success?
abort "#{C::RED}✗ Sin identidades SSH cargadas. Abortando.#{C::RESET}"
# Cargar clave via SSH_ASKPASS
ENV['SSH_ASKPASS'] = askpass_path
ENV['SSH_ASKPASS_REQUIRE'] = 'force'
ENV['DISPLAY'] = ENV['DISPLAY'] || ':0'
system('ssh-add < /dev/null')
# Limpiar
FileUtils.rm_f(askpass_path)
ENV.delete('SSH_ASKPASS')
ENV.delete('SSH_ASKPASS_REQUIRE')
if system('ssh-add -l > /dev/null 2>&1')
$stderr.puts "#{C::GREEN}🔓 SSH autorizado. Identidades cargadas en el agente.#{C::RESET}"
else
abort "#{C::RED}✗ Sin identidades SSH cargadas. Verificá la passphrase en 'rsa'.#{C::RESET}"
end
puts "#{C::GREEN}🔓 SSH autorizado. Las identidades permanecen activas mientras dure la sesión del agente.#{C::RESET}"
end
def cmd_encrypt(cripto, texto)
@@ -326,8 +349,10 @@ def cmd_load(cripto, boveda, clave)
# Seguridad: NUNCA imprimir secretos a stdout/stderr.
# Escribir exports en un archivo temporal con permisos estrictos (600).
# A stdout solo emitimos el comando `source` + autoborrado.
# NOTA: Usamos tmp/ del proyecto (no /tmp/) para cumplir principio 11 de 05_ia.md
require 'securerandom'
tmpfile_path = "/tmp/.candados_env_#{SecureRandom.hex(8)}"
FileUtils.mkdir_p(TMP_DIR) unless File.exist?(TMP_DIR)
tmpfile_path = File.join(TMP_DIR, ".candados_env_#{SecureRandom.hex(8)}")
safe_user = user_value.gsub("'", "'\\''")
safe_pass = pass_value.gsub("'", "'\\''")
@@ -463,6 +488,10 @@ if __FILE__ == $0
autorizar!
when 'cerrar'
cerrar_sesion!
when 'abrir'
cripto = Cripto.new(cargar_master_key)
boveda = Boveda.new(BOVEDA)
cmd_abrir(cripto, boveda)
when 'sudo'
cripto = Cripto.new(cargar_master_key)
boveda = Boveda.new(BOVEDA)