#!/usr/bin/env bash
#=============================================================================
# Sentinela ? Intelligent Linux Security Framework
# telegram/telegram.sh ? Módulo de alertas por Telegram
#=============================================================================
#
# Envía alertas de seguridad formateadas a Telegram.
# Incluye botones inline para acciones rápidas (desbloquear).
#
# Formato del mensaje:
# ?? SENTINELA ALERTA
# Servidor: server01
# IP: 192.168.1.100
# País: US
# ASN: AS12345 - Example ISP
# Tipo: sql_injection
# Puerto: 80
# Dominio: example.com
# User-Agent: sqlmap/1.5
# Hora: 2026-07-01 15:30:00
# Duración: 600s
# [? Desbloquear IP]
#=============================================================================
# shellcheck source=lib/common.sh
source "${SENTINELA_DIR}/lib/common.sh"
#=============================================================================
# Variables
#=============================================================================
TELEGRAM_API="https://api.telegram.org/bot"
#=============================================================================
# Verificar configuración de Telegram
#=============================================================================
telegram_check_config() {
if [[ "${TELEGRAM_ENABLE}" != "yes" ]]; then
return 1
fi
if [[ -z "${TELEGRAM_BOT_TOKEN}" ]] || [[ -z "${TELEGRAM_CHAT_ID}" ]]; then
log_warn "Telegram habilitado pero TELEGRAM_BOT_TOKEN o TELEGRAM_CHAT_ID no configurados"
return 1
fi
return 0
}
#=============================================================================
# Enviar mensaje de texto a Telegram
#=============================================================================
telegram_send_message() {
local message="$1"
local parse_mode="${2:-HTML}"
telegram_check_config || return 1
local response
response=$(curl -s -X POST "${TELEGRAM_API}${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
-d "text=${message}" \
-d "parse_mode=${parse_mode}" \
-d "disable_web_page_preview=true" \
--max-time 10 2>/dev/null) || {
log_warn "Fallo al enviar mensaje Telegram (timeout o error de red)"
return 1
}
if echo "${response}" | grep -q '"ok":true'; then
log_debug "Mensaje Telegram enviado exitosamente"
return 0
else
local error_desc
error_desc=$(echo "${response}" | grep -oP '"description":"[^"]+"' || echo "desconocido")
log_warn "Error al enviar mensaje Telegram: ${error_desc}"
return 1
fi
}
#=============================================================================
# Enviar mensaje con botón inline para desbloquear IP
#=============================================================================
telegram_send_alert_with_button() {
local ip="$1"
local message="$2"
telegram_check_config || return 1
local callback_data="unban_${ip}"
local payload
payload=$(jq -n \
--arg chat_id "${TELEGRAM_CHAT_ID}" \
--arg text "${message}" \
--arg callback_data "${callback_data}" \
'{
"chat_id": $chat_id,
"text": $text,
"parse_mode": "HTML",
"disable_web_page_preview": true,
"reply_markup": {
"inline_keyboard": [[
{
"text": "\ud83d\udd13 Desbloquear IP",
"callback_data": $callback_data
}
]]
}
}' 2>/dev/null) || {
log_warn "Fallo al construir payload JSON para Telegram"
return 1
}
local response
response=$(curl -s -X POST "${TELEGRAM_API}${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "${payload}" \
--max-time 10 2>/dev/null) || {
log_warn "Fallo al enviar alerta Telegram con botón (timeout o error de red)"
return 1
}
if echo "${response}" | grep -q '"ok":true'; then
log_debug "Alerta Telegram con botón enviada para IP ${ip}"
return 0
else
local error_desc
error_desc=$(echo "${response}" | grep -oP '"description":"[^"]+"' || echo "desconocido")
log_warn "Error al enviar alerta Telegram con botón: ${error_desc}"
return 1
fi
}
#=============================================================================
# Obtener información GeoIP de una IP
#=============================================================================
telegram_get_geo_info() {
local ip="$1"
local country="?"
local asn="?"
local org="?"
# Intentar con geoiplookup
if command -v geoiplookup &>/dev/null; then
country=$(geoiplookup "${ip}" 2>/dev/null | grep -oP 'Country:\s+\K\w+' || echo "?")
fi
# Intentar con mmdblookup
if command -v mmdblookup &>/dev/null && [[ -f "${GEOIP_DB}" ]]; then
country=$(mmdblookup --file "${GEOIP_DB}" --ip "${ip}" country iso_code 2>/dev/null | \
grep -oP '"[^"]+"' | head -1 | tr -d '"' || echo "${country}")
fi
if command -v mmdblookup &>/dev/null && [[ -f "${GEOIP_ASN_DB}" ]]; then
local asn_num
asn_num=$(mmdblookup --file "${GEOIP_ASN_DB}" --ip "${ip}" autonomous_system_number 2>/dev/null | \
grep -oP '\d+' | head -1 || echo "")
org=$(mmdblookup --file "${GEOIP_ASN_DB}" --ip "${ip}" autonomous_system_organization 2>/dev/null | \
grep -oP '"[^"]+"' | head -1 | tr -d '"' || echo "")
if [[ -n "${asn_num}" ]]; then
asn="AS${asn_num}"
fi
fi
# Intentar API gratuita si no hay GeoIP local
if [[ "${country}" == "?" ]] || [[ "${asn}" == "?" ]]; then
local ipinfo
ipinfo=$(curl -s "http://ip-api.com/json/${ip}" --max-time 3 2>/dev/null || echo "")
if [[ -n "${ipinfo}" ]]; then
[[ "${country}" == "?" ]] && country=$(echo "${ipinfo}" | grep -oP '"countryCode":"[^"]+"' | cut -d'"' -f4 || echo "?")
[[ "${asn}" == "?" ]] && {
local ipinfo_asn
ipinfo_asn=$(echo "${ipinfo}" | grep -oP '"as":"[^"]+"' | cut -d'"' -f4 || echo "")
[[ -n "${ipinfo_asn}" ]] && asn="${ipinfo_asn}"
}
[[ "${org}" == "" ]] && org=$(echo "${ipinfo}" | grep -oP '"org":"[^"]+"' | cut -d'"' -f4 || echo "")
fi
fi
echo "${country}|||${asn}|||${org}"
}
#=============================================================================
# Enviar alerta completa de seguridad
#=============================================================================
telegram_send_alert() {
local ip="$1"
local attack_type="$2"
local reason="$3"
local uri="${4:-}"
local ua="${5:-}"
local method="${6:-}"
local service="${7:-web}"
telegram_check_config || return 1
# Obtener información Geo
local geo_info
geo_info=$(telegram_get_geo_info "${ip}")
local country="${geo_info%%|||*}"
local rest="${geo_info#*|||}"
local asn="${rest%%|||*}"
local org="${rest#*|||*}"
# Obtener hostname
local hostname
hostname=$(hostname 2>/dev/null || echo "unknown")
# Obtener hora actual
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Mapear tipo de ataque a emoji
local emoji="??"
case "${attack_type}" in
sqli) emoji="??" ;;
xss) emoji="?" ;;
lfi) emoji="?" ;;
rfi) emoji="?" ;;
command_injection) emoji="??" ;;
path_traversal) emoji="??" ;;
scanner) emoji="?" ;;
tool) emoji="?" ;;
ddos) emoji="?" ;;
ssh_bruteforce) emoji="?" ;;
log4shell) emoji="?" ;;
xxe) emoji="?" ;;
honey) emoji="?" ;;
reputation) emoji="?" ;;
*) emoji="??" ;;
esac
# Construir mensaje
local message
message=$(cat <<EOF
<strong>${emoji} SENTINELA ? ALERTA DE SEGURIDAD</strong>
<b>Servidor:</b> ${hostname}
<b>IP Atacante:</b> <code>${ip}</code>
<b>País:</b> ${country}
<b>ASN:</b> ${asn:-N/A}
<b>Organización:</b> ${org:-N/A}
<b>Tipo:</b> ${attack_type}
<b>Razón:</b> ${reason}
<b>Servicio:</b> ${service}
<b>Método:</b> ${method:-N/A}
<b>URI:</b> <code>${uri:-N/A}</code>
<b>User-Agent:</b> <code>${ua:0:100}</code>
<b>Hora:</b> ${timestamp}
<b>Duración:</b> ${BAN_TIME}s
EOF
)
# Enviar con botón de desbloqueo
telegram_send_alert_with_button "${ip}" "${message}"
return $?
}
#=============================================================================
# Procesar callback query de botón (desbloquear IP)
#=============================================================================
telegram_process_callback() {
local callback_data="$1"
if [[ "${callback_data}" == unban_* ]]; then
local ip="${callback_data#unban_}"
if is_valid_ip "${ip}"; then
source "${SENTINELA_DIR}/firewall/blacklist.sh"
blacklist_remove "${ip}"
log_info "Telegram: IP ${ip} desbloqueada por botón"
telegram_send_message "? IP <code>${ip}</code> desbloqueada exitosamente"
fi
fi
}
#=============================================================================
# Configurar webhook de Telegram (para botones)
#=============================================================================
telegram_setup_webhook() {
local url="$1"
telegram_check_config || return 1
if [[ -z "${url}" ]]; then
log_error "URL del webhook requerida"
return 1
fi
local response
response=$(curl -s -X POST "${TELEGRAM_API}${TELEGRAM_BOT_TOKEN}/setWebhook" \
-d "url=${url}" \
--max-time 10 2>/dev/null) || {
log_warn "Fallo al configurar webhook Telegram"
return 1
}
if echo "${response}" | grep -q '"ok":true'; then
log_info "Webhook Telegram configurado: ${url}"
return 0
else
log_warn "Error al configurar webhook Telegram"
return 1
fi
}
#=============================================================================
# Probar conexión con Telegram
#=============================================================================
telegram_test() {
telegram_check_config || {
log_error "Telegram no está configurado correctamente"
return 1
}
local hostname
hostname=$(hostname)
if telegram_send_message "?? <b>Sentinela</b> conectado exitosamente en <b>${hostname}</b>"; then
log_info "Prueba de Telegram exitosa"
return 0
else
log_error "Prueba de Telegram falló"
return 1
fi
}
|