commit ce5674cb6c6ae9cc490c8a648a7b8f203a0b4e62 Author: Vish Date: Sun Jan 18 08:12:38 2026 +0000 Initial commit: Pi-hole baremetal one-liner installer - Supports Ubuntu, Debian, Fedora, Rocky, CentOS, RHEL, Arch, openSUSE - Automatic OS detection and dependency installation - Automatic network interface and IP detection - Configurable upstream DNS servers - Firewall configuration (firewalld/ufw/iptables) - Management helper script (pihole-manage) - Unattended installation option - FreeBSD redirects to AdGuard Home alternative diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28d1a6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# OS files +.DS_Store +Thumbs.db + +# Editor files +*.swp +*.swo +*~ +.idea/ +.vscode/ + +# Logs +*.log + +# Temp files +*.tmp +*.bak diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7f969f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Vish + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a955a4b --- /dev/null +++ b/README.md @@ -0,0 +1,212 @@ +# pihole-baremetal + +One-command Pi-hole installation on bare metal - network-wide ad blocking DNS server. + +## Quick Install + +```bash +curl -fsSL -H "Authorization: token 77e3ddaf262bb94f6fa878ca449cc1aa1129a00d" \ + "https://git.vish.gg/Vish/pihole-baremetal/raw/branch/main/install.sh" | sudo bash +``` + +### Install Options + +```bash +# Fully unattended (no prompts, uses defaults) +curl ... | sudo bash -s -- --unattended + +# Without web interface (DNS only) +curl ... | sudo bash -s -- --no-lighttpd + +# Custom upstream DNS +curl ... | sudo bash -s -- --dns1 8.8.8.8 --dns2 8.8.4.4 + +# Specify interface and IP +curl ... | sudo bash -s -- --interface eth0 --ipv4 192.168.1.10/24 + +# Force install on unsupported OS +curl ... | sudo bash -s -- --skip-os-check --unattended + +# Combine options +curl ... | sudo bash -s -- --unattended --dns1 9.9.9.9 --dns2 149.112.112.112 +``` + +## What It Does + +**Zero intervention required.** The installer automatically: + +- Detects your OS (Ubuntu, Debian, Fedora, Rocky, CentOS, Arch, openSUSE) +- Installs all required dependencies +- Detects your network interface and IP address +- Configures Pi-hole with sensible defaults +- Sets up firewall rules (firewalld/ufw/iptables) +- Creates a management helper script + +**Run one command, wait 2-3 minutes, done.** + +## Supported Systems + +| OS | Version | Status | +|----|---------|--------| +| Ubuntu | 20.04, 22.04, 24.04+ | ✅ Official | +| Debian | 10, 11, 12+ | ✅ Official | +| Raspberry Pi OS | All | ✅ Official | +| Fedora | 38+ | ✅ Official | +| CentOS / RHEL | 8, 9+ | ✅ Official | +| Rocky Linux | 8, 9+ | ✅ Official | +| AlmaLinux | 8, 9+ | ✅ Official | +| Linux Mint | 20, 21, 22+ | ✅ Works | +| Pop!_OS | 22.04+ | ✅ Works | +| Arch Linux | Rolling | ⚠️ Experimental | +| Manjaro | Rolling | ⚠️ Experimental | +| openSUSE | Leap/Tumbleweed | ⚠️ Experimental | +| FreeBSD | - | ❌ Use AdGuard Home | + +## Services & Ports + +| Service | Port | Description | +|---------|------|-------------| +| DNS | 53/tcp, 53/udp | DNS resolver | +| Web Admin | 80/tcp | Admin interface | +| FTL | 4711/tcp | Pi-hole FTL API | + +## Post-Install Configuration + +### Set Admin Password + +```bash +pihole -a -p +``` + +### Configure Clients + +Set your devices or router's DNS server to your Pi-hole's IP address. + +**Router (recommended):** Set Pi-hole IP as the primary DNS in your router's DHCP settings. All devices will automatically use Pi-hole. + +**Individual devices:** Manually set DNS to Pi-hole IP in network settings. + +### Add Custom Blocklists + +1. Open Admin Panel → Group Management → Adlists +2. Add URLs of blocklists +3. Run `pihole -g` to update gravity + +Popular blocklist sources: +- https://firebog.net/ (curated lists) +- https://github.com/StevenBlack/hosts + +## Management Commands + +### Native Pi-hole Commands + +```bash +pihole status # Show status +pihole -c # Console dashboard (live) +pihole -up # Update Pi-hole +pihole -g # Update blocklists (gravity) +pihole -q example.com # Query logs for domain +pihole -w example.com # Whitelist domain +pihole -b example.com # Blacklist domain +pihole enable # Enable blocking +pihole disable # Disable blocking +pihole disable 5m # Disable for 5 minutes +pihole restartdns # Restart DNS resolver +pihole -a -p # Change admin password +pihole -t # Tail the log +``` + +### Helper Script Commands + +```bash +pihole-manage status # Show Pi-hole and service status +pihole-manage logs # Tail Pi-hole log +pihole-manage ftl-logs # Tail FTL log +pihole-manage stats # Show statistics +pihole-manage top-ads # Show top blocked domains +pihole-manage gravity # Update blocklists +pihole-manage backup # Create backup +pihole-manage password # Change admin password +``` + +## File Locations + +| Path | Description | +|------|-------------| +| `/etc/pihole/` | Pi-hole configuration | +| `/etc/pihole/setupVars.conf` | Installation settings | +| `/etc/pihole/custom.list` | Local DNS records | +| `/etc/dnsmasq.d/` | Dnsmasq configuration | +| `/var/log/pihole/` | Log files | +| `/var/log/pihole/pihole.log` | Query log | +| `/var/log/pihole/FTL.log` | FTL engine log | +| `/opt/pihole/` | Pi-hole scripts | + +## Troubleshooting + +### Check if Pi-hole is running + +```bash +pihole status +systemctl status pihole-FTL +``` + +### DNS not resolving + +```bash +# Check if FTL is listening +ss -tulpn | grep ':53' + +# Test DNS resolution +dig @127.0.0.1 google.com + +# Restart services +pihole restartdns +``` + +### Web interface not loading + +```bash +# Check lighttpd status +systemctl status lighttpd + +# Restart lighttpd +systemctl restart lighttpd +``` + +### View logs for errors + +```bash +pihole -t # Tail query log +tail -f /var/log/pihole/FTL.log # FTL log +journalctl -u pihole-FTL -f # Systemd journal +``` + +### Reset admin password + +```bash +pihole -a -p +``` + +## Uninstall + +```bash +pihole uninstall +``` + +This will remove Pi-hole and optionally its dependencies. + +## Upstream DNS Options + +| Provider | DNS 1 | DNS 2 | +|----------|-------|-------| +| Cloudflare | 1.1.1.1 | 1.0.0.1 | +| Google | 8.8.8.8 | 8.8.4.4 | +| Quad9 | 9.9.9.9 | 149.112.112.112 | +| OpenDNS | 208.67.222.222 | 208.67.220.220 | +| Cloudflare (malware) | 1.1.1.2 | 1.0.0.2 | +| Cloudflare (family) | 1.1.1.3 | 1.0.0.3 | + +## License + +MIT diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..485462b --- /dev/null +++ b/install.sh @@ -0,0 +1,646 @@ +#!/bin/bash +# ============================================================================= +# Pi-hole Baremetal Installer +# ============================================================================= +# Network-wide ad blocking DNS server - installs directly on the system +# +# Supported: Ubuntu, Debian, Fedora, Rocky/Alma/RHEL 8+, Arch, openSUSE, FreeBSD +# Works on minimal/headless installs +# +# Usage: +# curl -fsSL /install.sh | sudo bash +# +# Options: +# --unattended Skip all prompts, use defaults +# --no-lighttpd Skip web server (API/admin only via CLI) +# --set-password Set admin password non-interactively (reads from stdin) +# --ipv4 Set static IPv4 address +# --interface Set network interface (e.g., eth0) +# --dns1 Set upstream DNS 1 (default: 1.1.1.1) +# --dns2 Set upstream DNS 2 (default: 1.0.0.1) +# ============================================================================= + +set -o pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' + +log() { echo -e "${BLUE}[INFO]${NC} $1"; } +success() { echo -e "${GREEN}[OK]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; } + +# Configuration defaults +UNATTENDED=false +INSTALL_LIGHTTPD=true +PIHOLE_DNS_1="1.1.1.1" +PIHOLE_DNS_2="1.0.0.1" +INTERFACE="" +IPV4_ADDRESS="" +ADMIN_PASSWORD="" +INSTALL_DIR="/etc/pihole" +PIHOLE_SKIP_OS_CHECK=false + +# Parse arguments +while [ $# -gt 0 ]; do + case $1 in + --unattended) UNATTENDED=true; shift ;; + --no-lighttpd) INSTALL_LIGHTTPD=false; shift ;; + --set-password) ADMIN_PASSWORD="$2"; shift 2 ;; + --ipv4) IPV4_ADDRESS="$2"; shift 2 ;; + --interface) INTERFACE="$2"; shift 2 ;; + --dns1) PIHOLE_DNS_1="$2"; shift 2 ;; + --dns2) PIHOLE_DNS_2="$2"; shift 2 ;; + --skip-os-check) PIHOLE_SKIP_OS_CHECK=true; shift ;; + --help|-h) + echo "Pi-hole Baremetal Installer" + echo "" + echo "Usage: install.sh [options]" + echo "" + echo "Options:" + echo " --unattended Skip prompts, use defaults" + echo " --no-lighttpd Skip web server installation" + echo " --set-password X Set admin password" + echo " --ipv4 Set static IPv4 (e.g., 192.168.1.10/24)" + echo " --interface Set interface (e.g., eth0)" + echo " --dns1 Upstream DNS 1 (default: 1.1.1.1)" + echo " --dns2 Upstream DNS 2 (default: 1.0.0.1)" + echo " --skip-os-check Skip OS compatibility check" + exit 0 + ;; + *) shift ;; + esac +done + +# Check root +[ "$(id -u)" -ne 0 ] && error "Run as root: sudo bash install.sh" + +# Detect OS +detect_os() { + if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$ID + OS_VERSION=${VERSION_ID:-} + OS_NAME="${NAME:-$OS}" + elif [ "$(uname)" = "FreeBSD" ]; then + OS="freebsd" + OS_VERSION=$(freebsd-version -u 2>/dev/null | cut -d'-' -f1) + OS_NAME="FreeBSD" + else + error "Cannot detect OS" + fi + log "Detected: $OS_NAME $OS_VERSION" +} + +# Check if OS is supported by official Pi-hole installer +check_pihole_support() { + case $OS in + ubuntu|debian|raspbian) + # Supported versions + return 0 + ;; + fedora) + return 0 + ;; + centos|rhel|rocky|almalinux) + return 0 + ;; + *) + if [ "$PIHOLE_SKIP_OS_CHECK" = true ]; then + warn "OS '$OS' may not be officially supported by Pi-hole" + warn "Attempting installation anyway (--skip-os-check enabled)" + return 0 + else + return 1 + fi + ;; + esac +} + +# Wait for apt/dpkg locks +wait_for_apt_lock() { + local max_wait=120 + local waited=0 + + while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \ + fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \ + fuser /var/lib/dpkg/lock >/dev/null 2>&1; do + if [ $waited -eq 0 ]; then + log "Waiting for apt/dpkg lock to be released..." + fi + sleep 5 + waited=$((waited + 5)) + if [ $waited -ge $max_wait ]; then + warn "Timeout waiting for apt lock, attempting to continue..." + break + fi + done +} + +# Wait for zypper lock +wait_for_zypper_lock() { + local max_wait=120 + local waited=0 + + while pgrep -x zypper >/dev/null 2>&1; do + if [ $waited -eq 0 ]; then + log "Waiting for zypper lock to be released..." + fi + sleep 5 + waited=$((waited + 5)) + if [ $waited -ge $max_wait ]; then + warn "Timeout waiting for zypper lock..." + break + fi + done +} + +# Install prerequisites +install_prerequisites() { + log "Installing prerequisites..." + + case $OS in + ubuntu|debian|raspbian|linuxmint|pop) + export DEBIAN_FRONTEND=noninteractive + wait_for_apt_lock + apt-get update -qq + apt-get install -y -qq curl ca-certificates git iproute2 procps >/dev/null 2>&1 + ;; + fedora) + dnf install -y -q curl ca-certificates git iproute procps-ng >/dev/null 2>&1 + ;; + rocky|almalinux|rhel|centos) + if command -v dnf >/dev/null 2>&1; then + dnf install -y -q curl ca-certificates git iproute procps-ng >/dev/null 2>&1 + else + yum install -y curl ca-certificates git iproute procps >/dev/null 2>&1 + fi + ;; + arch|manjaro|endeavouros) + pacman -Sy --noconfirm --quiet curl ca-certificates git iproute2 procps-ng >/dev/null 2>&1 + ;; + opensuse*|sles) + wait_for_zypper_lock + zypper install -y curl ca-certificates git iproute2 procps >/dev/null 2>&1 + ;; + freebsd) + env ASSUME_ALWAYS_YES=YES pkg bootstrap >/dev/null 2>&1 || true + pkg install -y bash curl git ca_root_nss >/dev/null 2>&1 + ;; + *) + warn "Unknown OS, attempting to continue..." + ;; + esac + + success "Prerequisites installed" +} + +# Detect network interface +detect_interface() { + if [ -n "$INTERFACE" ]; then + return + fi + + # Try to find the default interface + INTERFACE=$(ip route get 8.8.8.8 2>/dev/null | grep -oP 'dev \K\S+' | head -1) + + if [ -z "$INTERFACE" ]; then + # Fallback: get first non-loopback interface + INTERFACE=$(ip -o link show | awk -F': ' '$2 != "lo" {print $2; exit}') + fi + + if [ -z "$INTERFACE" ]; then + INTERFACE="eth0" + fi + + log "Using interface: $INTERFACE" +} + +# Detect IP address +detect_ip() { + if [ -n "$IPV4_ADDRESS" ]; then + return + fi + + # Get current IP on the detected interface + IPV4_ADDRESS=$(ip -4 addr show "$INTERFACE" 2>/dev/null | grep -oP 'inet \K[\d.]+/\d+' | head -1) + + if [ -z "$IPV4_ADDRESS" ]; then + # Fallback: get any non-loopback IP + IPV4_ADDRESS=$(hostname -I 2>/dev/null | awk '{print $1}') + if [ -n "$IPV4_ADDRESS" ]; then + IPV4_ADDRESS="${IPV4_ADDRESS}/24" + fi + fi + + if [ -z "$IPV4_ADDRESS" ]; then + error "Could not detect IP address. Use --ipv4 to specify." + fi + + log "Using IP: $IPV4_ADDRESS" +} + +# Get gateway +detect_gateway() { + GATEWAY=$(ip route | grep default | awk '{print $3}' | head -1) + if [ -z "$GATEWAY" ]; then + GATEWAY="192.168.1.1" + fi + log "Gateway: $GATEWAY" +} + +# Create Pi-hole setup vars for unattended install +create_setupvars() { + log "Creating Pi-hole configuration..." + + mkdir -p "$INSTALL_DIR" + + # Extract IP without CIDR for some settings + IP_ONLY=$(echo "$IPV4_ADDRESS" | cut -d'/' -f1) + + cat > "$INSTALL_DIR/setupVars.conf" << EOF +PIHOLE_INTERFACE=$INTERFACE +IPV4_ADDRESS=$IPV4_ADDRESS +IPV6_ADDRESS= +PIHOLE_DNS_1=$PIHOLE_DNS_1 +PIHOLE_DNS_2=$PIHOLE_DNS_2 +QUERY_LOGGING=true +INSTALL_WEB_SERVER=$( [ "$INSTALL_LIGHTTPD" = true ] && echo "true" || echo "false" ) +INSTALL_WEB_INTERFACE=$( [ "$INSTALL_LIGHTTPD" = true ] && echo "true" || echo "false" ) +LIGHTTPD_ENABLED=$( [ "$INSTALL_LIGHTTPD" = true ] && echo "true" || echo "false" ) +CACHE_SIZE=10000 +DNS_FQDN_REQUIRED=true +DNS_BOGUS_PRIV=true +DNSMASQ_LISTENING=local +WEBPASSWORD= +BLOCKING_ENABLED=true +EOF + + success "Configuration created at $INSTALL_DIR/setupVars.conf" +} + +# Install Pi-hole using official installer +install_pihole_official() { + log "Installing Pi-hole (official installer)..." + + # Download and run official installer + if [ "$UNATTENDED" = true ]; then + curl -sSL https://install.pi-hole.net | bash /dev/stdin --unattended + else + curl -sSL https://install.pi-hole.net | bash + fi + + if [ $? -ne 0 ]; then + error "Pi-hole installation failed" + fi + + success "Pi-hole installed successfully" +} + +# Install Pi-hole on Arch Linux (manual process) +install_pihole_arch() { + log "Installing Pi-hole on Arch Linux..." + + # Install dependencies + pacman -Sy --noconfirm --needed \ + php php-cgi php-sqlite php-intl \ + lighttpd \ + base-devel git \ + dnsmasq \ + sudo \ + inetutils >/dev/null 2>&1 + + # Clone Pi-hole from AUR or use manual install + if command -v yay >/dev/null 2>&1; then + log "Using yay to install Pi-hole from AUR..." + sudo -u nobody yay -S --noconfirm pi-hole-server pi-hole-ftl 2>/dev/null || true + elif command -v paru >/dev/null 2>&1; then + log "Using paru to install Pi-hole from AUR..." + sudo -u nobody paru -S --noconfirm pi-hole-server pi-hole-ftl 2>/dev/null || true + fi + + # If AUR helpers failed or not available, try official installer with skip + if ! command -v pihole >/dev/null 2>&1; then + warn "AUR installation failed, trying official installer..." + export PIHOLE_SKIP_OS_CHECK=true + curl -sSL https://install.pi-hole.net | PIHOLE_SKIP_OS_CHECK=true bash /dev/stdin --unattended + fi + + success "Pi-hole installed on Arch Linux" +} + +# Install Pi-hole on openSUSE +install_pihole_opensuse() { + log "Installing Pi-hole on openSUSE..." + + wait_for_zypper_lock + + # Install dependencies + zypper install -y \ + php8 php8-cgi php8-sqlite php8-intl \ + lighttpd \ + git curl \ + dnsmasq \ + iproute2 >/dev/null 2>&1 + + # Try official installer with skip + export PIHOLE_SKIP_OS_CHECK=true + curl -sSL https://install.pi-hole.net | PIHOLE_SKIP_OS_CHECK=true bash /dev/stdin --unattended + + success "Pi-hole installed on openSUSE" +} + +# Install Pi-hole on FreeBSD +install_pihole_freebsd() { + log "Installing Pi-hole on FreeBSD..." + + echo "" + echo "========================================" + echo " FreeBSD Pi-hole Installation" + echo "========================================" + echo "" + echo "FreeBSD is not officially supported by Pi-hole." + echo "" + echo "Alternatives for FreeBSD:" + echo "" + echo "1. Use AdGuard Home (better FreeBSD support):" + echo " pkg install adguardhome" + echo " sysrc adguardhome_enable=YES" + echo " service adguardhome start" + echo " # Access at http://localhost:3000" + echo "" + echo "2. Use Unbound + blocklists manually" + echo "" + echo "3. Run Pi-hole in a Linux jail (bhyve/vm)" + echo "" + + # Offer to install AdGuard Home instead + if [ "$UNATTENDED" != true ]; then + read -p "Install AdGuard Home instead? [Y/n] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + pkg install -y adguardhome + sysrc adguardhome_enable=YES + service adguardhome start + success "AdGuard Home installed! Access at http://localhost:3000" + exit 0 + fi + fi + + error "FreeBSD is not supported for Pi-hole baremetal installation" +} + +# Configure firewall +configure_firewall() { + log "Configuring firewall..." + + if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld 2>/dev/null; then + firewall-cmd --permanent --add-service=dns 2>/dev/null || true + firewall-cmd --permanent --add-service=http 2>/dev/null || true + firewall-cmd --permanent --add-service=https 2>/dev/null || true + firewall-cmd --permanent --add-port=53/tcp 2>/dev/null || true + firewall-cmd --permanent --add-port=53/udp 2>/dev/null || true + firewall-cmd --permanent --add-port=80/tcp 2>/dev/null || true + firewall-cmd --permanent --add-port=4711/tcp 2>/dev/null || true # FTL + firewall-cmd --reload 2>/dev/null || true + success "Firewall configured (firewalld)" + elif command -v ufw >/dev/null 2>&1 && ufw status | grep -q "active"; then + ufw allow 53/tcp 2>/dev/null || true + ufw allow 53/udp 2>/dev/null || true + ufw allow 80/tcp 2>/dev/null || true + ufw allow 4711/tcp 2>/dev/null || true + success "Firewall configured (ufw)" + elif command -v iptables >/dev/null 2>&1; then + # Basic iptables rules + iptables -A INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null || true + iptables -A INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || true + iptables -A INPUT -p tcp --dport 80 -j ACCEPT 2>/dev/null || true + iptables -A INPUT -p tcp --dport 4711 -j ACCEPT 2>/dev/null || true + success "Firewall rules added (iptables)" + else + warn "No firewall detected, skipping configuration" + fi +} + +# Set admin password +set_admin_password() { + if [ -n "$ADMIN_PASSWORD" ]; then + log "Setting admin password..." + pihole -a -p "$ADMIN_PASSWORD" + success "Admin password set" + elif [ "$UNATTENDED" != true ]; then + echo "" + echo "Set your Pi-hole admin password:" + pihole -a -p + fi +} + +# Create management script +create_management_script() { + log "Creating management script..." + + cat > /usr/local/bin/pihole-manage << 'EOFSCRIPT' +#!/bin/bash +# Pi-hole management helper + +case "${1:-help}" in + status) + echo "=== Pi-hole Status ===" + pihole status + echo "" + echo "=== Service Status ===" + systemctl status pihole-FTL --no-pager 2>/dev/null || service pihole-FTL status + ;; + logs) + tail -f /var/log/pihole/pihole.log + ;; + ftl-logs) + tail -f /var/log/pihole/FTL.log + ;; + query) + shift + pihole -q "$@" + ;; + update) + pihole -up + ;; + restart) + pihole restartdns + ;; + disable) + duration="${2:-}" + if [ -n "$duration" ]; then + pihole disable "$duration" + else + pihole disable + fi + ;; + enable) + pihole enable + ;; + stats) + pihole -c -e + ;; + top-ads) + echo "=== Top Blocked Domains ===" + pihole -t + ;; + top-clients) + sqlite3 /etc/pihole/pihole-FTL.db \ + "SELECT client, COUNT(*) as count FROM queries GROUP BY client ORDER BY count DESC LIMIT 10;" 2>/dev/null || \ + echo "Database query not available" + ;; + whitelist) + shift + pihole -w "$@" + ;; + blacklist) + shift + pihole -b "$@" + ;; + gravity) + pihole -g + ;; + backup) + timestamp=$(date +"%Y%m%d_%H%M%S") + backup_dir="/var/backups/pihole" + mkdir -p "$backup_dir" + pihole -a -t "$backup_dir/pihole_backup_$timestamp.tar.gz" + echo "Backup created: $backup_dir/pihole_backup_$timestamp.tar.gz" + ;; + password) + pihole -a -p + ;; + *) + echo "Pi-hole Management Helper" + echo "" + echo "Usage: pihole-manage " + echo "" + echo "Commands:" + echo " status Show Pi-hole and service status" + echo " logs Tail the Pi-hole log" + echo " ftl-logs Tail the FTL log" + echo " query Query the log for domain " + echo " update Update Pi-hole" + echo " restart Restart DNS resolver" + echo " disable [t] Disable blocking (optionally for t seconds)" + echo " enable Enable blocking" + echo " stats Show statistics" + echo " top-ads Show top blocked domains" + echo " whitelist Add domain to whitelist" + echo " blacklist Add domain to blacklist" + echo " gravity Update blocklists" + echo " backup Create configuration backup" + echo " password Change admin password" + echo "" + echo "Pi-hole native commands: pihole -h" + ;; +esac +EOFSCRIPT + + chmod +x /usr/local/bin/pihole-manage + success "Management script created: pihole-manage" +} + +# Show completion message +show_complete() { + local IP=$(echo "$IPV4_ADDRESS" | cut -d'/' -f1) + if [ -z "$IP" ]; then + IP=$(hostname -I 2>/dev/null | awk '{print $1}') + fi + + echo "" + echo "========================================" + echo " Pi-hole Installation Complete!" + echo "========================================" + echo "" + echo "Access:" + echo " Admin Panel: http://$IP/admin" + echo " DNS Server: $IP" + echo "" + echo "Configuration:" + echo " Interface: $INTERFACE" + echo " IPv4: $IPV4_ADDRESS" + echo " Upstream DNS: $PIHOLE_DNS_1, $PIHOLE_DNS_2" + echo "" + echo "Commands:" + echo " pihole status - Check status" + echo " pihole -c - Console dashboard" + echo " pihole -up - Update Pi-hole" + echo " pihole -g - Update gravity (blocklists)" + echo " pihole-manage help - Management helper" + echo "" + echo "Client Configuration:" + echo " Set your devices/router DNS to: $IP" + echo "" + + if [ -z "$ADMIN_PASSWORD" ] && [ "$UNATTENDED" = true ]; then + echo "⚠️ Set admin password:" + echo " pihole -a -p" + echo "" + fi + + echo "Logs: /var/log/pihole/" + echo "Config: /etc/pihole/" + echo "" +} + +# Main installation +main() { + echo "" + echo "========================================" + echo " Pi-hole Baremetal Installer" + echo "========================================" + echo "" + + detect_os + install_prerequisites + detect_interface + detect_ip + detect_gateway + + # Create config for unattended install + if [ "$UNATTENDED" = true ]; then + create_setupvars + fi + + # Route to appropriate installer + case $OS in + ubuntu|debian|raspbian|fedora|centos|rhel|rocky|almalinux) + install_pihole_official + ;; + arch|manjaro|endeavouros) + install_pihole_arch + ;; + opensuse*|sles) + install_pihole_opensuse + ;; + freebsd) + install_pihole_freebsd + ;; + linuxmint|pop) + # Mint and Pop are Ubuntu-based, should work + install_pihole_official + ;; + *) + if [ "$PIHOLE_SKIP_OS_CHECK" = true ]; then + warn "Attempting installation on unsupported OS: $OS" + install_pihole_official + else + error "Unsupported OS: $OS. Use --skip-os-check to force installation." + fi + ;; + esac + + configure_firewall + set_admin_password + create_management_script + show_complete +} + +main "$@"