diff --git a/README.md b/README.md index 82ee10b..55c8372 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ bin/update.sh | Comando | Descripción | |---------|-------------| +| `bin/qr_secret.sh` | Extrae secretos de imágenes QR y los guarda encriptados con SOPS | | `bin/vpn_install.sh` | Instala el cliente Pritunl VPN | | `bin/vpn_install.sh --update` | Actualiza el cliente VPN a la última versión | | `bin/vpn_users.sh` | Gestiona usuarios de la VPN | diff --git a/bin/qr_secret.sh b/bin/qr_secret.sh new file mode 100755 index 0000000..368c254 --- /dev/null +++ b/bin/qr_secret.sh @@ -0,0 +1,249 @@ +#!/bin/bash +# +# Script: qr_secret.sh +# Description: Extrae una contraseña secreta de una imagen QR y la guarda encriptada con SOPS +# Modified: 2025/03/15 19:30:00 +# [Author] Cortana Rosero One +# [Generated] Created by Claude Code (claude-3-7-sonnet-20250219) +# +# Derechos de Autor (C) [2025] [Mauro Rosero P. ] +# +# Este programa es software libre: usted puede redistribuirlo y/o modificarlo +# bajo los términos de la Licencia Pública Affero General de GNU tal como +# lo publica la Free Software Foundation, ya sea la versión 3 de la licencia, +# o (a su elección) cualquier versión posterior. +# +# Este programa se distribuye con la esperanza de que sea útil, +# pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de +# COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Consulte la +# Licencia Pública Affero General de GNU para obtener más detalles. +# +# Debería haber recibido una copia de la Licencia Pública Affero General +# junto con este programa. Si no la recibió, consulte . + +# Configuración inicial +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Leer DEVSPATH desde el archivo de configuración o usar "devs" por defecto +if [ -f "$SCRIPT_DIR/config/devspath.dat" ]; then + DEVSPATH=$(cat "$SCRIPT_DIR/config/devspath.dat") +else + DEVSPATH="devs" +fi + +BIN_HOME="$HOME/$DEVSPATH" +BIN_BASE="bin" +BIN_LIBS="lib" +BIN_MESG="msg" + +# CHECK SHELL LANGUAGE +BIN_LANG=${LANG:0:2} + +# Importar bibliotecas necesarias +source "${BIN_HOME}/${BIN_BASE}/${BIN_LIBS}/base.lib" +source "${BIN_HOME}/${BIN_BASE}/${BIN_LIBS}/console.lib" + +# Cargar mensajes en el idioma del sistema o español por defecto +load_messages "${BIN_HOME}/${BIN_BASE}" "${BIN_MESG}" "${BIN_LANG}" "head" + +# Variables globales +title="${head_000} ${head_002}" +apps_title="Extractor de Secretos QR" + +# Verificar dependencias +check_dependencies() { + # Verificar si dialog está instalado + if ! command -v dialog &> /dev/null; then + echo "Error: dialog no está instalado. Por favor, ejecute bin/bootstrap.sh para instalarlo." + exit 1 + fi + + # Verificar si zbar está instalado + if ! command -v zbarimg &> /dev/null; then + echo "Error: zbar no está instalado. Por favor, ejecute bin/bootstrap.sh para instalarlo." + exit 1 + fi + + # Verificar si sops está instalado + if ! command -v sops &> /dev/null; then + echo "Error: SOPS no está instalado. Por favor, ejecute bin/bootstrap.sh para instalarlo." + exit 1 + fi +} + +# Validar formato del nombre +validate_name() { + local name="$1" + + # Verificar si el nombre está vacío + if [ -z "$name" ]; then + return 1 + fi + + # Verificar si el nombre contiene espacios + if [[ "$name" =~ [[:space:]] ]]; then + return 2 + fi + + # Verificar si el nombre contiene caracteres no permitidos + if ! [[ "$name" =~ ^[a-zA-Z0-9._-]+$ ]]; then + return 3 + fi + + return 0 +} + +# Solicitar nombre del archivo +get_output_name() { + local name="" + local valid=false + + while [ "$valid" != "true" ]; do + # Solicitar nombre + dialog_input_box "Nombre del archivo" "Ingrese un nombre para el archivo (solo letras, números, ., - y _)" "" + + if [ $codex -ne 0 ]; then + # Usuario canceló + return 1 + fi + + name="$value" + + # Validar nombre + validate_name "$name" + local validation_result=$? + + case $validation_result in + 0) + valid="true" + ;; + 1) + dialog_error_box "Error" "El nombre no puede estar vacío." + ;; + 2) + dialog_error_box "Error" "El nombre no puede contener espacios." + ;; + 3) + dialog_error_box "Error" "El nombre solo puede contener letras, números, ., - y _." + ;; + esac + done + + echo "$name" + return 0 +} + +# Seleccionar archivo QR +select_qr_file() { + # Usar dialog_input_filepath para seleccionar un archivo + file_path="$HOME" + dialog_input_filepath "$file_path" "Seleccione una imagen QR" + + if [ "$valid_file" == "2" ]; then + # Usuario canceló + return 1 + fi + + echo "$file_path" + return 0 +} + +# Extraer secreto de imagen QR +extract_secret() { + local qr_file="$1" + + # Usar zbarimg para extraer el texto del QR + local secret=$(zbarimg --quiet --raw "$qr_file") + local result=$? + + if [ $result -ne 0 ] || [ -z "$secret" ]; then + return 1 + fi + + # Si el secreto comienza con "otpauth://", extraer solo el secreto + if [[ "$secret" == otpauth://* ]]; then + # Extraer el valor después de "secret=" + secret=$(echo "$secret" | grep -oP 'secret=\K[A-Z0-9]+') + + # Si no se pudo extraer el secreto, mostrar error + if [ -z "$secret" ]; then + return 2 + fi + fi + + echo "$secret" + return 0 +} + +# Guardar secreto encriptado con SOPS +save_encrypted_secret() { + local name="$1" + local secret="$2" + local output_file="${DEVELOPER_DIR}/${name}.sops.yaml" + + # Crear archivo YAML temporal con el secreto + local temp_file=$(mktemp) + echo "secret: $secret" > "$temp_file" + + # Encriptar el archivo con SOPS + sops --encrypt "$temp_file" > "$output_file" + local result=$? + + # Eliminar archivo temporal + rm -f "$temp_file" + + # Establecer permisos adecuados + if [ $result -eq 0 ]; then + chmod 600 "$output_file" + return 0 + else + return 1 + fi +} + +# Función principal +main() { + # Verificar dependencias + check_dependencies + + # Solicitar nombre del archivo + local output_name=$(get_output_name) + if [ $? -ne 0 ]; then + clear + echo "Operación cancelada." + exit 0 + fi + + # Seleccionar archivo QR + local qr_file=$(select_qr_file) + if [ $? -ne 0 ]; then + clear + echo "Operación cancelada." + exit 0 + fi + + # Extraer secreto de la imagen QR + local secret=$(extract_secret "$qr_file") + local extract_status=$? + + if [ $extract_status -ne 0 ]; then + dialog_error_box "Error" "No se pudo extraer el secreto de la imagen QR seleccionada." + clear + exit 1 + fi + + # Guardar secreto encriptado + save_encrypted_secret "$output_name" "$secret" + if [ $? -eq 0 ]; then + dialog_error_box "Éxito" "Secreto guardado correctamente en ${DEVELOPER_DIR}/${output_name}.sops.yaml" + else + dialog_error_box "Error" "No se pudo guardar el secreto encriptado." + clear + exit 1 + fi + + clear +} + +# Ejecutar función principal +main \ No newline at end of file