Ajustes gerais

This commit is contained in:
2025-12-08 07:22:44 -03:00
parent c0681ea6a9
commit 7e529c8460
10 changed files with 630 additions and 111 deletions

View File

@@ -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')])

View File

@@ -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')],

View File

@@ -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')],

View File

@@ -151,6 +151,7 @@ imports:
&#8226; <a href="#enum">enum</a>
&#8226; <a href="#functools">functools</a>
&#8226; <a href="#genericpath">genericpath</a>
&#8226; <a href="#grp">grp</a>
&#8226; <a href="#heapq">heapq</a>
&#8226; <a href="#io">io</a>
&#8226; <a href="#json">json</a>
@@ -163,6 +164,7 @@ imports:
&#8226; <a href="#operator">operator</a>
&#8226; <a href="#os">os</a>
&#8226; <a href="#posixpath">posixpath</a>
&#8226; <a href="#pwd">pwd</a>
&#8226; <a href="#pyi_rth_inspect.py">pyi_rth_inspect.py</a>
&#8226; <a href="#re">re</a>
&#8226; <a href="#re._casefix">re._casefix</a>
@@ -171,6 +173,7 @@ imports:
&#8226; <a href="#re._parser">re._parser</a>
&#8226; <a href="#reprlib">reprlib</a>
&#8226; <a href="#requests">requests</a>
&#8226; <a href="#socket">socket</a>
&#8226; <a href="#sre_compile">sre_compile</a>
&#8226; <a href="#sre_constants">sre_constants</a>
&#8226; <a href="#sre_parse">sre_parse</a>
@@ -5517,7 +5520,8 @@ imported by:
<a name="grp"></a>
<tt>grp</tt> <span class="moduletype"><i>(builtin module)</i></span> <div class="import">
imported by:
<a href="#pathlib">pathlib</a>
<a href="#main.py">main.py</a>
&#8226; <a href="#pathlib">pathlib</a>
&#8226; <a href="#shutil">shutil</a>
&#8226; <a href="#subprocess">subprocess</a>
&#8226; <a href="#tarfile">tarfile</a>
@@ -7274,6 +7278,7 @@ imported by:
<tt>pwd</tt> <span class="moduletype"><i>(builtin module)</i></span> <div class="import">
imported by:
<a href="#getpass">getpass</a>
&#8226; <a href="#main.py">main.py</a>
&#8226; <a href="#netrc">netrc</a>
&#8226; <a href="#pathlib">pathlib</a>
&#8226; <a href="#posixpath">posixpath</a>
@@ -8230,6 +8235,7 @@ imported by:
&#8226; <a href="#ftplib">ftplib</a>
&#8226; <a href="#http.client">http.client</a>
&#8226; <a href="#logging.handlers">logging.handlers</a>
&#8226; <a href="#main.py">main.py</a>
&#8226; <a href="#platform">platform</a>
&#8226; <a href="#requests.adapters">requests.adapters</a>
&#8226; <a href="#requests.utils">requests.utils</a>

View File

@@ -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"
}

View File

@@ -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:

View File

@@ -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 <<EOF > "$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