diff --git a/agent/__pycache__/main.cpython-311.pyc b/agent/__pycache__/main.cpython-311.pyc index fa04c87..4af7379 100644 Binary files a/agent/__pycache__/main.cpython-311.pyc and b/agent/__pycache__/main.cpython-311.pyc differ diff --git a/agent/build/dnsblock-agent/Analysis-00.toc b/agent/build/dnsblock-agent/Analysis-00.toc index e395a17..2c04dec 100644 --- a/agent/build/dnsblock-agent/Analysis-00.toc +++ b/agent/build/dnsblock-agent/Analysis-00.toc @@ -115,8 +115,6 @@ ('calendar', '/usr/lib/python3.11/calendar.py', 'PYMODULE'), ('urllib.parse', '/usr/lib/python3.11/urllib/parse.py', 'PYMODULE'), ('ipaddress', '/usr/lib/python3.11/ipaddress.py', 'PYMODULE'), - ('socket', '/usr/lib/python3.11/socket.py', 'PYMODULE'), - ('selectors', '/usr/lib/python3.11/selectors.py', 'PYMODULE'), ('quopri', '/usr/lib/python3.11/quopri.py', 'PYMODULE'), ('email', '/usr/lib/python3.11/email/__init__.py', 'PYMODULE'), ('email.parser', '/usr/lib/python3.11/email/parser.py', 'PYMODULE'), @@ -149,12 +147,12 @@ ('opcode', '/usr/lib/python3.11/opcode.py', 'PYMODULE'), ('ast', '/usr/lib/python3.11/ast.py', 'PYMODULE'), ('stringprep', '/usr/lib/python3.11/stringprep.py', 'PYMODULE'), - ('_py_abc', '/usr/lib/python3.11/_py_abc.py', 'PYMODULE'), ('tracemalloc', '/usr/lib/python3.11/tracemalloc.py', 'PYMODULE'), ('pickle', '/usr/lib/python3.11/pickle.py', 'PYMODULE'), ('pprint', '/usr/lib/python3.11/pprint.py', 'PYMODULE'), ('dataclasses', '/usr/lib/python3.11/dataclasses.py', 'PYMODULE'), ('_compat_pickle', '/usr/lib/python3.11/_compat_pickle.py', 'PYMODULE'), + ('_py_abc', '/usr/lib/python3.11/_py_abc.py', 'PYMODULE'), ('logging.handlers', '/usr/lib/python3.11/logging/handlers.py', 'PYMODULE'), ('http.client', '/usr/lib/python3.11/http/client.py', 'PYMODULE'), ('ssl', '/usr/lib/python3.11/ssl.py', 'PYMODULE'), @@ -384,6 +382,8 @@ ('urllib3.contrib.emscripten.fetch', '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/fetch.py', 'PYMODULE'), + ('socket', '/usr/lib/python3.11/socket.py', 'PYMODULE'), + ('selectors', '/usr/lib/python3.11/selectors.py', 'PYMODULE'), ('uuid', '/usr/lib/python3.11/uuid.py', 'PYMODULE'), ('subprocess', '/usr/lib/python3.11/subprocess.py', 'PYMODULE'), ('signal', '/usr/lib/python3.11/signal.py', 'PYMODULE'), @@ -467,25 +467,17 @@ ('libuuid.so.1', '/lib/x86_64-linux-gnu/libuuid.so.1', 'BINARY')], [], [], - [('certifi/py.typed', - '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', - 'DATA'), - ('certifi/cacert.pem', + [('certifi/cacert.pem', '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/cacert.pem', 'DATA'), + ('certifi/py.typed', + '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', + 'DATA'), ('base_library.zip', '/home/halbebruno/Projetos/DNSBlock/agent/build/dnsblock-agent/base_library.zip', 'DATA')], - [('sre_compile', '/usr/lib/python3.11/sre_compile.py', 'PYMODULE'), - ('traceback', '/usr/lib/python3.11/traceback.py', 'PYMODULE'), - ('_weakrefset', '/usr/lib/python3.11/_weakrefset.py', 'PYMODULE'), - ('enum', '/usr/lib/python3.11/enum.py', 'PYMODULE'), - ('keyword', '/usr/lib/python3.11/keyword.py', 'PYMODULE'), - ('stat', '/usr/lib/python3.11/stat.py', 'PYMODULE'), - ('sre_parse', '/usr/lib/python3.11/sre_parse.py', 'PYMODULE'), - ('weakref', '/usr/lib/python3.11/weakref.py', 'PYMODULE'), - ('linecache', '/usr/lib/python3.11/linecache.py', 'PYMODULE'), - ('functools', '/usr/lib/python3.11/functools.py', 'PYMODULE'), + [('copyreg', '/usr/lib/python3.11/copyreg.py', 'PYMODULE'), + ('genericpath', '/usr/lib/python3.11/genericpath.py', 'PYMODULE'), ('encodings.zlib_codec', '/usr/lib/python3.11/encodings/zlib_codec.py', 'PYMODULE'), @@ -716,26 +708,34 @@ ('encodings.ascii', '/usr/lib/python3.11/encodings/ascii.py', 'PYMODULE'), ('encodings.aliases', '/usr/lib/python3.11/encodings/aliases.py', 'PYMODULE'), ('encodings', '/usr/lib/python3.11/encodings/__init__.py', 'PYMODULE'), - ('abc', '/usr/lib/python3.11/abc.py', 'PYMODULE'), - ('locale', '/usr/lib/python3.11/locale.py', 'PYMODULE'), - ('ntpath', '/usr/lib/python3.11/ntpath.py', 'PYMODULE'), - ('io', '/usr/lib/python3.11/io.py', 'PYMODULE'), - ('reprlib', '/usr/lib/python3.11/reprlib.py', 'PYMODULE'), - ('heapq', '/usr/lib/python3.11/heapq.py', 'PYMODULE'), - ('sre_constants', '/usr/lib/python3.11/sre_constants.py', 'PYMODULE'), - ('collections.abc', '/usr/lib/python3.11/collections/abc.py', 'PYMODULE'), - ('collections', '/usr/lib/python3.11/collections/__init__.py', 'PYMODULE'), - ('posixpath', '/usr/lib/python3.11/posixpath.py', 'PYMODULE'), ('_collections_abc', '/usr/lib/python3.11/_collections_abc.py', 'PYMODULE'), - ('codecs', '/usr/lib/python3.11/codecs.py', 'PYMODULE'), + ('traceback', '/usr/lib/python3.11/traceback.py', 'PYMODULE'), + ('types', '/usr/lib/python3.11/types.py', 'PYMODULE'), + ('warnings', '/usr/lib/python3.11/warnings.py', 'PYMODULE'), + ('keyword', '/usr/lib/python3.11/keyword.py', 'PYMODULE'), + ('posixpath', '/usr/lib/python3.11/posixpath.py', 'PYMODULE'), + ('_weakrefset', '/usr/lib/python3.11/_weakrefset.py', 'PYMODULE'), + ('stat', '/usr/lib/python3.11/stat.py', 'PYMODULE'), + ('sre_compile', '/usr/lib/python3.11/sre_compile.py', 'PYMODULE'), + ('sre_constants', '/usr/lib/python3.11/sre_constants.py', 'PYMODULE'), + ('heapq', '/usr/lib/python3.11/heapq.py', 'PYMODULE'), ('operator', '/usr/lib/python3.11/operator.py', 'PYMODULE'), - ('copyreg', '/usr/lib/python3.11/copyreg.py', 'PYMODULE'), ('re._parser', '/usr/lib/python3.11/re/_parser.py', 'PYMODULE'), ('re._constants', '/usr/lib/python3.11/re/_constants.py', 'PYMODULE'), ('re._compiler', '/usr/lib/python3.11/re/_compiler.py', 'PYMODULE'), ('re._casefix', '/usr/lib/python3.11/re/_casefix.py', 'PYMODULE'), ('re', '/usr/lib/python3.11/re/__init__.py', 'PYMODULE'), - ('genericpath', '/usr/lib/python3.11/genericpath.py', 'PYMODULE'), - ('types', '/usr/lib/python3.11/types.py', 'PYMODULE'), - ('warnings', '/usr/lib/python3.11/warnings.py', 'PYMODULE'), + ('functools', '/usr/lib/python3.11/functools.py', 'PYMODULE'), + ('sre_parse', '/usr/lib/python3.11/sre_parse.py', 'PYMODULE'), + ('weakref', '/usr/lib/python3.11/weakref.py', 'PYMODULE'), + ('collections.abc', '/usr/lib/python3.11/collections/abc.py', 'PYMODULE'), + ('collections', '/usr/lib/python3.11/collections/__init__.py', 'PYMODULE'), + ('locale', '/usr/lib/python3.11/locale.py', 'PYMODULE'), + ('enum', '/usr/lib/python3.11/enum.py', 'PYMODULE'), + ('io', '/usr/lib/python3.11/io.py', 'PYMODULE'), + ('codecs', '/usr/lib/python3.11/codecs.py', 'PYMODULE'), + ('ntpath', '/usr/lib/python3.11/ntpath.py', 'PYMODULE'), + ('abc', '/usr/lib/python3.11/abc.py', 'PYMODULE'), + ('reprlib', '/usr/lib/python3.11/reprlib.py', 'PYMODULE'), + ('linecache', '/usr/lib/python3.11/linecache.py', 'PYMODULE'), ('os', '/usr/lib/python3.11/os.py', 'PYMODULE')]) diff --git a/agent/build/dnsblock-agent/EXE-00.toc b/agent/build/dnsblock-agent/EXE-00.toc index a24a46a..18a0f92 100644 --- a/agent/build/dnsblock-agent/EXE-00.toc +++ b/agent/build/dnsblock-agent/EXE-00.toc @@ -109,19 +109,19 @@ ('libbz2.so.1.0', '/lib/x86_64-linux-gnu/libbz2.so.1.0', 'BINARY'), ('libssl.so.3', '/lib/x86_64-linux-gnu/libssl.so.3', 'BINARY'), ('libuuid.so.1', '/lib/x86_64-linux-gnu/libuuid.so.1', 'BINARY'), - ('certifi/py.typed', - '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', - 'DATA'), ('certifi/cacert.pem', '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/cacert.pem', 'DATA'), + ('certifi/py.typed', + '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', + 'DATA'), ('base_library.zip', '/home/halbebruno/Projetos/DNSBlock/agent/build/dnsblock-agent/base_library.zip', 'DATA')], [], False, False, - 1765065952, + 1765128812, [('run', '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run', 'EXECUTABLE')], diff --git a/agent/build/dnsblock-agent/PKG-00.toc b/agent/build/dnsblock-agent/PKG-00.toc index 052a3ac..e4418d3 100644 --- a/agent/build/dnsblock-agent/PKG-00.toc +++ b/agent/build/dnsblock-agent/PKG-00.toc @@ -104,12 +104,12 @@ ('libbz2.so.1.0', '/lib/x86_64-linux-gnu/libbz2.so.1.0', 'BINARY'), ('libssl.so.3', '/lib/x86_64-linux-gnu/libssl.so.3', 'BINARY'), ('libuuid.so.1', '/lib/x86_64-linux-gnu/libuuid.so.1', 'BINARY'), - ('certifi/py.typed', - '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', - 'DATA'), ('certifi/cacert.pem', '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/cacert.pem', 'DATA'), + ('certifi/py.typed', + '/home/halbebruno/Projetos/DNSBlock/agent/build_venv/lib/python3.11/site-packages/certifi/py.typed', + 'DATA'), ('base_library.zip', '/home/halbebruno/Projetos/DNSBlock/agent/build/dnsblock-agent/base_library.zip', 'DATA')], diff --git a/agent/build/dnsblock-agent/base_library.zip b/agent/build/dnsblock-agent/base_library.zip index 169a604..abd5048 100644 Binary files a/agent/build/dnsblock-agent/base_library.zip and b/agent/build/dnsblock-agent/base_library.zip differ diff --git a/agent/build/dnsblock-agent/dnsblock-agent.pkg b/agent/build/dnsblock-agent/dnsblock-agent.pkg index 77508fe..16ac8dd 100644 Binary files a/agent/build/dnsblock-agent/dnsblock-agent.pkg and b/agent/build/dnsblock-agent/dnsblock-agent.pkg differ diff --git a/agent/build/dnsblock-agent/xref-dnsblock-agent.html b/agent/build/dnsblock-agent/xref-dnsblock-agent.html index 0e82578..a16977b 100644 --- a/agent/build/dnsblock-agent/xref-dnsblock-agent.html +++ b/agent/build/dnsblock-agent/xref-dnsblock-agent.html @@ -151,6 +151,7 @@ imports: • enumfunctoolsgenericpath + • grpheapqiojson @@ -163,6 +164,7 @@ imports: • operatorosposixpath + • pwdpyi_rth_inspect.pyrere._casefix @@ -171,6 +173,7 @@ imports: • re._parserreprlibrequests + • socketsre_compilesre_constantssre_parse @@ -5517,7 +5520,8 @@ imported by: grp (builtin module)
imported by: - pathlib + main.py + • pathlibshutilsubprocesstarfile @@ -7274,6 +7278,7 @@ imported by: pwd (builtin module)
imported by: getpass + • main.pynetrcpathlibposixpath @@ -8230,6 +8235,7 @@ imported by: • ftplibhttp.clientlogging.handlers + • main.pyplatformrequests.adaptersrequests.utils diff --git a/agent/config.json.example b/agent/config.json.example index 0a1251e..18993c1 100644 --- a/agent/config.json.example +++ b/agent/config.json.example @@ -1,6 +1,7 @@ { - "serial_key": "YOUR_SERIAL_KEY_HERE", + "serial_key": "SUA_CHAVE_SERIAL_AQUI", "api_url": "https://app.dnsblock.com.br", - "rpz_file": "/etc/unbound/rpz.zones/rpz.dnsblock.zone", - "reload_command": "systemctl reload unbound" + "rpz_file": "/opt/dnsblock/rpz/rpz.dnsblock.zone", + "reload_command": "systemctl reload unbound", + "file_owner": "unbound:unbound" } diff --git a/agent/main.py b/agent/main.py index c9e0bc2..8802fe4 100644 --- a/agent/main.py +++ b/agent/main.py @@ -3,17 +3,17 @@ import sys import time import json import uuid +import socket +import pwd +import grp import requests import logging from datetime import datetime - -# Configuration -CONFIG_FILE = '/opt/dnsblock/config.json' -import logging from logging.handlers import RotatingFileHandler # Configuration CONFIG_FILE = '/opt/dnsblock/config.json' +STATE_FILE = '/opt/dnsblock/rpz_state.json' LOG_DIR = '/opt/dnsblock/logs' LOG_FILE = os.path.join(LOG_DIR, 'agent.log') RPZ_FILE_DEFAULT = '/opt/dnsblock/rpz.dnsblock.zone' @@ -66,32 +66,142 @@ def load_config(): logging.error(f"Error loading config: {e}") sys.exit(1) -def generate_rpz(domains, output_file): - """Generates the RPZ zone file.""" +def get_system_domain(): + """Detects the system domain from FQDN or hostname.""" + try: + fqdn = socket.getfqdn() + if fqdn and '.' in fqdn: + # Extract domain portion (e.g., server.gtecnet.com.br -> gtecnet.com.br) + parts = fqdn.split('.') + if len(parts) >= 2: + domain = '.'.join(parts[1:]) + logging.info(f"Detected system domain: {domain}") + return domain + + # Try hostname as fallback + hostname = socket.gethostname() + if hostname and '.' in hostname: + parts = hostname.split('.') + if len(parts) >= 2: + domain = '.'.join(parts[1:]) + logging.info(f"Detected system domain from hostname: {domain}") + return domain + + logging.info("Could not detect system domain, using localhost") + return "localhost" + except Exception as e: + logging.warning(f"Error detecting system domain: {e}, using localhost") + return "localhost" + +def load_rpz_state(): + """Loads RPZ state from JSON file.""" + if not os.path.exists(STATE_FILE): + return {'last_serial': None, 'last_date': None} + + try: + with open(STATE_FILE, 'r') as f: + return json.load(f) + except Exception as e: + logging.warning(f"Error loading RPZ state: {e}") + return {'last_serial': None, 'last_date': None} + +def save_rpz_state(state): + """Saves RPZ state to JSON file.""" + try: + with open(STATE_FILE, 'w') as f: + json.dump(state, f) + except Exception as e: + logging.error(f"Error saving RPZ state: {e}") + +def generate_serial(): + """Generates serial in YYYYMMDDNN format.""" + today = datetime.now().strftime('%Y%m%d') + state = load_rpz_state() + + last_serial = state.get('last_serial') + last_date = state.get('last_date') + + if last_date == today and last_serial: + # Same day, increment sequence + sequence = int(str(last_serial)[-2:]) + 1 + if sequence > 99: + sequence = 99 # Cap at 99 + else: + # New day, reset sequence + sequence = 0 + + new_serial = int(f"{today}{sequence:02d}") + + # Save state + save_rpz_state({'last_serial': new_serial, 'last_date': today}) + + return new_serial + +def apply_file_ownership(path, file_owner): + """Apply user:group ownership to a file or directory.""" + if not file_owner: + return True + + try: + parts = file_owner.split(':') + user = parts[0] + group = parts[1] if len(parts) > 1 else user + + uid = pwd.getpwnam(user).pw_uid + gid = grp.getgrnam(group).gr_gid + + os.chown(path, uid, gid) + logging.debug(f"Applied ownership {file_owner} to {path}") + return True + except KeyError as e: + logging.warning(f"User or group not found for file_owner '{file_owner}': {e}") + return False + except PermissionError as e: + logging.warning(f"Permission denied setting ownership on {path}: {e}") + return False + except Exception as e: + logging.error(f"Error applying ownership to {path}: {e}") + return False + +def generate_rpz(domains, output_file, file_owner=None): + """Generates the RPZ zone file with proper SOA header.""" try: # Ensure directory exists - os.makedirs(os.path.dirname(output_file), exist_ok=True) + output_dir = os.path.dirname(output_file) + if output_dir: + os.makedirs(output_dir, exist_ok=True) + # Apply ownership to directory + if file_owner: + apply_file_ownership(output_dir, file_owner) + + # Get system domain and generate serial + system_domain = get_system_domain() + serial = generate_serial() with open(output_file, 'w') as f: - f.write("$TTL 30\n") - f.write("@ IN SOA rpz.dnsblock.zone. root.rpz.dnsblock.zone. (\n") - f.write(f" {int(time.time())} ; Serial\n") - f.write(" 30 ; Refresh\n") - f.write(" 15 ; Retry\n") - f.write(" 604800 ; Expire\n") - f.write(" 30 ) ; Negative Cache TTL\n") - f.write(";\n") - f.write("@ IN NS localhost.\n") - f.write("@ IN A 127.0.0.1\n") - f.write(";\n") - f.write("; Blocked Domains\n") + f.write("$TTL 1H\n") + f.write(f"@ IN SOA localhost. {system_domain}. (\n") + f.write(f" {serial} ; Serial\n") + f.write(" 1h ; Refresh\n") + f.write(" 15m ; Retry\n") + f.write(" 30d ; Expire\n") + f.write(" 2h ; Negative Cache TTL\n") + f.write(" )\n") + f.write(f" NS {system_domain}.\n") + f.write("\n") + f.write("; RPZ block hosts\n") + f.write("\n") for domain in domains: # CNAME to . (NXDOMAIN) or specific sinkhole f.write(f"{domain} CNAME .\n") f.write(f"*.{domain} CNAME .\n") - logging.info(f"RPZ file generated at {output_file} with {len(domains)} domains.") + # Apply ownership to file + if file_owner: + apply_file_ownership(output_file, file_owner) + + logging.info(f"RPZ file generated at {output_file} with {len(domains)} domains (serial: {serial}).") return True except Exception as e: logging.error(f"Error generating RPZ: {e}") @@ -106,6 +216,7 @@ def main(): api_url = config.get('api_url') rpz_file = config.get('rpz_file', RPZ_FILE_DEFAULT) reload_command = config.get('reload_command') + file_owner = config.get('file_owner') # Ex: 'unbound:unbound' if not serial_key or not api_url: logging.error("Missing serial_key or api_url in config.") @@ -140,7 +251,7 @@ def main(): if current_checksum != last_checksum: logging.info("Change detected in domain list. Updating RPZ...") domains = data.get('domains', []) - if generate_rpz(domains, rpz_file): + if generate_rpz(domains, rpz_file, file_owner): last_checksum = current_checksum if reload_command: diff --git a/agent/pkg/install.sh b/agent/pkg/install.sh index 1c94c8a..9348881 100644 --- a/agent/pkg/install.sh +++ b/agent/pkg/install.sh @@ -1,58 +1,459 @@ #!/bin/bash -# DNSBlock Agent Installer (Binary Distribution) +# ============================================================================ +# 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" -SOURCE_DIR=$(pwd) +RPZ_DIR="$INSTALL_DIR/rpz" +SERVICE_FILE="/etc/systemd/system/dnsblock-agent.service" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Check root -if [ "$(id -u)" -ne 0 ]; then - echo "Please run as root" - exit 1 -fi +# Configurações do Unbound +UNBOUND_CONF_DIR="/etc/unbound/unbound.conf.d" +UNBOUND_MAIN_CONF="/etc/unbound/unbound.conf" +DNSBLOCK_CONF="$UNBOUND_CONF_DIR/dnsblock.conf" +DNSBLOCK_INCLUDE='include: "/etc/unbound/unbound.conf.d/dnsblock.conf"' -echo "Installing DNSBlock Agent..." +# Variáveis globais +SERIAL="" +NO_SERIAL=false -# Create Directory -mkdir -p "$INSTALL_DIR" +# ============================================================================ +# FUNÇÕES UTILITÁRIAS +# ============================================================================ -# Copy Binary -if [ -f "$SOURCE_DIR/dnsblock-agent" ]; then - cp "$SOURCE_DIR/dnsblock-agent" "$INSTALL_DIR/" - chmod +x "$INSTALL_DIR/dnsblock-agent" -else - echo "Error: dnsblock-agent binary not found in current directory." - exit 1 -fi - -# Create Config if not exists -if [ ! -f "$INSTALL_DIR/config.json" ]; then - echo "Creating default config..." - if [ -f "$SOURCE_DIR/config.json.example" ]; then - cp "$SOURCE_DIR/config.json.example" "$INSTALL_DIR/config.json" - else - cat < "$INSTALL_DIR/config.json" -{ - "serial_key": "YOUR_SERIAL_KEY_HERE", - "api_url": "https://seu-painel.com", - "rpz_file": "/opt/dnsblock/rpz.dnsblock.zone" -} -EOF +# 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 - echo "Config file created at $INSTALL_DIR/config.json. Please edit it with your Serial Key and API URL." -fi +} -# Setup Systemd Service -echo "Setting up systemd service..." -if [ -f "$SOURCE_DIR/dnsblock-agent.service" ]; then - cp "$SOURCE_DIR/dnsblock-agent.service" /etc/systemd/system/ - systemctl daemon-reload - systemctl enable dnsblock-agent +# Verificar dependências +verificar_dependencias() { + local deps_faltando="" - echo "Installation Complete!" - echo "1. Edit /opt/dnsblock/config.json" - echo "2. Start service: systemctl start dnsblock-agent" -else - echo "Error: dnsblock-agent.service not found." - exit 1 -fi + if ! command -v whiptail &> /dev/null; then + echo "Erro: whiptail não encontrado. Instale-o antes de continuar." + exit 1 + fi + + if ! command -v tar &> /dev/null; then + deps_faltando="$deps_faltando tar" + 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 serial="" + + serial=$(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 + # Usuário cancelou com ESC ou Cancel + whiptail --title "Instalação Cancelada" \ + --msgbox "A instalação foi cancelada pelo usuário." 8 45 + exit 0 + fi + + if [ -z "$serial" ]; then + # Serial em branco - perguntar se deseja continuar + 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 + echo "" + else + whiptail --title "Instalação Cancelada" \ + --msgbox "A instalação foi cancelada pelo usuário." 8 45 + exit 0 + fi + else + echo "$serial" + fi +} + +# Atualizar o serial no config.json +atualizar_config_serial() { + local serial="$1" + local config_file="$INSTALL_DIR/config.json" + + if [ -f "$config_file" ]; then + sed -i "s/\"serial_key\":[[:space:]]*\"[^\"]*\"/\"serial_key\": \"$serial\"/" "$config_file" + fi +} + +# ============================================================================ +# FUNÇÕES DE CONFIGURAÇÃO DO UNBOUND +# ============================================================================ + +# Encontrar o arquivo de configuração apropriado do Unbound +encontrar_arquivo_config_unbound() { + local config_file="" + + # Verificar se existe algum arquivo com configuração RPZ + for conf in "$UNBOUND_CONF_DIR"/*.conf; do + if [ -f "$conf" ] && grep -q "rpz:" "$conf" 2>/dev/null; then + config_file="$conf" + break + fi + done + + # Se não encontrou arquivo com RPZ, usar local.conf se existir + if [ -z "$config_file" ]; then + if [ -f "$UNBOUND_CONF_DIR/local.conf" ]; then + config_file="$UNBOUND_CONF_DIR/local.conf" + elif [ -f "$UNBOUND_MAIN_CONF" ]; then + config_file="$UNBOUND_MAIN_CONF" + fi + fi + + echo "$config_file" +} + +# Verificar se o include já existe +verificar_include_existente() { + local include_pattern='include:[[:space:]]*"/etc/unbound/unbound.conf.d/dnsblock.conf"' + + if [ -f "$UNBOUND_MAIN_CONF" ] && grep -qE "$include_pattern" "$UNBOUND_MAIN_CONF" 2>/dev/null; then + return 0 + fi + + for conf in "$UNBOUND_CONF_DIR"/*.conf; do + if [ -f "$conf" ] && grep -qE "$include_pattern" "$conf" 2>/dev/null; then + return 0 + fi + done + + return 1 +} + +# Configurar o Unbound para usar o DNSBlock +configurar_unbound() { + # Criar diretório de configuração do Unbound se não existir + if [ ! -d "$UNBOUND_CONF_DIR" ]; then + mkdir -p "$UNBOUND_CONF_DIR" + chown unbound:unbound "$UNBOUND_CONF_DIR" + fi + + # Criar o diretório RPZ + mkdir -p "$RPZ_DIR" + chown unbound:unbound "$RPZ_DIR" + + # Criar arquivo de configuração do DNSBlock para o Unbound + cat > "$DNSBLOCK_CONF" << 'EOF' +rpz: + name: rpz.dnsblock.zone + zonefile: /opt/dnsblock/rpz/rpz.dnsblock.zone + rpz-action-override: nxdomain +EOF + + chown unbound:unbound "$DNSBLOCK_CONF" + chmod 644 "$DNSBLOCK_CONF" + + # Adicionar include se não existir + if ! verificar_include_existente; then + local config_file=$(encontrar_arquivo_config_unbound) + + if [ -z "$config_file" ]; then + return 1 + fi + + if grep -q "rpz:" "$config_file" 2>/dev/null; then + sed -i "/rpz:/i # DNSBlock\n$DNSBLOCK_INCLUDE\n" "$config_file" + else + echo "" >> "$config_file" + echo "# DNSBlock" >> "$config_file" + echo "$DNSBLOCK_INCLUDE" >> "$config_file" + fi + 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 configuração do Unbound + if ! unbound-checkconf &>/dev/null; then + rm -f "$DNSBLOCK_CONF" + remover_include_unbound + return 1 + fi + + # Reiniciar o Unbound + systemctl restart unbound 2>/dev/null || true + + return 0 +} + +# Remover o include do DNSBlock dos arquivos de configuração +remover_include_unbound() { + local include_pattern='include:[[:space:]]*"/etc/unbound/unbound.conf.d/dnsblock.conf"' + local comment_pattern='#[[:space:]]*DNSBlock' + + if [ -f "$UNBOUND_MAIN_CONF" ]; then + sed -i "/$include_pattern/d" "$UNBOUND_MAIN_CONF" + sed -i "/$comment_pattern/d" "$UNBOUND_MAIN_CONF" + fi + + for conf in "$UNBOUND_CONF_DIR"/*.conf; do + if [ -f "$conf" ] && [ "$conf" != "$DNSBLOCK_CONF" ]; then + sed -i "/$include_pattern/d" "$conf" + sed -i "/$comment_pattern/d" "$conf" + fi + done +} + +# Remover configuração do Unbound +remover_configuracao_unbound() { + remover_include_unbound + + if [ -f "$DNSBLOCK_CONF" ]; then + rm -f "$DNSBLOCK_CONF" + fi + + 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" + fi + + # Solicitar serial + SERIAL=$(solicitar_serial) + + # Exibir progresso + { + echo "10"; echo "# Criando diretórios..." + mkdir -p "$INSTALL_DIR" + mkdir -p "$INSTALL_DIR/logs" + mkdir -p "$INSTALL_DIR/rpz" + sleep 0.3 + + echo "30"; echo "# Copiando arquivos..." + # Copiar todos os arquivos exceto o service file e o próprio install.sh + for item in "$SCRIPT_DIR"/*; do + local basename=$(basename "$item") + if [ "$basename" != "dnsblock-agent.service" ] && [ "$basename" != "install.sh" ]; then + cp -r "$item" "$INSTALL_DIR/" + fi + done + sleep 0.3 + + echo "50"; echo "# Configurando permissões..." + chmod +x "$INSTALL_DIR/dnsblock-agent" 2>/dev/null || true + touch "$INSTALL_DIR/logs/agent.log" + chown -R unbound:unbound "$INSTALL_DIR" + chmod 755 "$INSTALL_DIR" + sleep 0.3 + + echo "60"; echo "# Configurando serial..." + if [ -n "$SERIAL" ]; then + atualizar_config_serial "$SERIAL" + fi + sleep 0.3 + + echo "70"; echo "# Instalando serviço..." + if [ -f "$SCRIPT_DIR/dnsblock-agent.service" ]; then + mv "$SCRIPT_DIR/dnsblock-agent.service" "$SERVICE_FILE" + fi + systemctl daemon-reload + systemctl enable dnsblock-agent + sleep 0.3 + + echo "85"; echo "# Configurando Unbound..." + configurar_unbound || true + sleep 0.3 + + echo "95"; echo "# Finalizando..." + if [ -n "$SERIAL" ]; then + systemctl start dnsblock-agent + fi + sleep 0.3 + + echo "100"; echo "# Concluído!" + } | whiptail --title "DNSBlock Agent - Instalação" --gauge "Iniciando instalação..." 8 60 0 + + # 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" ] && [ ! -f "$DNSBLOCK_CONF" ]; 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"; echo "# Concluído!" + } | 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