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:
• enum
• functools
• genericpath
+ • grp
• heapq
• io
• json
@@ -163,6 +164,7 @@ imports:
• operator
• os
• posixpath
+ • pwd
• pyi_rth_inspect.py
• re
• re._casefix
@@ -171,6 +173,7 @@ imports:
• re._parser
• reprlib
• requests
+ • socket
• sre_compile
• sre_constants
• sre_parse
@@ -5517,7 +5520,8 @@ imported by:
grp (builtin module)
imported by:
-
pathlib
+
main.py
+ •
pathlib
•
shutil
•
subprocess
•
tarfile
@@ -7274,6 +7278,7 @@ imported by:
pwd (builtin module)
imported by:
getpass
+ •
main.py
•
netrc
•
pathlib
•
posixpath
@@ -8230,6 +8235,7 @@ imported by:
•
ftplib
•
http.client
•
logging.handlers
+ •
main.py
•
platform
•
requests.adapters
•
requests.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