#!/bin/bash # lib/steps/wrapper.sh — generar wrapper de privilegios para sing-box step_wrapper() { print_section "Security Wrapper" log_step "Installing hydraveil-singbox wrapper" 5 $TOTAL_STEPS [ -f "$SINGBOX_WRAPPER" ] && sudo rm -f "$SINGBOX_WRAPPER" && log_ok "Wrapper anterior eliminado" sudo tee "$SINGBOX_WRAPPER" > /dev/null << WRAPPER #!/bin/bash # hydraveil-singbox — wrapper de privilegios para sing-box # Generado por el installer. NO editar manualmente. # Solo este binario tiene NOPASSWD en sudoers. # # Uso: # sudo hydraveil-singbox start # sudo hydraveil-singbox "" stop # sudo hydraveil-singbox "" log set -euo pipefail CONFIG="\${1:-}" ACTION="\${2:-}" # ── Paths fijos (rutas XDG correctas — INSTALL_DIR=\$HOME) ──────────────────── SINGBOX_BIN="${SINGBOX_BIN}" PID_FILE="${SINGBOX_PID_FILE}" LOG_FILE="${SINGBOX_LOG_FILE}" CONFIG_DIR="${SINGBOX_CONFIG_DIR}" STOP_TIMEOUT=8 # ── Validar acción ──────────────────────────────────────────────────────────── if [[ "\$ACTION" != "start" && "\$ACTION" != "stop" && "\$ACTION" != "log" ]]; then echo "Error: acción inválida '\$ACTION'. Uso: start|stop|log" >&2 exit 1 fi # ── Helpers ─────────────────────────────────────────────────────────────────── _pid_running() { local pid="\$1" [[ "\$pid" =~ ^[0-9]+$ ]] && kill -0 "\$pid" 2>/dev/null } _delete_tun() { if ip link show tun0 &>/dev/null 2>&1; then ip link delete tun0 2>/dev/null || true echo "[wrapper] tun0 eliminada" >> "\$LOG_FILE" 2>/dev/null || true fi } _kill_by_pidfile() { [ -f "\$PID_FILE" ] || return 0 local pid pid=\$(cat "\$PID_FILE" 2>/dev/null || echo "") if ! _pid_running "\$pid"; then rm -f "\$PID_FILE" return 0 fi kill -TERM "\$pid" 2>/dev/null || true echo "[wrapper] SIGTERM → PID \$pid" >> "\$LOG_FILE" 2>/dev/null || true local elapsed=0 while _pid_running "\$pid" && (( elapsed < STOP_TIMEOUT * 2 )); do sleep 0.5 (( elapsed++ )) || true done if _pid_running "\$pid"; then kill -KILL "\$pid" 2>/dev/null || true echo "[wrapper] SIGKILL → PID \$pid" >> "\$LOG_FILE" 2>/dev/null || true sleep 0.3 fi rm -f "\$PID_FILE" } # ── log ─────────────────────────────────────────────────────────────────────── if [[ "\$ACTION" == "log" ]]; then [ -f "\$LOG_FILE" ] && tail -80 "\$LOG_FILE" || echo "Sin logs disponibles" exit 0 fi # ── stop ────────────────────────────────────────────────────────────────────── if [[ "\$ACTION" == "stop" ]]; then _kill_by_pidfile _delete_tun echo "[wrapper] sing-box detenido — \$(date '+%Y-%m-%d %H:%M:%S')" >> "\$LOG_FILE" 2>/dev/null || true exit 0 fi # ── start ───────────────────────────────────────────────────────────────────── [[ -z "\$CONFIG" ]] && { echo "Error: config requerido para start" >&2; exit 1; } [[ ! -f "\$CONFIG" ]] && { echo "Error: config no encontrado: \$CONFIG" >&2; exit 1; } [[ ! -r "\$CONFIG" ]] && { echo "Error: config no legible: \$CONFIG" >&2; exit 1; } _real_config=\$(realpath "\$CONFIG") _real_config_dir=\$(realpath "\$CONFIG_DIR") if [[ "\$_real_config" != "\$_real_config_dir"/* ]]; then echo "Error: config fuera del directorio permitido" >&2 echo " Config: \$_real_config" >&2 echo " Permitido: \$_real_config_dir" >&2 exit 1 fi if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "\$CONFIG" 2>/dev/null; then echo "Error: JSON inválido: \$CONFIG" >&2 exit 1 fi [[ ! -x "\$SINGBOX_BIN" ]] && { echo "Error: sing-box no encontrado: \$SINGBOX_BIN" >&2; exit 1; } _kill_by_pidfile _delete_tun mkdir -p "\$(dirname "\$PID_FILE")" && chmod 700 "\$(dirname "\$PID_FILE")" mkdir -p "\$(dirname "\$LOG_FILE")" && chmod 700 "\$(dirname "\$LOG_FILE")" "\$SINGBOX_BIN" run -c "\$CONFIG" >> "\$LOG_FILE" 2>&1 & _new_pid=\$! sleep 0.3 if ! _pid_running "\$_new_pid"; then echo "Error: sing-box terminó inmediatamente. Ver: \$LOG_FILE" >&2 exit 1 fi echo "\$_new_pid" > "\$PID_FILE" chmod 600 "\$PID_FILE" echo "[wrapper] sing-box iniciado — PID \$_new_pid — \$(date '+%Y-%m-%d %H:%M:%S')" >> "\$LOG_FILE" echo "[wrapper] config: \$_real_config" >> "\$LOG_FILE" exit 0 WRAPPER sudo chmod 755 "$SINGBOX_WRAPPER" log_ok "Wrapper instalado → ${SINGBOX_WRAPPER}" # Verificar que el wrapper funciona if sudo "$SINGBOX_WRAPPER" "" stop &>/dev/null; then log_ok "Wrapper verificado (stop OK)" else log_warn "Wrapper instalado pero verificación falló — revisar manualmente" fi }