Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added FETCH_HEAD
Empty file.
108 changes: 68 additions & 40 deletions README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@

## ✨ Features

✅ **Ein-Klick-Installation** - Setup mit einem Befehl
✅ **DNS-Sicherheit** - Pi-hole + Unbound mit DNSSEC
✅ **Netzwerk-Monitoring** - NetAlertX Geräte-Tracking
✅ **API-Monitoring** - Python FastAPI + SQLite
✅ **Produktionsbereit** - Systemd-Hardening & Auto-Restart
✅ **Idempotent** - Sicher mehrfach ausführbar
✅ **Ein-Klick-Installation** Setup mit einem Befehl
✅ **DNS-Sicherheit** Pi-hole + Unbound mit DNSSEC
✅ **Netzwerk-Monitoring** NetAlertX Geräte-Tracking
✅ **API-Monitoring** Python FastAPI + SQLite
✅ **Produktionsbereit** Systemd-Hardening & Auto-Restart
✅ **Idempotent** Sicher mehrfach ausführbar

---

Expand All @@ -38,20 +38,20 @@ git clone https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup.git
cd Pi-hole-Unbound-PiAlert-Setup
chmod +x install.sh
sudo ./install.sh
```
````

**Fertig!** 🎉 Ihr kompletter DNS-Sicherheits-Stack läuft jetzt.

---

## 🧰 Was installiert wird

| Komponente | Zweck | Zugriff |
|------------|-------|---------|
| **🕳️ Pi-hole** | DNS-Werbeblocker & Web-UI | `http://[ihre-ip]/admin` |
| **🔐 Unbound** | Rekursiver DNS + DNSSEC | `127.0.0.1:5335` |
| **📡 NetAlertX** | Netzwerkgeräte-Monitoring | `http://[ihre-ip]:20211` |
| **🐍 Python API** | Monitoring & Statistik-API | `http://127.0.0.1:8090` |
| Komponente | Zweck | Zugriff |
| ----------------- | --------------------------------- | ------------------------ |
| **🕳️ Pi-hole** | DNS-Werbeblocker & Web-Oberfläche | `http://[ihre-ip]/admin` |
| **🔐 Unbound** | Rekursiver DNS + DNSSEC | `127.0.0.1:5335` |
| **📡 NetAlertX** | Netzwerkgeräte-Monitoring | `http://[ihre-ip]:20211` |
| **🐍 Python API** | Monitoring- & Statistik-API | `http://127.0.0.1:8090` |

---

Expand All @@ -77,6 +77,7 @@ sudo ./install.sh
```

**Datenfluss:**

1. **Clients** → Pi-hole (DNS-Filterung)
2. **Pi-hole** → Unbound (rekursive Auflösung)
3. **Unbound** → Root-Server (DNSSEC-Validierung)
Expand All @@ -87,15 +88,32 @@ sudo ./install.sh

## 🔌 API-Referenz

#### `GET /leases`

```json
[
{
"ip": "192.168.1.101",
"mac": "aa:bb:cc:dd:ee:ff",
"hostname": "drucker",
"lease_start": "2024-12-21 10:00:00",
"lease_end": "2024-12-21 12:00:00"
}
]
```

### Authentifizierung
Alle Endpunkte benötigen `X-API-Key`-Header:

Alle Endpunkte benötigen den `X-API-Key`-Header:

```bash
curl -H "X-API-Key: ihr-api-key" http://127.0.0.1:8090/endpoint
curl -H "X-API-Key: $SUITE_API_KEY" http://127.0.0.1:8090/endpoint
```

### Endpunkte

#### `GET /health`

```json
{
"ok": true,
Expand All @@ -105,6 +123,7 @@ curl -H "X-API-Key: ihr-api-key" http://127.0.0.1:8090/endpoint
```

#### `GET /dns?limit=50`

```json
[
{
Expand All @@ -117,6 +136,7 @@ curl -H "X-API-Key: ihr-api-key" http://127.0.0.1:8090/endpoint
```

#### `GET /devices`

```json
[
{
Expand All @@ -130,6 +150,7 @@ curl -H "X-API-Key: ihr-api-key" http://127.0.0.1:8090/endpoint
```

#### `GET /stats`

```json
{
"total_dns_logs": 1250,
Expand All @@ -143,21 +164,24 @@ curl -H "X-API-Key: ihr-api-key" http://127.0.0.1:8090/endpoint
## 🛠️ Manuelle Schritte (Optional)

### Pi-hole-Konfiguration
1. Admin-Interface aufrufen: `http://[ihre-ip]/admin`

1. Admin-Oberfläche aufrufen: `http://[ihre-ip]/admin`
2. **Einstellungen → DNS** navigieren
3. **Custom upstream** prüfen: `127.0.0.1#5335`
4. Geräte konfigurieren, um Pi-hole als DNS-Server zu verwenden
3. **Custom Upstream** setzen: `127.0.0.1#5335`
4. Geräte im Netzwerk konfigurieren, um Pi-hole als DNS-Server zu nutzen

### NetAlertX-Setup
- Dashboard aufrufen: `http://[ihre-ip]:20211`
- Scan-Zeitpläne und Benachrichtigungen konfigurieren
- Netzwerk-Topologie und Geräteliste überprüfen

* Dashboard aufrufen: `http://[ihre-ip]:20211`
* Scan-Zeitpläne und Benachrichtigungen konfigurieren
* Netzwerk-Topologie und Geräteliste prüfen

---

## 🧪 Gesundheitschecks & Problembehandlung

### Schneller Gesundheitscheck

```bash
# Unbound testen
dig @127.0.0.1 -p 5335 example.com
Expand All @@ -173,6 +197,7 @@ curl -H "X-API-Key: $SUITE_API_KEY" http://127.0.0.1:8090/health
```

### Service-Verwaltung

```bash
# Services prüfen
systemctl status pihole-suite unbound pihole-FTL
Expand All @@ -190,32 +215,35 @@ docker restart netalertx

### Häufige Probleme

| Problem | Lösung |
|---------|--------|
| **Port 53 belegt** | `sudo systemctl stop systemd-resolved` |
| **API-Key fehlt** | `.env`-Datei prüfen oder mit Installer neu generieren |
| **Datenbankfehler** | `python scripts/bootstrap.py` ausführen |
| **Unbound startet nicht** | `/etc/unbound/unbound.conf.d/pi-hole.conf` prüfen |
| Problem | Lösung |
| ------------------------- | -------------------------------------------------------------------------------------------- |
| **Port 53 belegt** | `sudo systemctl stop systemd-resolved` *(ggf. dauerhaft: disable + /etc/resolv.conf prüfen)* |
| **API-Key fehlt** | `.env`-Datei prüfen oder mit Installer neu generieren |
| **Datenbankfehler** | `python scripts/bootstrap.py` ausführen |
| **Unbound startet nicht** | `/etc/unbound/unbound.conf.d/pi-hole.conf` prüfen |

---

## 🧯 Sicherheitshinweise

### 🔐 API-Sicherheit
- **API-Keys** werden automatisch generiert (16-Byte Hex)
- **CORS** nur für localhost aktiviert
- **Authentifizierung** für alle Endpunkte erforderlich

* **API-Keys** werden automatisch generiert (16-Byte Hex)
* **CORS** nur für localhost aktiviert
* **Authentifizierung** für alle Endpunkte erforderlich

### 🛡️ Systemd-Hardening
- **NoNewPrivileges** verhindert Rechte-Eskalation
- **ProtectSystem=strict** Schreibschutz für Dateisystem
- **PrivateTmp** isolierte temporäre Verzeichnisse
- **Memory-Limits** verhindern Ressourcen-Erschöpfung

* `NoNewPrivileges` verhindert Rechte-Eskalation
* `ProtectSystem=strict` schützt das Dateisystem
* `PrivateTmp` isoliert temporäre Verzeichnisse
* Speicherlimits verhindern Ressourcenüberlastung

### 🔒 Netzwerk-Sicherheit
- **Unbound** nur auf localhost (nicht exponiert)
- **DNS über TLS** zu Upstream-Resolvern
- **DNSSEC**-Validierung aktiviert

* **Unbound** lauscht nur auf `localhost`
* DNS über TLS zu Upstream-Resolvern
* DNSSEC-Validierung ist aktiviert

---

Expand All @@ -231,19 +259,19 @@ docker restart netalertx

## 📜 Lizenz

Dieses Projekt ist unter der **MIT-Lizenz** lizenziert - siehe [LICENSE](LICENSE)-Datei.
Dieses Projekt ist unter der **MIT-Lizenz** lizenziert siehe [LICENSE](LICENSE)-Datei.

---

## 📈 Changelog

Siehe [CHANGELOG.md](CHANGELOG.md) für Versionshistorie und Updates.
Siehe [CHANGELOG.md](CHANGELOG.md) für Versionsverlauf und Updates.

---

<div align="center">

**Mit ❤️ für die Pi-hole-Community erstellt**
**Mit ❤️ für die Pi-hole-Community entwickelt**

[🐛 Bug melden](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/issues) •
[✨ Feature anfordern](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/issues) •
Expand Down
9 changes: 2 additions & 7 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ set -euo pipefail
readonly UNBOUND_PORT=5335
readonly NETALERTX_PORT=20211
readonly PYTHON_SUITE_PORT=8090
readonly NETALERTX_IMAGE="techxartisan/netalertx:latest"
readonly SUITE_API_KEY=${SUITE_API_KEY:-$(openssl rand -hex 16)}
readonly INSTALL_USER=${SUDO_USER:-$(whoami)}
readonly INSTALL_HOME=$(getent passwd "$INSTALL_USER" | cut -d: -f6)
readonly PROJECT_DIR="$(pwd)"

# 🎨 Colors
Expand All @@ -38,7 +34,6 @@ check_system() {
success "System checks passed"
}

# 🔌 Port conflicts
check_ports() {
step "Checking ports"
local ports=($UNBOUND_PORT $NETALERTX_PORT $PYTHON_SUITE_PORT 53)
Expand All @@ -53,7 +48,6 @@ check_ports() {
install_packages() {
step "Installing system packages"
apt-get update -qq
apt-get install -y unbound unbound-anchor ca-certificates curl dnsutils \
python3 python3-venv python3-pip git docker.io openssl systemd sqlite3
success "System packages installed"
}
Expand Down Expand Up @@ -87,6 +81,7 @@ forward-zone:
forward-tls-upstream: yes
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
# NOTE: This is DoT forwarding to Quad9 (not full recursion to the root); intended.
EOF

unbound-anchor -a /var/lib/unbound/root.key || true
Expand Down Expand Up @@ -174,7 +169,6 @@ ENV
run_health_checks() {
step "Running health checks"
dig +short @127.0.0.1 -p $UNBOUND_PORT example.com | grep -q "." && success "Unbound OK" || error "Unbound FAIL"
pihole status | grep -q "blocking is enabled" && success "Pi-hole OK" || warn "Pi-hole status unclear"
docker ps | grep -q netalertx && success "NetAlertX OK" || warn "NetAlertX missing"
systemctl is-active --quiet pihole-suite && success "Python suite OK" || warn "Python suite not active"
}
Expand All @@ -195,6 +189,7 @@ main() {
check_ports
install_packages
configure_unbound
handle_systemd_resolved
install_pihole
install_netalertx
setup_python_suite
Expand Down
21 changes: 1 addition & 20 deletions pyalloc/allocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
import logging
import threading
from typing import Set
"""Simple IP pool allocator."""
import ipaddress
import logging
import threading

logger = logging.getLogger(__name__)

Expand All @@ -31,16 +27,10 @@ def allocate(self) -> str:

Returns:
String IP address

Raises:
RuntimeError: If no IPs available
"""
def __init__(self, network: str):
self.network = ipaddress.ip_network(network)
self.lock = threading.Lock()
self.allocated = set()

def allocate(self) -> str:
with self.lock:
for ip in self.network.hosts():
ip_str = str(ip)
Expand Down Expand Up @@ -95,12 +85,3 @@ def get_stats(self) -> dict:
"available": total_hosts - allocated_count,
"utilization_percent": (allocated_count / total_hosts) * 100 if total_hosts > 0 else 0
}
logger.info("Allocated IP %s", ip_str)
return ip_str
raise RuntimeError("No free IP addresses")

def release(self, ip: str) -> None:
with self.lock:
if ip in self.allocated:
self.allocated.remove(ip)
logger.info("Released IP %s", ip)
27 changes: 1 addition & 26 deletions pyalloc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
import logging
import sqlite3
import threading
from typing import Optional
"""Placeholder IP allocator worker."""
import logging
import threading
import time
from typing import Optional

from .allocator import IPPool

Expand Down Expand Up @@ -101,25 +98,3 @@ def stop() -> None:
def get_pool() -> Optional[IPPool]:
"""Get the current IP pool instance (for demo/testing)."""
return _pool
_pool = None
_stop_event = threading.Event()


def start(conn, network: str = "192.168.0.0/24"):
del conn # not used yet
global _pool
_pool = IPPool(network)
logger.info("Starting IP allocator for %s", network)
_stop_event.clear()
thread = threading.Thread(target=_run, daemon=True)
thread.start()
return thread


def _run() -> None:
while not _stop_event.is_set():
time.sleep(60)


def stop() -> None:
_stop_event.set()
2 changes: 0 additions & 2 deletions shared/shared_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""Shared configuration for the Pi-hole suite."""

"""
import logging
import os
from pathlib import Path
Expand Down
Loading