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
This commit is contained in:
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor files
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temp files
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -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.
|
||||||
212
README.md
Normal file
212
README.md
Normal file
@@ -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
|
||||||
646
install.sh
Normal file
646
install.sh
Normal file
@@ -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 <url>/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 <ip> Set static IPv4 address
|
||||||
|
# --interface <if> Set network interface (e.g., eth0)
|
||||||
|
# --dns1 <ip> Set upstream DNS 1 (default: 1.1.1.1)
|
||||||
|
# --dns2 <ip> 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 <ip> Set static IPv4 (e.g., 192.168.1.10/24)"
|
||||||
|
echo " --interface <if> Set interface (e.g., eth0)"
|
||||||
|
echo " --dns1 <ip> Upstream DNS 1 (default: 1.1.1.1)"
|
||||||
|
echo " --dns2 <ip> 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 <command>"
|
||||||
|
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 <d> Query the log for domain <d>"
|
||||||
|
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 "$@"
|
||||||
Reference in New Issue
Block a user