[Seguridad] Consolidar scripts de bóveda en scripts/seguridad/ + documentar mecanismo candado.rb en ADN
This commit is contained in:
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env ruby
|
||||
# script: candado.rb
|
||||
# Uso: ruby scripts/candado.rb [minutos]
|
||||
# Descripción: Genera un token de autorización temporal para que el Agente pueda ejecutar tareas críticas.
|
||||
# Requiere que el usuario tenga claves SSH cargadas (prueba de identidad).
|
||||
|
||||
require 'json'
|
||||
require 'fileutils'
|
||||
|
||||
TOKEN_FILE = File.expand_path('../../.agent_token', __dir__)
|
||||
DEFAULT_DURATION_MINS = 5
|
||||
|
||||
# 1. Validación de Identidad (Proof of User Presence)
|
||||
puts "🔐 Requerimiento de Protocolo: Autorización FRESCA."
|
||||
puts "🧹 Limpiando identidades previas en el agente SSH para exigir validación..."
|
||||
system("ssh-add -D > /dev/null 2>&1") # Borra claves cacheadas para forzar el prompt
|
||||
|
||||
puts "🔑 Por favor, ingresa tu passphrase para autorizar al Agente:"
|
||||
# Intentamos cargar las claves interactivamente (ahora sí pedirá pass porque borramos las anteriores)
|
||||
system("ssh-add")
|
||||
|
||||
# Verificamos que la carga haya sido exitosa
|
||||
system("ssh-add -l > /dev/null 2>&1")
|
||||
unless $?.success?
|
||||
puts "❌ ERROR: No se detectaron identidades SSH."
|
||||
puts " Debes completar el desafío de identidad (passphrase) para continuar."
|
||||
exit 1
|
||||
end
|
||||
|
||||
# 2. Generación del Token
|
||||
duration = (ARGV[0] || DEFAULT_DURATION_MINS).to_i
|
||||
expires_at = Time.now + (duration * 60)
|
||||
|
||||
token_data = {
|
||||
created_at: Time.now.to_s,
|
||||
expires_at: expires_at.to_s,
|
||||
duration_mins: duration,
|
||||
authorized_by: ENV['USER'] || 'unknown',
|
||||
signature: rand(36**8).to_s(36) # Firma aleatoria simple
|
||||
}
|
||||
|
||||
File.write(TOKEN_FILE, JSON.pretty_generate(token_data))
|
||||
FileUtils.chmod(0600, TOKEN_FILE) # Solo lectura para el usuario
|
||||
|
||||
puts "\n🔓 VACANCIA DE SEGURIDAD ACTIVADA"
|
||||
puts "--------------------------------"
|
||||
puts "✅ Agente AUTORIZADO para operaciones críticas."
|
||||
puts "⏳ Ventana de tiempo: #{duration} minutos."
|
||||
puts "💀 Expira: #{expires_at.strftime('%H:%M:%S')}"
|
||||
puts "🔑 Token: #{TOKEN_FILE}"
|
||||
puts "\n>> Ahora puedes pedirle al Agente que ejecute sus tareas."
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'json'
|
||||
|
||||
class SecretBox
|
||||
ALGORITHM = 'aes-256-gcm'.freeze
|
||||
|
||||
def initialize(master_key_content)
|
||||
# Generar una clave de 32 bytes usando SHA256 sobre el contenido del master_key
|
||||
@key = Digest::SHA256.digest(master_key_content.strip)
|
||||
end
|
||||
|
||||
def encrypt(plaintext)
|
||||
cipher = OpenSSL::Cipher.new(ALGORITHM)
|
||||
cipher.encrypt
|
||||
cipher.key = @key
|
||||
iv = cipher.random_iv
|
||||
|
||||
ciphertext = cipher.update(plaintext.to_s) + cipher.final
|
||||
auth_tag = cipher.auth_tag
|
||||
|
||||
# Base64 encodear las 3 partes para un JSON
|
||||
payload = {
|
||||
iv: Base64.strict_encode64(iv),
|
||||
tag: Base64.strict_encode64(auth_tag),
|
||||
data: Base64.strict_encode64(ciphertext)
|
||||
}
|
||||
|
||||
Base64.strict_encode64(payload.to_json)
|
||||
end
|
||||
|
||||
def decrypt(encrypted_payload)
|
||||
parsed_payload = JSON.parse(Base64.strict_decode64(encrypted_payload), symbolize_names: true)
|
||||
|
||||
cipher = OpenSSL::Cipher.new(ALGORITHM)
|
||||
cipher.decrypt
|
||||
cipher.key = @key
|
||||
cipher.iv = Base64.strict_decode64(parsed_payload[:iv])
|
||||
cipher.auth_tag = Base64.strict_decode64(parsed_payload[:tag])
|
||||
|
||||
cipher.update(Base64.strict_decode64(parsed_payload[:data])) + cipher.final
|
||||
rescue => e
|
||||
raise "Error al desencriptar. Clave incorrecta o payload dañado. Detalles: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
action = ARGV[0]
|
||||
|
||||
unless ['encrypt', 'decrypt'].include?(action)
|
||||
puts "Uso:"
|
||||
puts " ruby #{$0} encrypt <texto_plano>"
|
||||
puts " ruby #{$0} decrypt <texto_encriptado>"
|
||||
exit 1
|
||||
end
|
||||
|
||||
text = ARGV[1]
|
||||
if text.nil? || text.empty?
|
||||
puts "Por favor, proporciona el texto a #{action}."
|
||||
puts "Ejemplo:"
|
||||
puts " ruby #{$0} encrypt \"MiSuperPassword\""
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Buscar el archivo .master.key en el root del proyecto
|
||||
key_path = File.join(File.expand_path('../..', __dir__), '.master.key')
|
||||
|
||||
unless File.exist?(key_path)
|
||||
require 'securerandom'
|
||||
File.write(key_path, SecureRandom.hex(32))
|
||||
puts "⚠️ ATENCIÓN: Se ha creado una nueva clave maestra en '#{key_path}'"
|
||||
puts " Asegúrate de NO subir este archivo a ningún repositorio (ya está cubierto por **/*.key en .gitignore)."
|
||||
puts " Cualquiera que tenga este archivo podrá desencriptar los textos.\n\n"
|
||||
end
|
||||
|
||||
box = SecretBox.new(File.read(key_path))
|
||||
|
||||
begin
|
||||
if action == 'encrypt'
|
||||
puts box.encrypt(text)
|
||||
else
|
||||
puts box.decrypt(text)
|
||||
end
|
||||
rescue => e
|
||||
puts "Error: #{e.message}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user