#!/bin/bash # ============================================================================ # DNSBlock Agent - Script de Instalação e Desinstalação # ============================================================================ # Este script realiza a instalação ou desinstalação completa do DNSBlock Agent # Requer privilégios de root para execução # # Uso: # ./install.sh Instalação interativa # ./install.sh uninstall Desinstalação # ============================================================================ set -e # Diretórios e arquivos INSTALL_DIR="/opt/dnsblock" RPZ_DIR="$INSTALL_DIR/rpz" SERVICE_FILE="/etc/systemd/system/dnsblock-agent.service" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Configurações do Unbound UNBOUND_CONF_DIR="/etc/unbound/unbound.conf.d" UNBOUND_MAIN_CONF="/etc/unbound/unbound.conf" DNSBLOCK_UNBOUND_CONF="$INSTALL_DIR/unbound.conf" DNSBLOCK_INCLUDE='include: "/opt/dnsblock/unbound.conf"' # Variáveis globais SERIAL="" NO_SERIAL=false UNBOUND_ERROR="" # ============================================================================ # FUNÇÕES UTILITÁRIAS # ============================================================================ # Verificar se está executando como root verificar_root() { if [ "$(id -u)" -ne 0 ]; then whiptail --title "Erro" --msgbox "Este script deve ser executado como root.\n\nExecute novamente com sudo." 10 50 exit 1 fi } # Verificar dependências verificar_dependencias() { local deps_faltando="" if ! command -v whiptail &> /dev/null; then echo "Erro: whiptail não encontrado. Instale-o antes de continuar." exit 1 fi if ! command -v unbound-checkconf &> /dev/null; then deps_faltando="$deps_faltando unbound-checkconf" fi if [ -n "$deps_faltando" ]; then whiptail --title "Dependências Faltando" \ --msgbox "As seguintes dependências não foram encontradas:\n$deps_faltando\n\nPor favor, instale-as antes de continuar." 12 55 exit 1 fi } # ============================================================================ # FUNÇÕES DE SERIAL # ============================================================================ # Solicitar o serial via whiptail solicitar_serial() { local input="" input=$(whiptail --title "DNSBlock Agent - Ativação" \ --inputbox "Digite o serial de ativação do agente:\n\n(Deixe em branco para instalar sem serial)" \ 12 60 "" 3>&1 1>&2 2>&3) local status=$? if [ $status -ne 0 ]; then whiptail --title "Instalação Cancelada" \ --msgbox "A instalação foi cancelada pelo usuário." 8 45 exit 0 fi if [ -z "$input" ]; then if whiptail --title "Instalação sem Serial" \ --yesno "Você não informou um serial.\n\nDeseja instalar sem serial?\n\n(O serviço será ativado mas não inicializado)\n\nVocê pode obter seu serial em:\nhttp://painel.dnsblock.com.br" \ 14 55; then NO_SERIAL=true SERIAL="" else whiptail --title "Instalação Cancelada" \ --msgbox "A instalação foi cancelada pelo usuário." 8 45 exit 0 fi else SERIAL="$input" fi } # Atualizar o serial no config.json atualizar_config_serial() { local serial="$1" local config_file="$INSTALL_DIR/config.json" if [ -f "$config_file" ] && [ -n "$serial" ]; then sed -i "s/\"serial_key\":[[:space:]]*\"[^\"]*\"/\"serial_key\": \"$serial\"/" "$config_file" fi } # ============================================================================ # FUNÇÕES DE CONFIGURAÇÃO DO UNBOUND # ============================================================================ # Encontrar arquivo de configuração do Unbound para adicionar o include encontrar_arquivo_config_unbound() { # Prioridade: arquivo com RPZ existente > local.conf > unbound.conf principal # Primeiro, verificar se existe algum arquivo com bloco RPZ if [ -d "$UNBOUND_CONF_DIR" ]; then for conf in "$UNBOUND_CONF_DIR"/*.conf; do if [ -f "$conf" ] && grep -q "^[[:space:]]*rpz:" "$conf" 2>/dev/null; then echo "$conf" return 0 fi done # Se existe local.conf, usar ele if [ -f "$UNBOUND_CONF_DIR/local.conf" ]; then echo "$UNBOUND_CONF_DIR/local.conf" return 0 fi fi # Verificar arquivo principal if [ -f "$UNBOUND_MAIN_CONF" ]; then # Verificar se tem bloco RPZ if grep -q "^[[:space:]]*rpz:" "$UNBOUND_MAIN_CONF" 2>/dev/null; then echo "$UNBOUND_MAIN_CONF" return 0 fi # Mesmo sem RPZ, usar o principal echo "$UNBOUND_MAIN_CONF" return 0 fi # Nenhum arquivo encontrado return 1 } # Verificar se o include do DNSBlock já existe verificar_include_existente() { local include_pattern='include:[[:space:]]*"/opt/dnsblock/unbound.conf"' # Verificar no arquivo principal if [ -f "$UNBOUND_MAIN_CONF" ] && grep -qE "$include_pattern" "$UNBOUND_MAIN_CONF" 2>/dev/null; then return 0 fi # Verificar nos arquivos do diretório conf.d if [ -d "$UNBOUND_CONF_DIR" ]; then for conf in "$UNBOUND_CONF_DIR"/*.conf; do if [ -f "$conf" ] && grep -qE "$include_pattern" "$conf" 2>/dev/null; then return 0 fi done fi return 1 } # Configurar o Unbound para usar o DNSBlock configurar_unbound() { UNBOUND_ERROR="" # Criar o diretório RPZ mkdir -p "$RPZ_DIR" chown unbound:unbound "$RPZ_DIR" # Copiar arquivo de configuração do DNSBlock para o Unbound (já vem no pacote) if [ -f "$SCRIPT_DIR/unbound.conf" ]; then cp "$SCRIPT_DIR/unbound.conf" "$DNSBLOCK_UNBOUND_CONF" chown unbound:unbound "$DNSBLOCK_UNBOUND_CONF" chmod 644 "$DNSBLOCK_UNBOUND_CONF" fi # Criar arquivo RPZ vazio se não existir local rpz_file="$RPZ_DIR/rpz.dnsblock.zone" if [ ! -f "$rpz_file" ]; then cat > "$rpz_file" << 'EOF' $TTL 1H @ SOA localhost. hostmaster.localhost. ( 1 ; Serial 1h ; Refresh 15m ; Retry 30d ; Expire 2h ; Negative Cache TTL ) NS localhost. ; RPZ block hosts EOF chown unbound:unbound "$rpz_file" chmod 644 "$rpz_file" fi # Verificar se o include já existe if verificar_include_existente; then # Já configurado, apenas verificar e reiniciar if unbound-checkconf 2>&1; then systemctl restart unbound 2>/dev/null || true return 0 else UNBOUND_ERROR="Configuração do Unbound com erros. Verifique manualmente." return 1 fi fi # Encontrar arquivo de configuração do Unbound local config_file config_file=$(encontrar_arquivo_config_unbound) if [ -z "$config_file" ]; then UNBOUND_ERROR="Não foi possível encontrar arquivo de configuração do Unbound." return 1 fi # Fazer backup do arquivo cp "$config_file" "${config_file}.bak.dnsblock" # Verificar se existe bloco RPZ no arquivo if grep -q "^[[:space:]]*rpz:" "$config_file" 2>/dev/null; then # Existe bloco RPZ - adicionar include ANTES do primeiro bloco rpz # Usando awk para inserir antes da primeira ocorrência de rpz: awk -v include="# DNSBlock\n$DNSBLOCK_INCLUDE\n" ' /^[[:space:]]*rpz:/ && !inserted { print "# DNSBlock" print "include: \"/opt/dnsblock/unbound.conf\"" print "" inserted=1 } {print} ' "$config_file" > "${config_file}.tmp" && mv "${config_file}.tmp" "$config_file" else # Não existe bloco RPZ - adicionar include no final do arquivo echo "" >> "$config_file" echo "# DNSBlock" >> "$config_file" echo "$DNSBLOCK_INCLUDE" >> "$config_file" fi # Verificar configuração do Unbound local check_output check_output=$(unbound-checkconf 2>&1) if [ $? -ne 0 ]; then # Erro na configuração - restaurar backup mv "${config_file}.bak.dnsblock" "$config_file" UNBOUND_ERROR="Erro na configuração do Unbound:\n$check_output" return 1 fi # Remover backup rm -f "${config_file}.bak.dnsblock" # Reiniciar o Unbound if ! systemctl restart unbound 2>&1; then UNBOUND_ERROR="Falha ao reiniciar o Unbound. Verifique o serviço manualmente." return 1 fi return 0 } # Remover o include do DNSBlock dos arquivos de configuração remover_include_unbound() { local include_pattern='include:[[:space:]]*"/opt/dnsblock/unbound.conf"' local comment_pattern='#[[:space:]]*DNSBlock' # Remover do arquivo principal if [ -f "$UNBOUND_MAIN_CONF" ]; then sed -i "/$include_pattern/d" "$UNBOUND_MAIN_CONF" 2>/dev/null || true sed -i "/$comment_pattern/d" "$UNBOUND_MAIN_CONF" 2>/dev/null || true # Remover linhas em branco duplicadas sed -i '/^$/N;/^\n$/d' "$UNBOUND_MAIN_CONF" 2>/dev/null || true fi # Remover dos arquivos do diretório conf.d if [ -d "$UNBOUND_CONF_DIR" ]; then for conf in "$UNBOUND_CONF_DIR"/*.conf; do if [ -f "$conf" ]; then sed -i "/$include_pattern/d" "$conf" 2>/dev/null || true sed -i "/$comment_pattern/d" "$conf" 2>/dev/null || true sed -i '/^$/N;/^\n$/d' "$conf" 2>/dev/null || true fi done fi } # Remover configuração do Unbound remover_configuracao_unbound() { # Remover include dos arquivos de configuração remover_include_unbound # Verificar se a configuração está OK e reiniciar if unbound-checkconf &>/dev/null; then systemctl restart unbound 2>/dev/null || true fi } # ============================================================================ # FUNÇÃO DE INSTALAÇÃO # ============================================================================ instalar() { # Verificar dependências verificar_dependencias # Verificar se já está instalado if [ -d "$INSTALL_DIR" ]; then if ! whiptail --title "DNSBlock já instalado" \ --yesno "O DNSBlock Agent já está instalado.\n\nDeseja reinstalar?" 10 50; then exit 0 fi # Parar serviço se estiver rodando systemctl stop dnsblock-agent 2>/dev/null || true systemctl disable dnsblock-agent 2>/dev/null || true rm -rf "$INSTALL_DIR" rm -f "$SERVICE_FILE" fi # Exibir progresso - Fase 1: Criar diretórios e copiar arquivos { echo "5"; echo "# Criando diretórios..." mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR/logs" mkdir -p "$INSTALL_DIR/rpz" sleep 0.2 echo "15"; echo "# Copiando binário..." if [ -f "$SCRIPT_DIR/dnsblock-agent" ]; then cp "$SCRIPT_DIR/dnsblock-agent" "$INSTALL_DIR/" chmod +x "$INSTALL_DIR/dnsblock-agent" else echo "ERRO: dnsblock-agent não encontrado!" >&2 exit 1 fi sleep 0.2 echo "25"; echo "# Criando config.json..." if [ -f "$SCRIPT_DIR/config.json.example" ]; then cp "$SCRIPT_DIR/config.json.example" "$INSTALL_DIR/config.json" else echo "ERRO: config.json.example não encontrado!" >&2 exit 1 fi sleep 0.2 echo "30"; echo "# Copiando configuração do Unbound..." if [ -f "$SCRIPT_DIR/unbound.conf" ]; then cp "$SCRIPT_DIR/unbound.conf" "$INSTALL_DIR/" fi sleep 0.2 echo "35"; echo "# Configurando permissões..." touch "$INSTALL_DIR/logs/agent.log" chown -R unbound:unbound "$INSTALL_DIR" chmod 755 "$INSTALL_DIR" sleep 0.2 echo "40" } | whiptail --title "DNSBlock Agent - Instalação" --gauge "Preparando arquivos..." 8 60 0 # Solicitar serial (fora do gauge para funcionar corretamente) solicitar_serial # Exibir progresso - Fase 2: Configurar serial e serviço { echo "45"; echo "# Configurando serial..." if [ -n "$SERIAL" ]; then atualizar_config_serial "$SERIAL" fi sleep 0.2 echo "55"; echo "# Instalando serviço systemd..." if [ -f "$SCRIPT_DIR/dnsblock-agent.service" ]; then cp "$SCRIPT_DIR/dnsblock-agent.service" "$SERVICE_FILE" else echo "ERRO: dnsblock-agent.service não encontrado!" >&2 exit 1 fi sleep 0.2 echo "65"; echo "# Habilitando serviço..." systemctl daemon-reload systemctl enable dnsblock-agent 2>/dev/null || true sleep 0.2 echo "75"; echo "# Configurando Unbound..." configurar_unbound || true sleep 0.2 echo "90"; echo "# Finalizando..." if [ -n "$SERIAL" ]; then systemctl start dnsblock-agent 2>/dev/null || true fi sleep 0.2 echo "100" } | whiptail --title "DNSBlock Agent - Instalação" --gauge "Configurando sistema..." 8 60 45 # Verificar se houve erro no Unbound if [ -n "$UNBOUND_ERROR" ]; then whiptail --title "Aviso - Configuração do Unbound" \ --msgbox "Houve um problema ao configurar o Unbound:\n\n$UNBOUND_ERROR\n\nO agente foi instalado, mas você precisará configurar o Unbound manualmente." 14 65 fi # Exibir mensagem final if [ -n "$SERIAL" ]; then whiptail --title "Instalação Concluída" \ --msgbox "O DNSBlock Agent foi instalado e iniciado com sucesso!\n\nComandos úteis:\n • Status: systemctl status dnsblock-agent\n • Logs: journalctl -u dnsblock-agent -f\n • Reiniciar: systemctl restart dnsblock-agent" 14 60 else whiptail --title "Instalação Concluída (sem serial)" \ --msgbox "O DNSBlock Agent foi instalado mas NÃO foi iniciado.\n\nPara ativar o agente:\n\n1. Obtenha seu serial em:\n http://painel.dnsblock.com.br\n\n2. Edite o arquivo:\n $INSTALL_DIR/config.json\n\n3. Inicie o serviço:\n systemctl start dnsblock-agent" 18 60 fi } # ============================================================================ # FUNÇÃO DE DESINSTALAÇÃO # ============================================================================ desinstalar() { if [ ! -d "$INSTALL_DIR" ] && [ ! -f "$SERVICE_FILE" ]; then whiptail --title "Não Instalado" \ --msgbox "O DNSBlock Agent não parece estar instalado." 8 50 exit 0 fi if ! whiptail --title "Confirmar Desinstalação" \ --yesno "Você está prestes a desinstalar o DNSBlock Agent.\n\nTodos os arquivos e configurações serão removidos.\n\nDeseja continuar?" 12 55; then exit 0 fi # Exibir progresso { echo "10"; echo "# Parando serviço..." systemctl stop dnsblock-agent 2>/dev/null || true sleep 0.3 echo "30"; echo "# Desabilitando serviço..." systemctl disable dnsblock-agent 2>/dev/null || true sleep 0.3 echo "50"; echo "# Removendo arquivo de serviço..." rm -f "$SERVICE_FILE" systemctl daemon-reload sleep 0.3 echo "70"; echo "# Removendo configuração do Unbound..." remover_configuracao_unbound sleep 0.3 echo "90"; echo "# Removendo arquivos..." rm -rf "$INSTALL_DIR" sleep 0.3 echo "100" } | whiptail --title "DNSBlock Agent - Desinstalação" --gauge "Removendo DNSBlock Agent..." 8 60 0 whiptail --title "Desinstalação Concluída" \ --msgbox "O DNSBlock Agent foi completamente removido do sistema." 8 55 } # ============================================================================ # MENU PRINCIPAL # ============================================================================ menu_principal() { local opcao opcao=$(whiptail --title "DNSBlock Agent" \ --menu "Selecione uma opção:" 15 60 4 \ "1" "Instalar DNSBlock Agent" \ "2" "Desinstalar DNSBlock Agent" \ "3" "Sair" \ 3>&1 1>&2 2>&3) local status=$? if [ $status -ne 0 ] || [ "$opcao" = "3" ]; then exit 0 fi case "$opcao" in 1) instalar ;; 2) desinstalar ;; esac } # ============================================================================ # INÍCIO DO SCRIPT # ============================================================================ # Verificar root verificar_root # Processar argumentos case "${1:-}" in install) instalar ;; uninstall|remove) desinstalar ;; --help|-h) echo "" echo "DNSBlock Agent - Script de Instalação" echo "" echo "Uso: $0 [install|uninstall]" echo "" echo "Opções:" echo " install Instalar o DNSBlock Agent" echo " uninstall Desinstalar o DNSBlock Agent" echo " --help Exibir esta mensagem" echo "" echo "Se executado sem argumentos, exibe o menu interativo." echo "" ;; "") menu_principal ;; *) echo "Opção inválida: $1" echo "Use --help para ver as opções disponíveis." exit 1 ;; esac