add project files: docker-compose, env example, README, ufw setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker 2026-04-16 12:09:13 +02:00
parent 8c8befc827
commit 59dd9a6350
4 changed files with 306 additions and 0 deletions

14
.env.example Normal file
View File

@ -0,0 +1,14 @@
# Datenbank Passwörter - BITTE ÄNDERN!
MYSQL_ROOT_PASSWORD=root_password_changeme
MYSQL_PASSWORD=npm_password_changeme
# Datenbank Einstellungen
MYSQL_DATABASE=npm
MYSQL_USER=npm
MYSQL_HOST=npm-db
MYSQL_PORT=3306
# Ports
HTTP_PORT=80
HTTPS_PORT=443
WEBUI_PORT=81

36
README.md Normal file
View File

@ -0,0 +1,36 @@
# Nginx Proxy Manager
## Verzeichnisstruktur
```
npm/
├── docker-compose.yml
├── .env
├── data/ # NPM Konfiguration (auto-erstellt)
├── letsencrypt/ # SSL Zertifikate (auto-erstellt)
└── mysql/ # Datenbank (auto-erstellt)
```
## Setup
1. Passwörter in `.env` anpassen
2. Starten:
```bash
docker compose up -d
```
3. Web-UI aufrufen: http://<server-ip>:81
## Standard Login (SOFORT ÄNDERN!)
- Email: admin@example.com
- Passwort: changeme
## Ports
- 80 → HTTP (Weiterleitungen + Let's Encrypt Challenge)
- 443 → HTTPS
- 81 → NPM Web-UI
## Neue Domain einrichten
1. "Proxy Hosts" → "Add Proxy Host"
2. Domain eintragen z.B. nextcloud.example.de
3. Ziel-IP + Port eintragen
4. Tab "SSL" → Let's Encrypt Zertifikat anfordern

42
docker-compose.yml Normal file
View File

@ -0,0 +1,42 @@
version: '3.8'
services:
npm:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "${HTTP_PORT}:80" # HTTP
- "${HTTPS_PORT}:443" # HTTPS
- "${WEBUI_PORT}:81" # NPM Web-UI
environment:
DB_MYSQL_HOST: "${MYSQL_HOST}"
DB_MYSQL_PORT: ${MYSQL_PORT}
DB_MYSQL_USER: "${MYSQL_USER}"
DB_MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
DB_MYSQL_NAME: "${MYSQL_DATABASE}"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
depends_on:
- npm-db
networks:
- npm-network
npm-db:
image: jc21/mariadb-aria:latest
container_name: nginx-proxy-manager-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
MYSQL_DATABASE: "${MYSQL_DATABASE}"
MYSQL_USER: "${MYSQL_USER}"
MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
volumes:
- ./mysql:/var/lib/mysql
networks:
- npm-network
networks:
npm-network:
driver: bridge

214
setup-ufw.sh Normal file
View File

@ -0,0 +1,214 @@
#!/bin/bash
# =============================================================================
# UFW Firewall Setup + Docker DOCKER-USER Chain
# =============================================================================
# wg0 = öffentliches Interface (getunnelte öffentliche IP)
# → Nur Port 80 + 443 für Docker Container erlaubt
# Internes Interface (eth0/ens*) = lokales Netz
# → Alles erlaubt (SSH, WireGuard, etc.)
#
# Ansatz: DOCKER-USER Chain statt "iptables: false"
# → Docker Networking bleibt intakt (Container können kommunizieren)
# → UFW kontrolliert welche Ports extern erreichbar sind
# =============================================================================
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${GREEN}[✓]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
error() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
info() { echo -e "${BLUE}[i]${NC} $1"; }
# Root check
[ "$EUID" -ne 0 ] && error "Bitte als root ausführen: sudo $0"
# =============================================================================
# Interfaces ermitteln
# =============================================================================
INTERNAL_IF=$(ip route show default 2>/dev/null | grep -oP 'dev \K\S+' | head -1)
[ -z "$INTERNAL_IF" ] && error "Konnte internes Interface nicht ermitteln"
PUBLIC_IF="wg0"
info "Öffentliches Interface (WireGuard): ${PUBLIC_IF}"
info "Internes Interface: ${INTERNAL_IF}"
# Prüfen ob wg0 existiert
if ! ip link show wg0 &>/dev/null; then
warn "wg0 existiert noch nicht - Regeln werden trotzdem gesetzt"
fi
echo ""
warn "Dieses Script wird UFW neu konfigurieren und Docker anpassen."
warn "Bestehende UFW Regeln werden GELÖSCHT."
read -p "Fortfahren? (j/N): " confirm
[[ "$confirm" != "j" && "$confirm" != "J" ]] && { info "Abgebrochen."; exit 0; }
# =============================================================================
# TEIL 1: Docker iptables reaktivieren
# =============================================================================
echo ""
info "=== TEIL 1: Docker iptables reaktivieren ==="
if systemctl is-active --quiet docker; then
warn "Stoppe Docker kurz..."
systemctl stop docker
DOCKER_WAS_RUNNING=true
fi
DOCKER_DAEMON=/etc/docker/daemon.json
if [ -f "$DOCKER_DAEMON" ] && grep -q '"iptables".*false' "$DOCKER_DAEMON"; then
cp "$DOCKER_DAEMON" "${DOCKER_DAEMON}.bak.$(date +%Y%m%d_%H%M%S)"
python3 -c "
import json
with open('$DOCKER_DAEMON', 'r') as f:
config = json.load(f)
config.pop('iptables', None)
with open('$DOCKER_DAEMON', 'w') as f:
json.dump(config, f, indent=2)
"
log "iptables: false aus daemon.json entfernt"
else
log "Docker iptables bereits aktiv"
fi
# =============================================================================
# TEIL 2: UFW Grundkonfiguration
# =============================================================================
echo ""
info "=== TEIL 2: UFW Konfiguration ==="
ufw --force reset
log "UFW zurückgesetzt"
ufw default deny incoming
ufw default allow outgoing
log "Default Policy: deny incoming, allow outgoing"
# =============================================================================
# TEIL 3: Regeln für wg0 (öffentliches Interface)
# =============================================================================
echo ""
info "=== TEIL 3: Regeln für ${PUBLIC_IF} (öffentlich) ==="
# Nur HTTP + HTTPS auf wg0
ufw allow in on "$PUBLIC_IF" to any port 80 proto tcp comment 'HTTP public (wg0)'
ufw allow in on "$PUBLIC_IF" to any port 443 proto tcp comment 'HTTPS public (wg0)'
log "Port 80+443 (HTTP/HTTPS) auf ${PUBLIC_IF} geöffnet"
# =============================================================================
# TEIL 4: Regeln für internes Interface
# =============================================================================
echo ""
info "=== TEIL 4: Regeln für ${INTERNAL_IF} (intern) ==="
ufw allow in on "$INTERNAL_IF" comment 'Internes Netz - alles erlaubt'
log "Alles auf ${INTERNAL_IF} erlaubt (SSH, WireGuard, etc.)"
# =============================================================================
# TEIL 5: DOCKER-USER Chain (Docker Port-Kontrolle)
# =============================================================================
echo ""
info "=== TEIL 5: DOCKER-USER Chain ==="
AFTER_RULES=/etc/ufw/after.rules
cp "$AFTER_RULES" "${AFTER_RULES}.bak.$(date +%Y%m%d_%H%M%S)"
# Alte Docker-Fixes aus before.rules entfernen (falls vorhanden)
BEFORE_RULES=/etc/ufw/before.rules
if grep -q "DOCKER-UFW-FIX" "$BEFORE_RULES"; then
cp "$BEFORE_RULES" "${BEFORE_RULES}.bak.$(date +%Y%m%d_%H%M%S)"
TMPFILE=$(mktemp)
awk '/# DOCKER-UFW-FIX/{skip=1} skip && /^COMMIT/{skip=0; next} !skip' "$BEFORE_RULES" > "$TMPFILE"
mv "$TMPFILE" "$BEFORE_RULES"
warn "Alte Docker-Fixes aus before.rules entfernt"
fi
# DOCKER-USER Regeln in after.rules einfügen
if grep -q "DOCKER-USER-FIX" "$AFTER_RULES"; then
warn "DOCKER-USER Regeln bereits vorhanden - überspringe"
else
cat >> "$AFTER_RULES" << EOF
# DOCKER-USER-FIX - Docker Container Port-Kontrolle
# Kontrolliert welcher externe Traffic Docker Container erreicht
# Docker Networking bleibt intakt, nur der Zugang von aussen wird gefiltert
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -m conntrack --ctstate RELATED,ESTABLISHED
-A DOCKER-USER -p tcp -m tcp --dport 80 -i ${PUBLIC_IF} -j RETURN
-A DOCKER-USER -p tcp -m tcp --dport 443 -i ${PUBLIC_IF} -j RETURN
-A DOCKER-USER -i ${INTERNAL_IF} -j RETURN
-A DOCKER-USER -j DROP
COMMIT
EOF
log "DOCKER-USER Regeln in after.rules eingefügt"
fi
# =============================================================================
# TEIL 6: IP Forwarding aktivieren
# =============================================================================
echo ""
info "=== TEIL 6: IP Forwarding ==="
SYSCTL_CONF=/etc/ufw/sysctl.conf
if grep -q "^net/ipv4/ip_forward=1" "$SYSCTL_CONF" 2>/dev/null; then
log "IP Forwarding bereits aktiv"
else
sed -i 's/#net\/ipv4\/ip_forward=1/net\/ipv4\/ip_forward=1/' "$SYSCTL_CONF"
grep -q "net/ipv4/ip_forward=1" "$SYSCTL_CONF" || echo "net/ipv4/ip_forward=1" >> "$SYSCTL_CONF"
log "IP Forwarding aktiviert"
fi
if ! grep -q "net.ipv4.ip_forward = 1" /etc/sysctl.conf; then
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
fi
sysctl -p /etc/sysctl.conf > /dev/null 2>&1
log "sysctl neu geladen"
# =============================================================================
# TEIL 7: UFW aktivieren und Docker neu starten
# =============================================================================
echo ""
info "=== TEIL 7: Aktivierung ==="
ufw --force enable
log "UFW aktiviert"
if [ "$DOCKER_WAS_RUNNING" = true ]; then
systemctl start docker
sleep 3
log "Docker neu gestartet"
fi
# =============================================================================
# ZUSAMMENFASSUNG
# =============================================================================
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} UFW Setup abgeschlossen!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
info "Ansatz: DOCKER-USER Chain (Docker Networking intakt)"
echo ""
info "Interface-Zuordnung:"
info " ${PUBLIC_IF} (öffentlich) → nur Port 80 + 443"
info " ${INTERNAL_IF} (intern) → alles erlaubt"
echo ""
info "Docker Container:"
info " Extern (${PUBLIC_IF}) → nur Port 80 + 443 erreichbar"
info " Intern (${INTERNAL_IF}) → alle Ports erreichbar"
echo ""
info "Aktive Regeln:"
ufw status verbose
echo ""
warn "WICHTIG: Teste SSH Verbindung in einem NEUEN Terminal bevor du dieses schließt!"
warn "Falls Docker Container kein Internet haben: 'docker restart <container>'"