#!/usr/bin/env bash
#=============================================================================
# Sentinela ? Intelligent Linux Security Framework
# install.sh ? Instalador del sistema
#=============================================================================
#
# Uso: ./install.sh [--unattended] [--dev]
#
# --unattended: Instalación sin preguntas
# --dev: Modo desarrollo (enlaces simbólicos en lugar de copias)
#
# Este instalador:
# 1. Verifica compatibilidad (Debian, root, kernel)
# 2. Instala dependencias (iptables, ipset, curl, jq, php, etc.)
# 3. Crea estructura de directorios
# 4. Copia archivos a /usr/local/sentinela
# 5. Crea enlace simbólico /usr/local/bin/sentinela
# 6. Configura ipsets
# 7. Configura systemd
# 8. Configura cron
# 9. Configura dashboard (Apache vhost)
# 10. Prueba de conectividad
# 11. Muestra resumen
#=============================================================================
set -Eeuo pipefail
#=============================================================================
# Configuración de instalación
#=============================================================================
INSTALL_DIR="/usr/local/sentinela"
BIN_LINK="/usr/local/bin/sentinela"
CONFIG_DIR="/etc/sentinela"
LOG_DIR_INSTALL="/var/log/sentinela"
BACKUP_DIR_INSTALL="/var/backups/sentinela"
DASHBOARD_DIR_INSTALL="/var/www/sentinela"
SENTINELA_SRC="/root/sentinela"
UNATTENDED=false
DEV_MODE=false
#=============================================================================
# Procesar argumentos
#=============================================================================
for arg in "$@"; do
case "${arg}" in
--unattended) UNATTENDED=true ;;
--dev) DEV_MODE=true ;;
esac
done
#=============================================================================
# Colores para output
#=============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; }
#=============================================================================
# Banner
#=============================================================================
show_banner() {
cat << 'EOF'
???????????????????????????????????????????????????????
? ?
? ???????????????????? ??????????????????? ??? ?
? ????????????????????? ???????????????????? ??? ?
? ?????????????? ?????? ??? ??? ????????? ??? ?
? ?????????????? ?????????? ??? ????????????? ?
? ??????????????????? ?????? ??? ?????? ?????? ?
? ??????????????????? ????? ??? ?????? ????? ?
? ?
? Intelligent Linux Security Framework ?
? Versión 1.0.0 ?
? ?
? Instalación en progreso... ?
???????????????????????????????????????????????????????
EOF
}
#=============================================================================
# Verificar requisitos
#=============================================================================
check_requirements() {
info "Verificando requisitos..."
# Root check
if [[ $EUID -ne 0 ]]; then
error "Este instalador debe ejecutarse como root"
exit 1
fi
# OS check
if [[ ! -f /etc/debian_version ]]; then
warn "Este sistema no parece ser Debian. La instalación puede fallar."
if [[ "${UNATTENDED}" != "true" ]]; then
read -r -p "¿Continuar de todas formas? (s/N): " confirm
[[ "${confirm,,}" != "s" ]] && exit 1
fi
fi
# Kernel check
local kernel
kernel=$(uname -r)
info "Kernel: ${kernel}"
# Espacio en disco
local available
available=$(df /usr/local --output=avail 2>/dev/null | tail -1 || echo 0)
if [[ "${available}" -lt 512000 ]]; then
warn "Poco espacio disponible en /usr/local (${available} KB). Se recomienda al menos 500MB."
fi
ok "Requisitos verificados"
return 0
}
#=============================================================================
# Instalar dependencias
#=============================================================================
install_dependencies() {
info "Instalando dependencias..."
apt-get update -qq
local packages=(
iptables
ipset
curl
wget
jq
sqlite3
php8.3
php8.3-sqlite3
php8.3-json
php8.3-curl
apache2
libapache2-mod-php8.3
geoip-bin
geoip-database
mmdb-bin
libmaxminddb0
whois
ca-certificates
lsb-release
systemd
cron
gzip
tar
procps
net-tools
iproute2
conntrack
dnsutils
)
# Detectar versión de PHP disponible
if apt-cache show php8.2 &>/dev/null 2>&1; then
packages=("${packages[@]/php8.3/php8.2}")
packages=("${packages[@]/php8.3-sqlite3/php8.2-sqlite3}")
packages=("${packages[@]/php8.3-json/php8.2-json}")
packages=("${packages[@]/php8.3-curl/php8.2-curl}")
packages=("${packages[@]/libapache2-mod-php8.3/libapache2-mod-php8.2}")
fi
for pkg in "${packages[@]}"; do
if ! dpkg -l "${pkg}" &>/dev/null 2>&1; then
info " Instalando ${pkg}..."
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "${pkg}" 2>/dev/null || warn " No se pudo instalar ${pkg}"
else
ok " ${pkg} ya está instalado"
fi
done
# Asegurar que los módulos del kernel están cargados
modprobe xt_conntrack 2>/dev/null || true
modprobe xt_hashlimit 2>/dev/null || true
modprobe xt_connlimit 2>/dev/null || true
modprobe xt_set 2>/dev/null || true
modprobe xt_limit 2>/dev/null || true
modprobe xt_state 2>/dev/null || true
modprobe xt_tcpudp 2>/dev/null || true
modprobe ip_set 2>/dev/null || true
modprobe ip_set_hash_ip 2>/dev/null || true
modprobe ip_set_hash_net 2>/dev/null || true
# GeoIP databases
if [[ ! -f /usr/share/GeoIP/GeoLite2-Country.mmdb ]]; then
info " Descargando GeoLite2-Country..."
mkdir -p /usr/share/GeoIP
curl -sL "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb" \
-o /usr/share/GeoIP/GeoLite2-Country.mmdb 2>/dev/null || warn " No se pudo descargar GeoLite2-Country"
fi
if [[ ! -f /usr/share/GeoIP/GeoLite2-ASN.mmdb ]]; then
info " Descargando GeoLite2-ASN..."
curl -sL "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-ASN.mmdb" \
-o /usr/share/GeoIP/GeoLite2-ASN.mmdb 2>/dev/null || warn " No se pudo descargar GeoLite2-ASN"
fi
ok "Dependencias instaladas"
}
#=============================================================================
# Crear directorios
#=============================================================================
create_directories() {
info "Creando directorios..."
mkdir -p "${INSTALL_DIR}"
mkdir -p "${CONFIG_DIR}"
mkdir -p "${LOG_DIR_INSTALL}"
mkdir -p "${BACKUP_DIR_INSTALL}"
mkdir -p "${DASHBOARD_DIR_INSTALL}"
# Directorios dentro de INSTALL_DIR
for dir in firewall ids ipset telegram backup dashboard api cron logs systemd lib geoip cli; do
mkdir -p "${INSTALL_DIR}/${dir}"
done
ok "Directorios creados"
}
#=============================================================================
# Copiar archivos
#=============================================================================
copy_files() {
info "Copiando archivos..."
if [[ "${DEV_MODE}" == "true" ]]; then
info "Modo desarrollo: creando enlaces simbólicos..."
# Backup de config si existe
if [[ -f "${CONFIG_DIR}/config.conf" ]]; then
cp "${CONFIG_DIR}/config.conf" "${CONFIG_DIR}/config.conf.bak"
fi
ln -sf "${SENTINELA_SRC}/config.conf" "${CONFIG_DIR}/config.conf"
ln -sf "${SENTINELA_SRC}/cli/sentinela.sh" "${BIN_LINK}"
# Enlaces para cada módulo
for dir in firewall ids ipset telegram backup dashboard api cron logs systemd lib geoip; do
for file in "${SENTINELA_SRC}/${dir}"/*; do
if [[ -f "${file}" ]]; then
ln -sf "${file}" "${INSTALL_DIR}/${dir}/" 2>/dev/null || true
fi
done
done
# Enlaces para lib
for file in "${SENTINELA_SRC}/lib"/*; do
if [[ -f "${file}" ]]; then
ln -sf "${file}" "${INSTALL_DIR}/lib/" 2>/dev/null || true
fi
done
else
cp -r "${SENTINELA_SRC}/firewall"/* "${INSTALL_DIR}/firewall/"
cp -r "${SENTINELA_SRC}/ids"/* "${INSTALL_DIR}/ids/"
cp -r "${SENTINELA_SRC}/ipset"/* "${INSTALL_DIR}/ipset/"
cp -r "${SENTINELA_SRC}/telegram"/* "${INSTALL_DIR}/telegram/"
cp -r "${SENTINELA_SRC}/backup"/* "${INSTALL_DIR}/backup/"
cp -r "${SENTINELA_SRC}/cron"/* "${INSTALL_DIR}/cron/"
cp -r "${SENTINELA_SRC}/lib"/* "${INSTALL_DIR}/lib/"
cp -r "${SENTINELA_SRC}/geoip"/* "${INSTALL_DIR}/geoip/" 2>/dev/null || true
cp -r "${SENTINELA_SRC}/dashboard"/* "${DASHBOARD_DIR_INSTALL}/" 2>/dev/null || true
cp -r "${SENTINELA_SRC}/api"/* "${INSTALL_DIR}/api/" 2>/dev/null || true
cp "${SENTINELA_SRC}/config.conf" "${CONFIG_DIR}/config.conf"
cp "${SENTINELA_SRC}/install.sh" "${INSTALL_DIR}/"
cp "${SENTINELA_SRC}/uninstall.sh" "${INSTALL_DIR}/" 2>/dev/null || true
cp "${SENTINELA_SRC}/update.sh" "${INSTALL_DIR}/" 2>/dev/null || true
cp "${SENTINELA_SRC}/cli/sentinela.sh" "${BIN_LINK}"
fi
chmod +x "${BIN_LINK}"
chmod 600 "${CONFIG_DIR}/config.conf"
ok "Archivos copiados"
}
#=============================================================================
# Configurar ipsets
#=============================================================================
setup_ipsets() {
info "Configurando ipsets..."
source "${SENTINELA_SRC}/lib/common.sh"
export SENTINELA_DIR="${INSTALL_DIR}"
# Crear ipsets
for set_name in SENTINELA_TRUSTED SENTINELA_BLACKLIST SENTINELA_DDOS SENTINELA_REPUTATION SENTINELA_HONEY; do
if ! ipset list "${set_name}" &>/dev/null; then
local type="hash:ip"
local timeout="600"
case "${set_name}" in
SENTINELA_TRUSTED) type="hash:ip"; timeout="86400" ;;
SENTINELA_BLACKLIST) type="hash:ip"; timeout="600" ;;
SENTINELA_DDOS) type="hash:ip"; timeout="600" ;;
SENTINELA_REPUTATION) type="hash:net"; timeout="86400" ;;
SENTINELA_HONEY) type="hash:ip"; timeout="3600" ;;
esac
ipset create "${set_name}" "${type}" timeout "${timeout}" 2>/dev/null || \
warn " No se pudo crear ipset ${set_name} (puede que ya exista)"
fi
done
ok "Ipsets configurados"
}
#=============================================================================
# Configurar systemd
#=============================================================================
setup_systemd() {
info "Configurando servicio systemd..."
cp "${SENTINELA_SRC}/systemd/sentinela.service" /etc/systemd/system/sentinela.service
chmod 644 /etc/systemd/system/sentinela.service
systemctl daemon-reload 2>/dev/null || true
if [[ "${UNATTENDED}" == "true" ]]; then
systemctl enable sentinela.service 2>/dev/null || true
else
info "¿Desea habilitar Sentinela para que inicie automáticamente?"
read -r -p "¿Habilitar? (S/n): " confirm
if [[ "${confirm,,}" != "n" ]]; then
systemctl enable sentinela.service 2>/dev/null || true
ok "Servicio habilitado"
fi
fi
systemctl daemon-reload 2>/dev/null || true
ok "Servicio systemd configurado"
}
#=============================================================================
# Configurar cron
#=============================================================================
setup_cron() {
info "Configurando tareas cron..."
source "${SENTINELA_SRC}/cron/cron.sh"
export SENTINELA_DIR="${INSTALL_DIR}"
cron_install
ok "Tareas cron configuradas"
}
#=============================================================================
# Configurar dashboard Apache
#=============================================================================
setup_dashboard() {
info "Configurando dashboard web..."
# Crear vhost de Apache
local vhost_file="/etc/apache2/sites-available/sentinela.conf"
cat > "${vhost_file}" << VHOSTEOF
<VirtualHost *:${DASHBOARD_PORT:-8080}>
ServerName sentinela.local
DocumentRoot ${DASHBOARD_DIR_INSTALL}
<Directory ${DASHBOARD_DIR_INSTALL}>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^api/(.*)$ api/index.php [QSA,L]
</IfModule>
</Directory>
<FilesMatch "\.php$">
SetHandler application/x-httpd-php
</FilesMatch>
ErrorLog \${APACHE_LOG_DIR}/sentinela-error.log
CustomLog \${APACHE_LOG_DIR}/sentinela-access.log combined
<Location /api>
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Location>
</VirtualHost>
VHOSTEOF
# Habilitar sitio si no es unattended
if [[ "${UNATTENDED}" == "true" ]]; then
a2ensite sentinela.conf 2>/dev/null || true
else
info "¿Desea habilitar el dashboard web?"
read -r -p "¿Habilitar dashboard? (S/n): " confirm
if [[ "${confirm,,}" != "n" ]]; then
a2ensite sentinela.conf 2>/dev/null || true
a2enmod rewrite 2>/dev/null || true
systemctl reload apache2 2>/dev/null || true
ok "Dashboard habilitado en puerto ${DASHBOARD_PORT:-8080}"
fi
fi
ok "Dashboard configurado"
}
#=============================================================================
# Prueba de conectividad
#=============================================================================
test_connectivity() {
if [[ "${UNATTENDED}" == "true" ]]; then
return 0
fi
info "Verificando conectividad básica..."
# Probar que el comando sentinela funciona
if command -v sentinela &>/dev/null; then
ok "Comando 'sentinela' disponible"
fi
# Probar iptables
if iptables -L -n &>/dev/null; then
ok "iptables operativo"
fi
# Probar ipset
if ipset list &>/dev/null; then
ok "ipset operativo"
fi
# Probar resolución DNS
if host -W 2 google.com &>/dev/null; then
ok "Resolución DNS operativa"
else
warn "Resolución DNS no disponible"
fi
}
#=============================================================================
# Resumen final
#=============================================================================
show_summary() {
echo
echo "???????????????????????????????????????????????????????"
echo " ? Sentinela instalado exitosamente"
echo "???????????????????????????????????????????????????????"
echo
echo " ? Instalación: ${INSTALL_DIR}"
echo " ? Config: ${CONFIG_DIR}/config.conf"
echo " ? Logs: ${LOG_DIR_INSTALL}"
echo " ? Dashboard: ${DASHBOARD_DIR_INSTALL}"
echo " ? Backups: ${BACKUP_DIR_INSTALL}"
echo
echo " ? Comando: sentinela"
echo
echo " Comandos útiles:"
echo " sentinela status - Ver estado del firewall"
echo " sentinela apply - Aplicar reglas de firewall"
echo " sentinela scan - Escanear logs (IDS)"
echo " sentinela unban <IP> - Desbloquear IP"
echo " sentinela ban <IP> - Bloquear IP manualmente"
echo " sentinela logs - Ver logs"
echo " sentinela dashboard - Abrir dashboard web"
echo " sentinela help - Ayuda completa"
echo
echo " ?? IMPORTANTE:"
echo " 1. Edite ${CONFIG_DIR}/config.conf con sus ajustes"
echo " 2. Configure TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID para alertas"
echo " 3. Ejecute 'sentinela apply' para activar el firewall"
echo " 4. El firewall NUNCA bloqueará su sesión SSH actual"
echo
}
#=============================================================================
# Main
#=============================================================================
main() {
clear
show_banner
echo
echo "??? Sentinela v1.0.0 ? Instalación ???"
echo
check_requirements
echo
install_dependencies
echo
create_directories
echo
copy_files
echo
setup_ipsets
echo
setup_systemd
echo
setup_cron
echo
setup_dashboard
echo
test_connectivity
echo
show_summary
}
main "$@"
|