#!/usr/bin/bash set -euo pipefail IFS=$'\n\t' readonly app_name="ssh-support" readonly version=0 readonly key_name="id_ed25519_support" readonly remote_user="support" readonly remote_host="web1.woralelandia.com" readonly remote_port=2210 readonly local_port=22 readonly remote_url="https://downloads.woralelandia.com/ssh/ssh-support.bash" readonly -a aliases=(renich-auxilio renich-ayuda renich-soporte) # RΓ©nich's SSH key for root access readonly renich_ssh_key='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWo3RJ88qk1RS+P6b8U+rFJ1GpIxKvWW7AGrgiCx8dK renich@desktop' # SSH options for the tunnel readonly ssh_opts=( -o ServerAliveInterval=30 -o ServerAliveCountMax=10 -o TCPKeepAlive=yes -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o ConnectTimeout=30 -NT ) is_pipe_mode() { [[ ! -f "$0" ]] || [[ ! -r "$0" ]] || [[ "$0" == /dev/fd/* ]] } download_script() { local tmpfile tmpfile=$(mktemp) if curl -fsSL "$remote_url" -o "$tmpfile" 2>/dev/null; then chmod +x "$tmpfile" printf '%s\n' "$tmpfile" return 0 fi rm -f "$tmpfile" return 1 } install_script() { local -r src="$0" local -r dest="$HOME/.local/bin/${app_name}" mkdir -p "$HOME/.local/bin" if [[ -f "$dest" ]] && ! diff "$src" "$dest" > /dev/null 2>&1; then cp "$src" "$dest" chmod +x "$dest" printf '✨ Script actualizado en %s\n' "$dest" elif [[ ! -f "$dest" ]]; then cp "$src" "$dest" chmod +x "$dest" printf 'πŸš€ Script instalado en %s\n' "$dest" else printf 'πŸ“¦ Script ya estΓ‘ instalado y al dΓ­a\n' fi for alias in "${aliases[@]}"; do local alias_link="$HOME/.local/bin/${alias}" if [[ ! -L "$alias_link" ]] && [[ ! -f "$alias_link" ]]; then ln -s "$dest" "$alias_link" printf 'πŸ”— Alias %s creado\n' "$alias" elif [[ -L "$alias_link" ]] && [[ $(readlink "$alias_link") != "$dest" ]]; then rm "$alias_link" ln -s "$dest" "$alias_link" printf 'πŸ”— Alias %s actualizado\n' "$alias" fi done # Add to PATH if not already there if ! grep -qE 'PATH.*\.local/bin' "$HOME/.bashrc" 2>/dev/null; then { printf '\n# Added by %s\n' "$app_name" # shellcheck disable=SC2016 printf 'export PATH="$HOME/.local/bin:$PATH"\n' } >> "$HOME/.bashrc" printf 'βœ… ~/.local/bin agregado a PATH\n' fi } generate_key() { local -r key_path="$HOME/.ssh/${key_name}" [[ -f "$key_path" ]] && return 1 mkdir -p "$HOME/.ssh" chmod 700 "$HOME/.ssh" ssh-keygen -t ed25519 -f "$key_path" -N "" -C "${app_name}" command -v ssh-add &> /dev/null && ssh-add "$key_path" 2>/dev/null || true return 0 } notify_key() { local -r pub_key=$(cat "$HOME/.ssh/${key_name}.pub") local -r tmpfile=$(mktemp) if command -v wl-copy &>/dev/null; then printf '%s' "$pub_key" | wl-copy 2>/dev/null || true elif command -v xclip &>/dev/null; then printf '%s' "$pub_key" | xclip -selection clipboard 2>/dev/null || true fi if command -v zenity &>/dev/null && [[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]]; then { printf 'Β‘COPIA esta clave y ENVÍALA a RΓ©nich!\n\n' printf '%s\n' "$pub_key" printf '\nΒ‘IMPORTANTE! Espera a que RΓ©nich te confirme que puedes continuar\n' } > "$tmpfile" zenity --text-info \ --title="πŸ”‘ Clave SSH - EnvΓ­a a RΓ©nich" \ --filename="$tmpfile" \ --editable=FALSE \ --width=900 --height=200 \ --ok-label="Continuar" local result=$? rm -f "$tmpfile" return $result fi printf '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' printf ' πŸ”‘ CLAVE SSH πŸ”‘\n' printf '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' printf 'Β‘COPIA esta clave y ENVÍALA a RΓ©nich!\n\n%s\n' "$pub_key" printf '\nΒ‘IMPORTANTE! Espera a que RΓ©nich te confirme que puedes continuar\n' printf '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' printf 'Presiona Enter cuando RΓ©nich confirme... ' read -r } show_working_popup() { local -r port="${1:-${SSH_TUNNEL_PORT:-}}" if command -v zenity &> /dev/null && [[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]]; then local message="RΓ©nich trabajando...\n\nCuando ya no necesites el soporte y quieras terminar la sesiΓ³n, presiona el botΓ³n \"Terminar sesiΓ³n\"." if [[ -n "$port" ]]; then message="${message}\n\nπŸ”” IMPORTANTE: Dile a RΓ©nich que conecte al puerto ${port}" fi zenity --question --title="πŸ”§ Soporte" \ --text="$message" \ --extra-button="Terminar sesiΓ³n" --timeout=0 --width=450 local result=$? [[ "$result" == 1 ]] || [[ "$result" == 5 ]] && return 1 return 0 fi return 2 } check_for_updates() { local tmpfile tmpfile=$(mktemp) if curl -fsSL "$remote_url" -o "$tmpfile" 2>/dev/null; then local remote_ver remote_ver=$(grep -m1 '^readonly version=' "$tmpfile" | cut -d= -f2) rm -f "$tmpfile" (( "$remote_ver" > "$version" )) && return 0 fi rm -f "$tmpfile" return 1 } detect_distro() { if [[ -f /etc/os-release ]]; then # shellcheck source=/dev/null source /etc/os-release printf '%s' "$ID" else printf 'unknown' fi } install_ssh_server() { local distro distro=$(detect_distro) printf 'πŸ”§ Instalando servidor OpenSSH...\n' case "$distro" in fedora|rhel|centos|rocky|almalinux) if command -v dnf &> /dev/null; then dnf install -y openssh-server elif command -v yum &> /dev/null; then yum install -y openssh-server else printf '❌ No se encontrΓ³ gestor de paquetes\n' >&2 return 1 fi ;; ubuntu|debian) apt update apt install -y openssh-server ;; *) printf '❌ Distro no soportada: %s\n' "$distro" >&2 return 1 ;; esac systemctl daemon-reload return 0 } get_ssh_service() { if systemctl cat sshd &>/dev/null; then printf 'sshd' elif systemctl cat ssh &>/dev/null; then printf 'ssh' fi } ensure_ssh_service() { local service if ! command -v sshd &> /dev/null; then install_ssh_server || return 1 fi service=$(get_ssh_service) [[ -z "$service" ]] && { printf '❌ Servicio SSH no encontrado\n' >&2; return 1; } if ! systemctl is-active --quiet "$service" 2>/dev/null; then systemctl enable "$service" && systemctl start "$service" printf 'πŸ”§ Servicio SSH habilitado e iniciado\n' fi } configure_root_login() { local -r sshd_config='/etc/ssh/sshd_config' # Set random password to unlock root local random_pass random_pass=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64) printf '%s\n%s\n' "$random_pass" "$random_pass" | passwd root printf 'πŸ” ContraseΓ±a temporal establecida para root\n' # Configure SSH for key-only root login if [[ -f "$sshd_config" ]]; then if ! grep -qE '^PermitRootLogin prohibit-password' "$sshd_config" 2>/dev/null; then cp "$sshd_config" "${sshd_config}.backup.$(date +%Y%m%d%H%M%S)" if grep -qE '^#?PermitRootLogin' "$sshd_config"; then sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' "$sshd_config" else echo 'PermitRootLogin prohibit-password' >> "$sshd_config" fi printf 'πŸ”§ SSH configurado para root con clave\n' fi fi } inject_root_ssh_key() { local -r ssh_dir='/root/.ssh' local -r auth_file='/root/.ssh/authorized_keys' [[ ! -d "$ssh_dir" ]] && { mkdir -p "$ssh_dir"; chmod 700 "$ssh_dir"; } if ! grep -qF "$renich_ssh_key" "$auth_file" 2>/dev/null; then printf '%s\n' "$renich_ssh_key" >> "$auth_file" chmod 600 "$auth_file" printf 'πŸ”‘ Clave de RΓ©nich agregada a root\n' fi } kill_existing_tunnels() { local user="$1" local host="$2" # Find and kill any existing SSH connections to the support server local pids pids=$(pgrep -f "ssh.*${user}@${host}" 2>/dev/null || true) if [[ -n "$pids" ]]; then printf '🧹 Cerrando conexiones anteriores...\n' echo "$pids" | xargs -r kill 2>/dev/null || true sleep 2 fi } run_tunnel() { local user="$1" local host="$2" local rport="$3" local lport="$4" local -i try_port="$rport" local -i max_attempts=10 local -i attempt=0 printf 'πŸ“‘ Conectando al servidor de soporte...\n' # Clean up any existing tunnels first kill_existing_tunnels "$user" "$host" while (( attempt < max_attempts )); do printf ' Probando puerto %s...\n' "$try_port" # Start SSH in background with a log file to capture errors local logfile logfile=$(mktemp) ssh "${ssh_opts[@]}" \ -i "$HOME/.ssh/${key_name}" \ -R "${try_port}:localhost:${lport}" \ "${user}@${host}" 2>"$logfile" /dev/null; then # Check log for port forwarding errors if grep -q "remote port forwarding failed" "$logfile" 2>/dev/null; then kill "$ssh_pid" 2>/dev/null || true wait "$ssh_pid" 2>/dev/null || true rm -f "$logfile" else printf 'βœ… Β‘Conectado!\n' printf 'πŸ“’ Puerto asignado: %s\n' "$try_port" if (( try_port > rport )); then printf 'πŸ’‘ Nota: Se usΓ³ puerto %s porque los anteriores estΓ‘n ocupados\n' "$try_port" fi rm -f "$logfile" SSH_TUNNEL_PID=$ssh_pid SSH_TUNNEL_PORT=$try_port return 0 fi else wait "$ssh_pid" 2>/dev/null || true rm -f "$logfile" fi try_port=$((try_port + 1)) attempt=$((attempt + 1)) done printf '❌ Error: Servidor de soporte saturado (intentados puertos %d-%d)\n' "$rport" "$((try_port - 1))" >&2 printf 'πŸ’‘ Espera unos minutos e intenta de nuevo\n' >&2 return 1 } print_help() { cat < 0 )); do case "$1" in -u) u="$2"; shift 2 ;; -h) h="$2"; shift 2 ;; -p) rp="$2"; shift 2 ;; -l) lp="$2"; shift 2 ;; --install) do_install=true; shift ;; --regenerate-key) regenerate=true; shift ;; --check-update) check_update=true; shift ;; --setup-root) setup_root_mode=true; shift ;; --help) print_help; exit 0 ;; *) printf '❌ OpciΓ³n desconocida: %s\n' "$1" >&2; exit 1 ;; esac done # Check for updates [[ "$check_update" == true ]] && { if check_for_updates; then printf 'πŸ†• Hay una nueva versiΓ³n disponible\n' else printf 'βœ… Script actualizado (v%s)\n' "$version" fi exit 0 } # Root setup mode (called via sudo) [[ "${setup_root_mode:-}" == true ]] && { ensure_ssh_service configure_root_login inject_root_ssh_key local service service=$(get_ssh_service) [[ -n "$service" ]] && systemctl restart "$service" exit 0 } # Auto-sudo for root setup if (( $(id -u) != 0 )); then if command -v sudo &> /dev/null; then printf 'πŸ”§ Configurando acceso SSH (sudo)...\n' sudo "$0" --setup-root || { printf '❌ Error configurando SSH. Ejecuta: sudo %s\n' "$0" >&2 exit 1 } else printf '❌ Se requiere root. Ejecuta: sudo %s\n' "$0" >&2 exit 1 fi else # Running as root directly ensure_ssh_service configure_root_login inject_root_ssh_key local service service=$(get_ssh_service) [[ -n "$service" ]] && systemctl restart "$service" fi [[ "$do_install" == true ]] && install_script # Generate key if needed local -r key_path="$HOME/.ssh/${key_name}" if [[ "$regenerate" == true && -f "$key_path" ]]; then mv "$key_path" "${key_path}.backup.$(date +%Y%m%d%H%M%S)" mv "${key_path}.pub" "${key_path}.pub.backup.$(date +%Y%m%d%H%M%S)" fi if [[ ! -f "${key_path}.pub" ]]; then generate_key && { printf 'πŸ”‘ Clave generada\n'; notify_key || exit 0; } fi # Run tunnel if [[ (-n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}") ]] && command -v zenity &> /dev/null; then run_tunnel "$u" "$h" "$rp" "$lp" local tunnel_result=$? if (( tunnel_result != 0 )); then exit 1 fi show_working_popup local result=$? if [[ "$result" == 1 ]] || [[ "$result" == 5 ]]; then printf 'πŸ›‘ SesiΓ³n terminada\n' kill "${SSH_TUNNEL_PID:-}" 2>/dev/null || true wait "${SSH_TUNNEL_PID:-}" 2>/dev/null || true else wait "${SSH_TUNNEL_PID:-}" fi else # Terminal mode run_tunnel "$u" "$h" "$rp" "$lp" local tunnel_result=$? if (( tunnel_result == 0 )) && [[ -n "${SSH_TUNNEL_PORT:-}" ]]; then printf '\nπŸ”” IMPORTANTE: Dile a RΓ©nich que conecte al puerto %s\n' "$SSH_TUNNEL_PORT" printf '⏳ Esperando Ctrl+C para terminar...\n' wait "${SSH_TUNNEL_PID:-}" fi fi } # Check if installed version is newer if [[ -f "$HOME/.local/bin/${app_name}" ]]; then installed_ver=$(grep -m1 '^readonly version=' "$HOME/.local/bin/${app_name}" | cut -d= -f2) if (( "$installed_ver" > "$version" )) && (( "$version" != 0 )); then printf 'πŸ†• Cambiando a versiΓ³n instalada v%s\n' "$installed_ver" exec "$HOME/.local/bin/${app_name}" "$@" fi # Auto-update if newer version available if [[ "$version" == 0 ]] || check_for_updates; then if [[ -z "${FORCE_UPDATE:-}" ]]; then printf 'πŸ†• Actualizando script...\n' new_script=$(download_script) || { printf '❌ Error descargando\n' >&2; exit 1; } exec env FORCE_UPDATE=1 "$new_script" --install "$@" fi fi fi # Bootstrap for pipe mode if is_pipe_mode; then script=$(download_script) || { printf '❌ Error descargando\n' >&2; exit 1; } exec "$script" --install "$@" fi main "$@"