initial pfannkuchen

This commit is contained in:
sascha 2026-03-30 15:19:20 +02:00
parent b6dafc7a73
commit 4d305fa19f
99 changed files with 3575 additions and 321 deletions

4
.gitignore vendored
View file

@ -7,11 +7,13 @@ vault-password
*.key *.key
*.pem *.pem
*.pfx *.pfx
id_rsa
id_rsa.pub
# Ansible temporäre Dateien # Ansible temporäre Dateien
*.retry *.retry
.ansible/ .ansible/
backup/
# Editor # Editor
.vscode/ .vscode/
*.swp *.swp

198
.kiro/settings/lsp.json Normal file
View file

@ -0,0 +1,198 @@
{
"languages": {
"typescript": {
"name": "typescript-language-server",
"command": "typescript-language-server",
"args": [
"--stdio"
],
"file_extensions": [
"ts",
"js",
"tsx",
"jsx"
],
"project_patterns": [
"package.json",
"tsconfig.json"
],
"exclude_patterns": [
"**/node_modules/**",
"**/dist/**"
],
"multi_workspace": false,
"initialization_options": {
"preferences": {
"disableSuggestions": false
}
},
"request_timeout_secs": 60
},
"java": {
"name": "jdtls",
"command": "jdtls",
"args": [],
"file_extensions": [
"java"
],
"project_patterns": [
"pom.xml",
"build.gradle",
"build.gradle.kts",
".project"
],
"exclude_patterns": [
"**/target/**",
"**/build/**",
"**/.gradle/**"
],
"multi_workspace": false,
"initialization_options": {
"settings": {
"java": {
"compile": {
"nullAnalysis": {
"mode": "automatic"
}
},
"configuration": {
"annotationProcessing": {
"enabled": true
}
}
}
}
},
"request_timeout_secs": 60
},
"python": {
"name": "pyright",
"command": "pyright-langserver",
"args": [
"--stdio"
],
"file_extensions": [
"py"
],
"project_patterns": [
"pyproject.toml",
"setup.py",
"requirements.txt",
"pyrightconfig.json"
],
"exclude_patterns": [
"**/__pycache__/**",
"**/venv/**",
"**/.venv/**",
"**/.pytest_cache/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
},
"go": {
"name": "gopls",
"command": "gopls",
"args": [],
"file_extensions": [
"go"
],
"project_patterns": [
"go.mod",
"go.sum"
],
"exclude_patterns": [
"**/vendor/**"
],
"multi_workspace": false,
"initialization_options": {
"usePlaceholders": true,
"completeUnimported": true
},
"request_timeout_secs": 60
},
"ruby": {
"name": "solargraph",
"command": "solargraph",
"args": [
"stdio"
],
"file_extensions": [
"rb"
],
"project_patterns": [
"Gemfile",
"Rakefile"
],
"exclude_patterns": [
"**/vendor/**",
"**/tmp/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
},
"rust": {
"name": "rust-analyzer",
"command": "rust-analyzer",
"args": [],
"file_extensions": [
"rs"
],
"project_patterns": [
"Cargo.toml"
],
"exclude_patterns": [
"**/target/**"
],
"multi_workspace": false,
"initialization_options": {
"cargo": {
"buildScripts": {
"enable": true
}
},
"diagnostics": {
"enable": true,
"enableExperimental": true
},
"workspace": {
"symbol": {
"search": {
"scope": "workspace"
}
}
}
},
"request_timeout_secs": 60
},
"cpp": {
"name": "clangd",
"command": "clangd",
"args": [
"--background-index"
],
"file_extensions": [
"cpp",
"cc",
"cxx",
"c",
"h",
"hpp",
"hxx"
],
"project_patterns": [
"CMakeLists.txt",
"compile_commands.json",
"Makefile"
],
"exclude_patterns": [
"**/build/**",
"**/cmake-build-**/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
}
}
}

371
README.md Normal file
View file

@ -0,0 +1,371 @@
# Ansible Playbooks
Ansible-Setup für Proxmox-VMs, Hetzner-Server und Borg Backup auf Hetzner Storagebox.
## Quickstart
```bash
# Wrapper-Script nutzen
./pfannkuchen.sh setup emby_sascha # VM komplett einrichten
./pfannkuchen.sh backup # Borg auf allen Backup-Hosts
./pfannkuchen.sh update # Alle Hosts updaten
./pfannkuchen.sh gpu tdarr # NVIDIA Setup
./pfannkuchen.sh pve # Proxmox Post-Install
./pfannkuchen.sh passthrough # GPU Passthrough vorbereiten
./pfannkuchen.sh telegraf # Telegraf Monitoring
./pfannkuchen.sh hysteria2 node1 # Hysteria2 + WireGuard
./pfannkuchen.sh ping # Alle Hosts anpingen
./pfannkuchen.sh list # Inventory anzeigen
# Oder direkt mit Ansible
ansible-playbook site.yml -l emby_sascha
ansible-playbook borg-backup.yml -l proxmox
ansible-playbook update.yml
```
## Infrastruktur
### Proxmox Hosts
| Host | IP | GPU |
|-------|--------------|------|
| node1 | 10.5.85.11 | |
| node2 | 10.5.85.12 | |
| node3 | 10.5.85.13 | |
| node4 | 10.5.85.14 | |
| node5 | 10.5.85.15 | A400 |
| node6 | 10.5.85.16 | A400 |
| node7 | 10.5.85.17 | A400 |
### VMs / LXCs
| Name | IP | Gruppe | Zweck |
|-------------|--------------|---------------|------------------------|
| emby-sascha | 10.6.1.103 | media | Emby (Sascha) |
| jellyfin | 10.5.1.112 | media | Jellyfin |
| immich | 10.4.1.107 | media | Immich Fotoverwaltung |
| emby-chris | 10.7.1.106 | media | Emby (Chris) + SMB |
| tdarr | 10.2.1.104 | arr | Tdarr Transcoding |
| arrapps | 10.2.1.100 | arr | Sonarr/Radarr etc. |
| sabnzbd | 10.2.1.119 | arr | SABnzbd |
| dockhand | 10.4.1.116 | docker | Dockhand |
| n8n | 10.4.1.113 | auto | n8n Automation |
| openclaw | 10.4.1.100 | auto | OpenClaw |
| monitoring | 10.1.1.111 | auto | Monitoring Stack |
| automation | 10.1.1.115 | auto | Automation Stack |
| matrix | 10.4.1.110 | communication | Matrix |
### Hetzner
| Name | IP | Zweck |
|-------------|-----------------|--------------------------|
| pfannkuchen | 159.69.245.190 | Caddy Reverse Proxy + VW |
### Hetzner Storagebox
- Host: `u457772-sub3.your-storagebox.de`
- User: `u457772-sub3`
- Port: 23
- SSH Key liegt in `roles/borg/files/` und wird als `/root/.ssh/id_borg` deployed
## Docker Stacks
Jeder Stack hat ein eigenes Git-Repo unter `~/pfannkuchen/github/` und ein eigenes Docker-Netzwerk.
| Stack | VM | Netzwerk | Services |
|------------|--------------|----------------------|-----------------------------------------------------------------|
| pfannkuchen| Hetzner VPS | `proxy_network` | Caddy, Vaultwarden, Homepage |
| monitoring | 10.1.1.111 | `monitoring_network` | Teslamate, Postgres, Grafana, Mosquitto, Prometheus, SNMP-Exporter, InfluxDB, Emby-Exporter, Node-Exporter |
| automation | 10.1.1.115 | `auto_network` | WAHA, Semaphore UI, Patchmon (DB, Redis, Backend, Frontend) |
| n8n | 10.4.1.113 | `n8n_network` | n8n, Postgres |
### Caddy Reverse Proxy (Hetzner VPS)
Caddy läuft auf dem Hetzner VPS und proxied alle Services über WireGuard-IPs:
| Domain | Backend | Bemerkung |
|-------------------------|----------------------------|--------------------|
| tv.sascha-lutz.de | host.docker.internal:18096 | Emby Sascha |
| guck.tv | host.docker.internal:28096 | Emby Chris |
| netzflix.org | host.docker.internal:38096 | Emby Jellyfin |
| vault.sascha-lutz.de | vaultwarden (Container) | Vaultwarden |
| home.sascha-lutz.de | homepage:3000 (Container) | Homepage (Basic Auth) |
| grafana.sascha-lutz.de | grafana:3000 (Container) | Grafana |
| patchmon.sascha-lutz.de | patchmon-frontend:3000 | Patchmon |
| tesla.sascha-lutz.de | teslamate:4000 (Container) | Teslamate (Basic Auth) |
| influx.sascha-lutz.de | influxdb:8086 (Container) | InfluxDB |
| status.guck.tv | 10.200.200.254:3001 | Uptime Kuma |
| n8n.sascha-lutz.de | 10.4.1.113:5678 | n8n |
| docker.sascha-lutz.de | 10.4.1.116:3000 | Dockhand |
| immich.sascha-lutz.de | 10.4.1.107:2283 | Immich |
| dl.guck.tv | 10.2.1.100:5055 | Jellyseerr |
| plappern.com | 10.4.1.110:8008 | Matrix Synapse |
| web.plappern.com | 10.4.1.110:8080 | Matrix Element |
| chat.plappern.com | 10.4.1.110:8090 | Matrix Chat |
| monitor.guck.tv | 10.5.1.102:19999 | Netdata (Basic Auth) |
### Seerr → WhatsApp + Auto-Approve
Kombinierter n8n Workflow der zwei Dinge macht:
**1. Auto-Approve (MEDIA_PENDING):**
```
Jellyseerr → Webhook → n8n → TMDB Genre+FSK Lookup → Kategorisieren → Pfad+Tags setzen → Approve → 20min warten → FHD Radarr/Sonarr Suche
```
Genre-Logik:
| Kategorie | Bedingung | Radarr Pfad | Tag |
|---|---|---|---|
| Horror | Genre "Horror" + FSK ≥ 16 | `/data/UHD/horror` | `horror-4k` |
| Kids | Genre "Animation"/"Family" + FSK ≤ 12 | `/data/UHD/kids-video` | `kids-4k` |
| Normal | Alles andere | `/data/UHD/video` | `video-4k` |
Serien:
| Kategorie | Bedingung | Sonarr Pfad | Tag |
|---|---|---|---|
| Kids | Genre "Animation" + FSK ≤ 12 | `/data/UHD/kids-serien` | `kids-4k` |
| Streaming | Network ist Netflix/HBO/Amazon/Disney+ etc. | `/data/UHD/serien` | `serien-4k` |
| TV | Alles andere | `/data/UHD/tvshows` | `tvshows-4k` |
**2. WhatsApp Newsletter (MEDIA_AVAILABLE):**
```
Jellyseerr → Webhook → n8n → WAHA Session Restart → WAHA sendText → WhatsApp Newsletter
```
- Webhook URL: `http://10.4.1.113:5678/webhook/seerr-notify`
- WAHA: `http://10.1.1.115:3500` (Dashboard: admin / WAHA_API_KEY)
- Newsletter Channel: `120363404705299449@newsletter`
- n8n Workflow: `tmp/n8n-seerr-combined.json`
- Status: **Funktioniert** (Text + Bild-URL, keine eingebetteten Bilder möglich)
### Semaphore UI
Web-UI für Ansible Playbook Management auf `http://10.1.1.115:3000`.
- Login: `admin` / `SEMAPHORE_ADMIN_PASSWORD` (aus `.env`)
- Projekt: Pfannkuchen
- Templates: Setup VM, Update All, Borg Backup, Base Setup, NVIDIA GPU
- Git Repo: `https://github.com/feldjaeger/ansible.git`
### Arr-Stack APIs
| Service | URL | API Key Datei |
|---|---|---|
| Radarr UHD | `http://10.2.1.100:7878` | `tmp/radarr` |
| Radarr FHD | `http://10.2.1.100:7879` | `tmp/radarr1080p` |
| Sonarr UHD | `http://10.2.1.100:8989` | `tmp/sonarr` |
| Sonarr FHD | `http://10.2.1.100:8990` | `tmp/sonarr1080p` |
| Jellyseerr | `http://10.2.1.100:5055` | `tmp/seer` |
| TMDB API | `https://api.themoviedb.org/3` | `tmp/tmdb` (nicht im Repo) |
## ISO Builder
Baut Custom Debian ISOs mit Preseed für unattended Installation. Siehe `iso-builder/README.md`.
```bash
# ISO bauen + auf Proxmox Node uploaden
./iso-builder/build-iso.sh --node 4 --ip 10.4.1.120 --hostname neue-vm
# ISO bauen + VM erstellen + starten
./iso-builder/build-iso.sh --node 4 --ip 10.4.1.120 --hostname neue-vm --create-vm
```
## Netzwerk
### WireGuard Tunnel (Hysteria2)
Alle Proxmox Nodes verbinden sich über Hysteria2 (QUIC) zum VPS. Das umgeht CGNAT-Beschränkungen und versteckt den WireGuard-Traffic vor DPI (Sophos Firewall).
```
[Proxmox Node] → Hysteria2-Client → QUIC (UDP:8443) → Hysteria2-Server (VPS) → WireGuard
```
- Hysteria2 nutzt UDP:8443, Caddy behält TCP:443 + UDP:443 (HTTP/3)
- TLS: Selbstsigniertes Zertifikat auf dem VPS (10 Jahre gültig), Clients mit `insecure: true`
- Bandwidth-Hints konfiguriert für optimale Performance (400 Mbit/s)
| Node | WG-IP | VM-Subnetz | Bemerkung |
|-------|----------------|----------------|------------------|
| VPS | 10.200.200.254 | | WG Server + Hub |
| node1 | 10.200.200.2 | 10.11.1.0/24 | |
| node2 | 10.200.200.3 | 10.2.1.0/24 | Arr-Stack |
| node3 | 10.200.200.113 | 10.3.1.0/24 | |
| node4 | 10.200.200.100 | 10.4.1.0/24 | Docker/Auto |
| node5 | 10.200.200.101 | 10.5.1.0/24 | Media |
| node6 | 10.200.200.116 | 10.6.1.0/24 | A400 |
| node7 | 10.200.200.117 | 10.7.1.0/24 | A400 |
Direkte Peers (kein Hysteria2):
- embyproxy (10.200.200.1)
- Sascha (10.200.200.5), Chris (10.200.200.6), Sascha Handy (10.200.200.7)
- Fassohneboden (10.200.200.4), Marco Minecraft (10.200.200.8)
### Netzwerk-Segmente
| Subnetz | Zweck | Node |
|--------------|----------------|-------|
| 10.5.85.x | Proxmox Hosts | alle |
| 10.1.1.x | Monitoring/Auto| node1 |
| 10.11.1.x | VMs node1 | node1 |
| 10.2.1.x | Arr-Stack | node2 |
| 10.3.1.x | VMs node3 | node3 |
| 10.4.1.x | Docker/Auto | node4 |
| 10.5.1.x | Media VMs | node5 |
| 10.6.1.x | VMs node6 | node6 |
| 10.7.1.x | VMs node7 | node7 |
| 10.200.200.x | WireGuard | VPS |
## Rollen
| Rolle | Zweck |
|---------------------|--------------------------------------------------------------|
| base | Repos, Pakete, Locale, SSH Key, Sudo, QEMU Guest Agent |
| docker | Docker CE + Compose Plugin, User sascha → docker Gruppe |
| nvidia | CUDA Repo, cuda-drivers, Container Toolkit, Docker nvidia-RT |
| borg | SSH Key Deploy, Borg Repo Init, Backup-Script, Systemd Timer |
| hawser | Hawser Install + Systemd Service + Token aus Vault |
| sysctl | BBR, TCP Tuning, Buffer Sizes für Streaming-VMs |
| sysctl_proxmox | Overcommit, File Handles, IP Forward, Bridge-Tuning |
| pve_postinstall | Repos (deb822), Enterprise deaktiviert, Nag-Patch, HA aus |
| pve_gpu_passthrough | IOMMU, VFIO, Nouveau/NVIDIA Blacklist für GPU Passthrough |
| telegraf | InfluxData Repo, Telegraf Config, lm-sensors, Synology SNMP |
| hysteria2 | Hysteria2 Client, Bandwidth-Hints, WireGuard Config |
| hysteria2_server | Hysteria2 Server, selbstsigniertes TLS, systemd Service |
## Playbooks
| Playbook | Wrapper-Befehl | Zweck |
|--------------------------|------------------------|------------------------------------------|
| `site.yml` | `setup <host>` | VM komplett einrichten |
| `base-debian.yml` | `base <host>` | Grundsetup + Docker |
| `nvidia-docker.yml` | `gpu <host>` | NVIDIA Treiber + Docker GPU Runtime |
| `borg-backup.yml` | `backup [host]` | Borg Backup einrichten |
| `hawser.yml` | `hawser <host>` | Hawser installieren |
| `update.yml` | `update [host]` | Dist-Upgrade + Autoremove |
| `sysctl.yaml` | `tune <host>` | Netzwerk-Tuning für Streaming |
| `sysctl-proxmox.yaml` | | Proxmox-Host-Tuning |
| `pve-postinstall.yml` | `pve [host]` | Proxmox Post-Install (Repos, Nag, HA) |
| `pve-gpu-passthrough.yml`| `passthrough [host]` | GPU PCI Passthrough vorbereiten |
| `telegraf.yml` | `telegraf [host]` | Telegraf Monitoring deployen |
| `hysteria2.yml` | | Hysteria2 Client + WireGuard deployen |
| `hysteria2-server.yml` | | Hysteria2 Server auf VPS deployen |
## Inventory-Gruppen
| Gruppe | Hosts | Zweck |
|---------------|-------------------------------------------------|------------------------------|
| proxmox | node1node7 | Alle Proxmox Hosts |
| proxmox_gpu | node2, node4, node6, node7 | Nodes mit A400 GPU |
| nvidia | tdarr, emby-sascha, emby-chris, immich | VMs mit NVIDIA GPU Runtime |
| wireguard | node1node7 | WireGuard Clients |
| media | emby-sascha, jellyfin, immich, emby-chris | Media VMs |
| arr | tdarr, arrapps, sabnzbd | Arr-Stack |
| docker | dockhand | Docker VMs |
| auto | n8n, openclaw, monitoring, automation | Automation |
| communication | matrix | Kommunikation |
| hetzner | pfannkuchen | Hetzner Server |
| frp | emby-sascha, emby-chris, jellyfin | FRP Clients |
| backup | alle (via children) | Borg Backup |
## Borg Backup
Alle Hosts in der Gruppe `backup` bekommen Borg Backup. Die Backup-Quellen sind pro Gruppe konfiguriert:
| Gruppe | Backup-Quellen | Quelle |
|-----------|-----------------------------------------------------------------------------|-------------------------------|
| VMs | `/app-config` | `roles/borg/defaults/main.yml`|
| Proxmox | `/etc/pve /etc/network /etc/wireguard /etc/crontab /etc/fstab /etc/systemd/system/ /etc/iptables /etc/telegraf` | `group_vars/proxmox/borg.yml` |
| Hetzner | `/etc/wireguard /app-config` | `group_vars/hetzner/borg.yml` |
Backup-Script: `/usr/local/bin/borg-backup.sh`
Systemd Timer: täglich 03:00 Uhr (±30min Jitter)
Log: `/var/log/borg-backup.log`
Kompression: lz4
Retention: 7 daily, 4 weekly, 6 monthly
## Telegraf Monitoring
Alle Proxmox Hosts bekommen Telegraf mit InfluxDB v2 Output.
Standard-Inputs: cpu, disk, diskio, kernel, mem, processes, swap, system, nstat, sensors
Sonderfall node2: Zusätzlich Synology NAS SNMP Monitoring (konfiguriert via `host_vars/node2/telegraf.yml`).
## Vault
Vault-Datei: `group_vars/all/vault.yml`
Enthaltene Variablen:
- `vault_hetzner_storage_host` Storagebox Hostname
- `vault_hetzner_storage_user` Storagebox User
- `vault_borg_passphrase` Borg Verschlüsselungspasswort
- `vault_sascha_password` SSH/Become-Passwort für User sascha
- `vault_chris_password` SSH/Become-Passwort für User chris
- `vault_telegraf_influx_token` InfluxDB v2 Token für Telegraf
- `vault_snmp_sec_name` SNMP v3 Security Name (Synology)
- `vault_snmp_auth_password` SNMP v3 Auth Passwort
- `vault_snmp_priv_password` SNMP v3 Privacy Passwort
- `vault_hysteria2_password` Hysteria2 Auth Passwort
- `vault_wireguard_vps_pubkey` WireGuard Public Key des VPS
- `vault_node[1-7]_wg_privkey` WireGuard Private Keys der Nodes
- `vault_hawser_token` Hawser Agent Token für Dockhand
```bash
# Passwörter eintragen und verschlüsseln
ansible-vault edit group_vars/all/vault.yml
ansible-vault encrypt group_vars/all/vault.yml
```
Vault-Passwort liegt in `.vault-password` (in `.gitignore`).
## Verzeichnisstruktur
```
ansible/
├── ansible.cfg
├── pfannkuchen.ini # Inventory
├── pfannkuchen.sh # Wrapper-Script
├── site.yml # Master-Playbook
├── base-debian.yml
├── borg-backup.yml
├── nvidia-docker.yml
├── hawser.yml
├── update.yml
├── sysctl.yaml
├── sysctl-proxmox.yaml
├── pve-postinstall.yml
├── pve-gpu-passthrough.yml
├── telegraf.yml
├── hysteria2.yml
├── hysteria2-server.yml
├── iso-builder/ # Custom Debian ISO Builder
│ ├── build-iso.sh # ISO Build + Upload + VM Create
│ ├── preseed.cfg.tpl # Preseed Template
│ └── output/ # Gebaute ISOs
├── group_vars/
│ ├── all/vault.yml # Vault (verschlüsselt)
│ ├── proxmox/borg.yml # Proxmox Backup-Pfade
│ └── hetzner/borg.yml # Hetzner Backup-Pfade
├── host_vars/
│ ├── node1/wireguard.yml
│ ├── node2/
│ │ ├── telegraf.yml # Synology SNMP
│ │ └── wireguard.yml
│ ├── node3node7/wireguard.yml
│ └── emby_chris/vars.yml # User chris Credentials
├── tmp/
│ ├── n8n-seerr-combined.json # n8n Workflow: Auto-Approve + WhatsApp
│ ├── radarr, sonarr, seer # API Keys
│ ├── radarr1080p, sonarr1080p # FHD API Keys
│ └── n8n # n8n API Key
└── roles/
├── base/tasks/main.yml
├── docker/tasks/main.yml
├── nvidia/{tasks,handlers,defaults}/
├── borg/{tasks,defaults,files,templates}/
├── hawser/tasks/main.yml
├── hawser/handlers/main.yml
├── sysctl/tasks/main.yml
├── sysctl_proxmox/tasks/main.yml
├── pve_postinstall/tasks/main.yml
├── pve_gpu_passthrough/{tasks,handlers}/
├── telegraf/{tasks,handlers,defaults,templates}/
├── hysteria2/{tasks,handlers,defaults}/
└── hysteria2_server/{tasks,handlers,defaults}/
```

View file

@ -1,5 +1,6 @@
[defaults] [defaults]
host_key_checking = false host_key_checking = false
vault_password_file = .vault-password
inventory=pfannkuchen.ini inventory=pfannkuchen.ini
ansible_python_interpreter=/usr/bin/python3 ansible_python_interpreter=/usr/bin/python3
interpreter_python = auto_silent interpreter_python = auto_silent

45
backup.sh Executable file
View file

@ -0,0 +1,45 @@
#!/bin/bash
# === Konfiguration ===
#REPO="root@10.5.85.202:/cluster-backup/$(hostname)"
REPO="ssh://storagebox/home/$(hostname)"
# === Zu sichernde Verzeichnisse ===
BACKUP_PATHS="/etc/wireguard/ /app-config"
# === Archivname mit Datum ===
ARCHIVE="$(hostname)-$(date +%Y-%m-%d_%H-%M)"
# === Logging ===
LOGFILE="/var/log/proxmox-borg-backup.log"
#export BORG_RSH='ssh -i /root/.ssh/id_ed25519'
#export BORG_PASSPHRASE='zUGb7Jbc+cMa8RJ'
# === Borg Backup ausführen ===
echo "[$(date)] Starte Backup: $ARCHIVE" >> $LOGFILE
borg create \
--verbose \
--filter AME \
--remote-path=borg-1.4 \
--stats \
--show-rc \
--compression lz4 \
"$REPO::$ARCHIVE" \
$BACKUP_PATHS >> $LOGFILE 2>&1
BACKUP_RC=$?
# === Alte Backups aufräumen ===
borg prune -v --list "$REPO" \
--keep-daily=7 \
--keep-weekly=4 \
--keep-monthly=6 >> $LOGFILE 2>&1
PRUNE_RC=$?
# === Exit-Code prüfen ===
GLOBAL_RC=$(( BACKUP_RC > PRUNE_RC ? BACKUP_RC : PRUNE_RC ))
echo "[$(date)] Backup beendet mit Code $GLOBAL_RC" >> $LOGFILE
exit $GLOBAL_RC

View file

@ -2,140 +2,6 @@
- name: Basis-Konfiguration für Debian VMs - name: Basis-Konfiguration für Debian VMs
hosts: all hosts: all
become: yes become: yes
vars: roles:
# Pfad auf dem Ansible-LXC (Quelle) - base
source_folder: "/ansible/komodo/" - docker
# Pfad auf der Ziel-VM (Ziel)
dest_folder: "/app-config/komodo/"
tasks:
- name: SSH Key für Benutzer sascha hinterlegen
ansible.posix.authorized_key:
user: chris
state: present
key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}"
- name: Standard Debian Trixie Repositories setzen
copy:
dest: /etc/apt/sources.list
content: |
deb http://ftp.gwdg.de/debian/ trixie main non-free-firmware non-free contrib
deb-src http://ftp.gwdg.de/debian/ trixie main non-free-firmware non-free contrib
deb http://security.debian.org/debian-security trixie-security main non-free-firmware non-free contrib
deb-src http://security.debian.org/debian-security trixie-security main non-free-firmware non-free contrib
deb http://ftp.gwdg.de/debian/ trixie-updates main non-free-firmware non-free contrib
deb-src http://ftp.gwdg.de/debian/ trixie-updates main non-free-firmware non-free contrib
owner: root
group: root
mode: '0644'
register: repo_status
- name: Apt Cache aktualisieren (falls Repos geändert wurden)
apt:
update_cache: yes
when: repo_status.changed
- name: Installiere benötigte Basis-Pakete
apt:
name:
- curl
- gnupg
- ca-certificates
- sudo
- wget
- vim
- mc
state: present
update_cache: yes
- name: Locales-Paket sicherstellen
apt:
name: locales
state: present
- name: en_US.UTF-8 Locale generieren
locale_gen:
name: en_US.UTF-8
state: present
- name: Systemweite Sprache auf en_US.UTF-8 setzen
debconf:
name: locales
question: locales/default_environment_locale
value: en_US.UTF-8
vtype: select
- name: Locale-Datei manuell schreiben (Sicherheitsnetz)
copy:
dest: /etc/default/locale
content: |
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
- name: Verzeichnis für Keyrings erstellen
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Docker GPG Key herunterladen (Modern)
get_url:
url: https://download.docker.com/linux/debian/gpg
dest: /etc/apt/keyrings/docker.asc
mode: '0644'
- name: Docker Repository Datei erstellen
copy:
dest: /etc/apt/sources.list.d/docker.list
content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable"
mode: '0644'
register: docker_repo
- name: Paketliste aktualisieren
apt:
update_cache: yes
when: docker_repo.changed
- name: Docker Engine installieren
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
- name: Zielverzeichnis auf der VM erstellen
file:
path: "{{ dest_folder }}"
state: directory
mode: '0755'
# 2. Sudoers anpassen (Ohne Passwort-Abfrage für die Gruppe sudo)
- name: Gruppe sudo passwortloses sudo erlauben
lineinfile:
path: /etc/sudoers
state: present
regexp: '^%sudo'
line: '%sudo ALL=(ALL:ALL) NOPASSWD: ALL'
validate: '/usr/sbin/visudo -cf %s'
# 3. Sascha in Gruppen stecken
- name: Benutzer sascha zu sudo und docker Gruppen hinzufügen
user:
name: sascha
groups: sudo,docker
append: yes
- name: Unnötige Pakete entfernen
apt:
autoremove: yes
- name: QEMU Guest Agent installieren und starten
apt:
name: qemu-guest-agent
state: present
- name: Agent Dienst aktivieren
service:
name: qemu-guest-agent
state: started
enabled: yes

6
borg-backup.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: Borg Backup Setup
hosts: backup
become: yes
roles:
- borg

69
caddy Normal file
View file

@ -0,0 +1,69 @@
{
metrics
admin :2019
log {
output file /var/log/caddy/caddy_main.log {
roll_size 100MiB
roll_keep 5
roll_keep_for 100d
}
format json
level INFO
}
}
(emby_config) {
log {
output file "/var/log/caddy/{args[0]}.log" {
roll_size 100MiB
roll_keep 5
roll_keep_for 100d
}
format json
}
@compress {
header Content-Type text/*
header Content-Type application/json*
header Content-Type application/javascript*
header Content-Type image/svg+xml
}
encode @compress zstd gzip
reverse_proxy {args[1]} {
flush_interval 10s
header_up X-Accel-Buffering "no"
}
header {
Access-Control-Allow-Origin *
Cache-Control "no-cache, no-transform"
defer
}
}
tunnel.sascha-lutz.de {
@wst {
path /5bb8a961812d2f966e09b2825635a1a1007a283083877ae7873ebda99c514ec7/events
header Upgrade websocket
header Connection *upgrade*
method GET
}
handle @wst {
reverse_proxy tunnel:8080
}
handle {
respond "Not Found" 404
}
}
tv.sascha-lutz.de {
import emby_config tv.sascha-lutz.de 10.6.1.103:8096
}
guck.tv {
import emby_config guck.tv 10.7.1.106:8096
}
netzflix.org {
import emby_config netzflix.org 10.6.1.112:8096
}

6
frp-client.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: frp Client deployen
hosts: frp
become: yes
roles:
- frp_client

6
frp-server.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: frp Server deployen
hosts: pfannkuchen
become: yes
roles:
- frp_server

6
glances.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: Glances Web auf allen Proxmox Nodes
hosts: proxmox
become: yes
roles:
- glances

154
group_vars/all/vault.yml Normal file
View file

@ -0,0 +1,154 @@
$ANSIBLE_VAULT;1.1;AES256
37383338316537366564323266376164623232323230633861636534373236633035333436336531
3937336563656166653061636538613331373166326137640a623362646631373264663939613436
34343939636538383462316532656134396665616438336465366162643230353065343838366638
3934303464616538640a396337363262333135376264353736623734643836393632373730663864
37346536623736663063623739323166636438643666366438356565343464306335343738373165
64393961636664306134313432393864393435616161383437363865663036386164613265383038
38633839373936366138306631336534666537376239323463653339366334623931356638373733
63366138303131363139666239616538313236353365356533653763326435313634613538626433
61623630326434336238646666373834386138623636313636613735616662323661303933623366
31303466306530646563626437623063356363303963393131646135323731373931326333613535
61376234643131356163643837663437396632306266623339336166623534646630623234316539
64373165343137343165366238656130663630343935333966386462373266313430623736333565
34666265383434646361306436306538383336303539316538656461656266633339663861613663
34303133613036643066396562613564353432613632366165613163313634643534396263393336
37373366623133366236613734623238363261363430613537613063613062356136373766653734
36313136383635343931376638613664373635633963626331366162313033356564333862666264
64653435393437353961656136363763363730373063313938386538323365303761616232656566
32313666366334346336383038383531656165333935636139346662633066643936323636306639
34346661303666656562373538313334303331383365366638326166333665326365363765316136
30336237613631366463383237366564333738316461653234373232623564326566363064666437
35663932326638613837306132636166356463353830396131663537313366633938633438383435
35376132313539333431666362376465336538383561356539313362616632363736343162316435
39653932623861363965626661393536346536356562343233306235396334353762333765363039
32303536613137633430333132346263323563363030353664653830646333326231313566363432
32623336643236356336323332363636323632326537636234623062343365643736373230616336
33363765363832333532633337373062303431343837346137626336363763636566633363656539
64653562333834346437383861323731653737356235383533363863646564636437616633363664
30366564623662383563663731366362353135623135663336343032316232643435373137353639
32303438363038616662636561393866653032333364623735376637396466373439646137646632
31313431616263396231383232646237663465333265366636373035633632303033323633323533
30343737626265353364623839336162643733616631323238663732373830643531646136306133
37656130393736663437656139643138363933363636643831373230623437653465666637326438
33393665336330333332323739383539353030323633343237376230333232633231393265373633
61343666663536613332386562663338333263366235353834393262373161346437633639643063
64613661636562333564376639646564623331656265323430313034326235323837353035656338
30336463353766633862663039636133326665326532613231666561653466616364333338656264
30393731373665333461346232313865376261393561323435626562316630633735396536366636
36636339353637636335376630343139303064336132373063643665333136383739313833386439
32353034653430323766303238336130306130353830356230623232356236393336373536346530
32393461656439643431313461316266313861303435613431313231353663393236663962393262
31346233356534653261386465323036616138623534313730616435356434386231383533376663
63303134643232663966613638386566386566333165623938313962313934373039613433643265
32613465383830343930373037643330613136633735306664613664303833633234636661376364
63303764386261336162666161393030363064613934373439383133363364306331353962363733
37366261623532613636353933623662336539363239653266646664353637663139393366663063
63333564313733313561666531663463386438366638343333363363383963373930323435656264
39616364626630653163613961366566386132643666663637336136663730346234323063626334
62313134383966336538393432313035326333666330346464653233306466346232323931356237
34343365303262616333383932393966303931653561336161663836623231653432633063336333
65636134653835616437626339393962356563633431396666353933623263316333366131356135
32323832346266613939373931646535666131303739323162646338363166313163653938376534
37336438613862643261613636356663333933633331616566653934376435633163353931383736
66653134626564653862653030393563363736326439653263306432346665356337306239633031
39306331346164633431343133346361636431356435626564313137666134653035633063383764
30633964326163326439393333666130366161306465643362336332393130303638353936346232
39663065663936613234373461613966303036383334653031646638633261386364636539366465
37326237353736626532303637346537663931626530623333613039313330306263653933636130
37363032633865623734656238323433353533663734343139663037343236343365333462353738
39643634316165396437313738373464613731323931656162366564396539643135613731643339
62373731613432653238613264323238393835663566396230393431363139306437666664303938
65663839663365303533376439323332323236643366333437326261396433623265653130643263
33333637303235383836356333623739646366393062316534336637333463643735333038373663
37333561663065363737363762643736633165636165393663323366343038386332333531653064
62663934346435386665363766633665643362366363666332613139613938313432376130336361
61613531376637636439386235646666373761353139613335393232353066306633313262343633
30323435326632643064373063346439666439643465313264353862636362663237386165646130
64326562336362386439353434313364343365323238653762313364633538656635346264303864
65616632376564356162616165616436653037616437666631303066383434346165316532613630
36363039633633343662303435383730646363373836656635313731376337313130346163333535
36393135306433633964373961613930653339303563393766303833376165626265323130313561
65656562663461356631626362323966396133383933393632386365336566373731623131376162
62313663373430383137306338643032656630613936636462633732336637363762323031343665
32356166373537343765653437333866323861633136613238663639376332363430623634636462
33323232663566633736366336396566303762343531343334376464666462653333356336333163
66623434316630343036383330383531333961313132313566396261326663353661636532313636
62663537363338386639396630356264303733356538366131643562373730373961333736656464
31343538383836353262653233383134343063366534343136646162623637393030633133316533
61643135626666623539613131663537353139376634356266363537326534656339353265636130
30623233393031323132383233376463316639616132333035643332386232663331326162316332
32303437653431626633643336613463373036393530663662336537663239656137306565653265
66613362396331376135336561613633666565363863363930383433393136626332383838356439
38343136353934386530343538646339323137343431353032386537653330656637343465323839
63323262303839303261336566386663316334363366346661323131376138626338633435623331
38643933663134303131373535376636323739333463383834343930336363346435376232363835
61636165316131623032393234333163643764663539633632393236636232356264623964333732
32653331313930636366323361346437303465646631623431313332336164303537326535393538
37623264376530663661613530643731336134626533383134356366393036343133326137396630
33326536643233346466663931386432653762613639333230663466393662386361663131633563
62393137643732623566613266323964663431303638663132636633373161333831613134633334
35383763363366366439396332613534356339326633393830333131333632396233306430343237
63303264396166393433366464303566316233393335396333393337363933373933343663663332
66323232626538366433303334633532393465356166333530376230363032323936353666636362
61363861626562373735633135366333363564663163626434643632323033306561616566396633
30393766613134316431363364336661323231313361636634326566363139343139623334613366
35653730626531366634636361393564366564656634303237653565663361343736313263636362
37316366363965616237306139326338663965373737383066326464383435393039646232303934
36633836383565643737396234343930323034376664653639393337363330636466626437623636
30643136323432653733363765363638363065656261383034663837616534346234356462346163
37316365343762306332313235373565663166316337383938613061666330383030666238343965
63313534383135306133663336343939386464636530636535666432393433346664656663623338
30666630633963303663336433303932366339613736636436373464383266636165666335613430
30343839346262356435393065383434353832623134333535646365353739336638396337356534
65356366656264373638343863353461383835363135306332636635376131346334613138653665
31336536656664346465343331643936666636346439353339373433643033363530343430313032
32343531306537313238623962366666623735613730663463643339323363613434633861343435
35353737646631376435343932613834363031643939363761633339333633633062383835613131
34326131373866333832343133616136306462663230386564306439376137373734326134306139
31326662346134316465616237353530356266356163366237626530343334316566353532653236
63656261366639323535396165366466306631616665336632396236313437623734643138333764
31643935643339306462646338343135663730656563373630303731623739663134373438613438
35666635336465616161653462393537363463366437313135343163386331363830663964373236
37356661336135343231326634343232643261386263373161303564623762353061306431313730
36346332346566636565356334653036633639323935386635656331626139316565323935343032
37666135396631616536643434663935613031643731313631393837313061303963626333343632
31356164376566656230393637626539666331653063373539333638643236616432343761636663
39646632333035303263663665623833613763656337616163663164303330323861366135373862
33346262643636316263383162366430393362626563613737636663366230636162633730383866
37643161306561613164656638623630333262336632313032313832396436636438663063323862
35376335346164333932383438376538663162393831323761313031643834303065663831633766
33386565353565616264386639303463306333396365303764613139326634356438663265373731
32383435383537353431656536383835396432356366623136353566333631306334643061313232
38323834633466613739393535633066333861343036633137396435303632636636306661356461
63646662646233303637326437366633633632623366393630396136633934636435376262323631
39323237383561313364373331396337356137656233633433646361333063633161666631636364
34653865383462663165313431373865393938653431396563373832313962616564366439373336
33303931386363333163666262323832353261386161393462643965333535326636656564383361
62396662656531626566633239383639363962353463623733616566623639323966613636373364
35653866656262346238363062353461336136666161386531666134383964626639346132663766
37333862666366336135373238326335356561366534323139653832353733313364333666383132
36353436343266346534393563366662623639626132393864653935356437356430356130623130
63323137663336393633386431643635666661646330356539306161313336376434646434343165
36653739393861353732613932333430373165633433646133363632303766353263643932626365
31376261616235343765393737313535616166353961393634386135613636613761623064386334
32326230373566616639663531336235336234313661323365343432623066613536616131343136
34643362643932393966393632666264623636313461383534333332623739646433383233383136
65313965376134323834393539333932643366666535666163363261613866373463653137383931
65623864326339353162663464643061653931396133393465313133353861333733373237646362
63663861643539336132306439303963323735643333393563343165626338663233366262386439
63313335383864663231373066653035343664656430313432376137623031626538663039613737
66633938353131656566623134323039363764396136303937333536386434663732343533643666
30306566363138363836663532663932346435353861663466336461333534636464313333663364
62363436336662386666396565623831366333353765646362373534323334633536663462646432
39323865613063313034616239666534343435623166313534323136663838316137623862663863
65393661636430616165656638646335313732656264326638643830663533626135323934303639
31383535383538343834303533313961353737346665663161613261383538333162376430316438
36303963346465316434633263616230633137353633393865393431326361393964366565386235
37366531336138653238653161666232303966326664643663613464306138333935663462313165
66396533343134616261663465373533333039393630656363663363643632646463626638346138
31303865333239353533323838363663343961643035343638306339306134396530623631333762
39333064646134386634643964656636626161366566343934626134666332356636343733623363
66393332633639383236653433616633316538396436343839396538633637333239346238376262
65373538656363373333323663623361366261613035393565656663646561313936643633363632
3565

4
group_vars/arr/vars.yml Normal file
View file

@ -0,0 +1,4 @@
ansible_user: sascha
ansible_become_method: su
ansible_password: "{{ vault_sascha_password }}"
ansible_become_password: "{{ vault_sascha_password }}"

4
group_vars/auto/vars.yml Normal file
View file

@ -0,0 +1,4 @@
ansible_user: sascha
ansible_become_method: su
ansible_password: "{{ vault_sascha_password }}"
ansible_become_password: "{{ vault_sascha_password }}"

View file

@ -0,0 +1,4 @@
ansible_user: sascha
ansible_become_method: su
ansible_password: "{{ vault_sascha_password }}"
ansible_become_password: "{{ vault_sascha_password }}"

View file

@ -0,0 +1,4 @@
ansible_user: sascha
ansible_become_method: su
ansible_password: "{{ vault_sascha_password }}"
ansible_become_password: "{{ vault_sascha_password }}"

View file

@ -0,0 +1,2 @@
---
backup_source: "/etc/wireguard /app-config"

View file

@ -0,0 +1,4 @@
ansible_user: sascha
ansible_become_method: su
ansible_password: "{{ vault_sascha_password }}"
ansible_become_password: "{{ vault_sascha_password }}"

View file

@ -0,0 +1,2 @@
---
backup_source: "/etc/pve /etc/network /etc/wireguard /etc/crontab /etc/fstab /etc/systemd/system/ /etc/iptables /etc/telegraf"

View file

@ -1,14 +1,6 @@
--- ---
- name: Hawser - name: Hawser Setup
hosts: all hosts: all
become: yes become: yes
tasks: roles:
- name: Hawser installieren (offizielles Install-Script) - hawser
ansible.builtin.shell:
cmd: curl -fsSL https://raw.githubusercontent.com/Finsys/hawser/main/scripts/install.sh | bash
- name: Hawser aktivieren und starten
ansible.builtin.systemd:
name: hawser
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,5 @@
ansible_user: chris
ansible_become_method: sudo
ansible_password: "{{ vault_chris_password }}"
ansible_become_password: "{{ vault_chris_password }}"
base_user: chris

View file

@ -0,0 +1,5 @@
ansible_user: chris
ansible_become_method: sudo
ansible_password: "{{ vault_chris_password }}"
ansible_become_password: "{{ vault_chris_password }}"
base_user: chris

View file

@ -0,0 +1,6 @@
---
frp_proxies:
- name: emby-chris
local_ip: "0.0.0.0"
local_port: 8096
remote_port: 28096

View file

@ -0,0 +1,5 @@
ansible_user: chris
ansible_become_method: sudo
ansible_password: "{{ vault_chris_password }}"
ansible_become_password: "{{ vault_chris_password }}"
base_user: chris

View file

@ -0,0 +1,6 @@
---
frp_proxies:
- name: emby-sascha
local_ip: "0.0.0.0"
local_port: 8096
remote_port: 18096

View file

@ -0,0 +1,6 @@
---
frp_proxies:
- name: jellyfin
local_ip: "0.0.0.0"
local_port: 8096
remote_port: 38096

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.11/24"
wireguard_private_key: "{{ vault_node1_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,3 @@
---
telegraf_synology_snmp: true
telegraf_synology_host: "192.168.1.24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.12/24"
wireguard_private_key: "{{ vault_node2_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.13/24"
wireguard_private_key: "{{ vault_node3_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.14/24"
wireguard_private_key: "{{ vault_node4_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.15/24"
wireguard_private_key: "{{ vault_node5_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.16/24"
wireguard_private_key: "{{ vault_node6_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

View file

@ -0,0 +1,4 @@
---
wireguard_address: "10.200.200.17/24"
wireguard_private_key: "{{ vault_node7_wg_privkey }}"
wireguard_allowed_ips: "10.200.200.0/24"

38
hysteria2-cleanup.yml Normal file
View file

@ -0,0 +1,38 @@
---
- name: Hysteria2 von Nodes entfernen
hosts: proxmox
become: yes
tasks:
- name: Hysteria2 Service stoppen und deaktivieren
systemd:
name: hysteria2
state: stopped
enabled: false
ignore_errors: true
- name: WireGuard Override entfernen
file:
path: /etc/systemd/system/wg-quick@wg0.service.d
state: absent
notify: reload systemd
- name: Hysteria2 systemd Unit entfernen
file:
path: /etc/systemd/system/hysteria2.service
state: absent
notify: reload systemd
- name: Hysteria2 Binary entfernen
file:
path: /usr/local/bin/hysteria
state: absent
- name: Hysteria2 Config entfernen
file:
path: /etc/hysteria
state: absent
handlers:
- name: reload systemd
systemd:
daemon_reload: true

1
id_ed25519.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPl/Zptf1zTIgIv01zekT3SqdJfDR10vfTljP1QvHwez sascha@proxy

301
iso-builder/build-iso.sh Executable file
View file

@ -0,0 +1,301 @@
#!/bin/bash
set -e
# ============================================================
# Debian Unattended ISO Builder
# Baut eine Custom Debian ISO mit eingebettetem Preseed
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
WORK_DIR="/tmp/iso-builder-$$"
DEBIAN_VERSION="${DEBIAN_VERSION:-13}"
DEBIAN_CODENAME="${DEBIAN_CODENAME:-trixie}"
ARCH="amd64"
# --- Defaults ---
IP=""
NETMASK="255.255.255.0"
GATEWAY=""
DNS="1.1.1.1"
HOSTNAME="debian"
USER="sascha"
PASSWORD=""
SSH_KEY_FILE="$HOME/.ssh/id_ed25519.pub"
OUTPUT_DIR="$SCRIPT_DIR/output"
NODE=""
VMID=""
CREATE_VM=false
VM_CORES=8
VM_MEMORY=16384
VM_DISK=64
usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Required (either --node or --ip + --gateway):
--node N Proxmox node number (1-7), auto-sets gateway and uploads ISO
--ip IP Static IP address
--gateway GW Gateway address
Optional:
--vmid ID Create VM with this ID (optional, auto-assigns next free if omitted)
--create-vm Create VM on the node (uses --vmid or next free ID)
--cores N CPU cores (default: 8)
--memory MB RAM in MB (default: 16384)
--disk GB Disk size in GB (default: 64)
--netmask MASK Netmask (default: 255.255.255.0)
--dns DNS DNS server (default: 1.1.1.1)
--hostname NAME Hostname (default: debian)
--user USER Username (default: sascha)
--password PASS Password (will be prompted if not set)
--ssh-key FILE SSH public key file (default: ~/.ssh/id_ed25519.pub)
--output DIR Output directory (default: ./output)
--debian-version VER Debian version (default: 13)
Node mapping:
--node 1 → gateway 10.1.1.1 (node1 / 10.5.85.11)
--node 2 → gateway 10.2.1.1 (node2 / 10.5.85.12)
--node 3 → gateway 10.3.1.1 (node3 / 10.5.85.13)
--node 4 → gateway 10.4.1.1 (node4 / 10.5.85.14)
--node 5 → gateway 10.5.1.1 (node5 / 10.5.85.15)
--node 6 → gateway 10.6.1.1 (node6 / 10.5.85.16)
--node 7 → gateway 10.7.1.1 (node7 / 10.5.85.17)
Examples:
$0 --node 4 --ip 10.4.1.120 --hostname neue-vm
$0 --node 4 --ip 10.4.1.120 --hostname neue-vm --vmid 120
$0 --node 7 --ip 10.7.1.110 --hostname chris-vm --user chris --vmid 710
EOF
exit 1
}
# --- Parse Args ---
while [[ $# -gt 0 ]]; do
case $1 in
--ip) IP="$2"; shift 2;;
--netmask) NETMASK="$2"; shift 2;;
--gateway) GATEWAY="$2"; shift 2;;
--dns) DNS="$2"; shift 2;;
--hostname) HOSTNAME="$2"; shift 2;;
--user) USER="$2"; shift 2;;
--password) PASSWORD="$2"; shift 2;;
--ssh-key) SSH_KEY_FILE="$2"; shift 2;;
--output) OUTPUT_DIR="$2"; shift 2;;
--debian-version) DEBIAN_VERSION="$2"; shift 2;;
--node) NODE="$2"; shift 2;;
--vmid) VMID="$2"; CREATE_VM=true; shift 2;;
--create-vm) CREATE_VM=true; shift;;
--cores) VM_CORES="$2"; shift 2;;
--memory) VM_MEMORY="$2"; shift 2;;
--disk) VM_DISK="$2"; shift 2;;
*) echo "Unknown option: $1"; usage;;
esac
done
# --- Node mapping ---
if [[ -n "$NODE" ]]; then
declare -A NODE_GW=(
[1]="10.1.1.1" [2]="10.2.1.1" [3]="10.3.1.1" [4]="10.4.1.1"
[5]="10.5.1.1" [6]="10.6.1.1" [7]="10.7.1.1"
)
declare -A NODE_IP=(
[1]="10.5.85.11" [2]="10.5.85.12" [3]="10.5.85.13" [4]="10.5.85.14"
[5]="10.5.85.15" [6]="10.5.85.16" [7]="10.5.85.17"
)
[[ -z "${NODE_GW[$NODE]}" ]] && echo "Error: Invalid node $NODE (1-7)" && exit 1
GATEWAY="${NODE_GW[$NODE]}"
PVE_HOST="${NODE_IP[$NODE]}"
fi
[[ -z "$IP" ]] && echo "Error: --ip is required" && usage
[[ -z "$GATEWAY" ]] && echo "Error: --gateway or --node is required" && usage
# --- Dependencies ---
for cmd in xorriso cpio gzip genisoimage; do
if ! command -v $cmd &>/dev/null; then
echo "Installing missing dependency: $cmd"
sudo apt-get install -y xorriso cpio gzip genisoimage 2>/dev/null || true
fi
done
# --- Password ---
if [[ -z "$PASSWORD" ]]; then
read -sp "Password for user $USER: " PASSWORD
echo
fi
PASSWORD_HASH=$(echo "$PASSWORD" | openssl passwd -6 -stdin)
# --- SSH Key ---
if [[ -f "$SSH_KEY_FILE" ]]; then
SSH_KEY=$(cat "$SSH_KEY_FILE")
else
echo "Warning: SSH key file not found: $SSH_KEY_FILE"
SSH_KEY=""
fi
# --- Download Debian ISO ---
ISO_URL="https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/debian-${DEBIAN_VERSION}.*-${ARCH}-netinst.iso"
ISO_FILE="$SCRIPT_DIR/debian-${DEBIAN_VERSION}-${ARCH}-netinst.iso"
if [[ ! -f "$ISO_FILE" ]]; then
echo "Downloading Debian ${DEBIAN_VERSION} netinstall ISO..."
# Get exact filename from listing
EXACT_URL=$(curl -sL "https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/" | \
grep -oP "debian-${DEBIAN_VERSION}\.[0-9]+-${ARCH}-netinst\.iso" | head -1)
wget -q --show-progress -O "$ISO_FILE" \
"https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/${EXACT_URL}"
fi
echo "Building ISO for: ${HOSTNAME} (${IP})"
# --- Extract ISO ---
cleanup() { rm -rf "$WORK_DIR"; }
trap cleanup EXIT
mkdir -p "$WORK_DIR"/{iso,custom}
xorriso -osirrox on -indev "$ISO_FILE" -extract / "$WORK_DIR/iso" 2>/dev/null
chmod -R u+w "$WORK_DIR/iso"
# --- Generate Preseed ---
PRESEED="$WORK_DIR/iso/preseed.cfg"
sed \
-e "s|{{IP}}|${IP}|g" \
-e "s|{{NETMASK}}|${NETMASK}|g" \
-e "s|{{GATEWAY}}|${GATEWAY}|g" \
-e "s|{{DNS}}|${DNS}|g" \
-e "s|{{HOSTNAME}}|${HOSTNAME}|g" \
-e "s|{{USER}}|${USER}|g" \
-e "s|{{PASSWORD_HASH}}|${PASSWORD_HASH}|g" \
-e "s|{{SSH_KEY}}|${SSH_KEY}|g" \
"$SCRIPT_DIR/preseed.cfg.tpl" > "$PRESEED"
# --- Patch GRUB (UEFI) - komplett ersetzen ---
GRUB_CFG="$WORK_DIR/iso/boot/grub/grub.cfg"
if [[ -f "$GRUB_CFG" ]]; then
cat > "$GRUB_CFG" << GRUBEOF
set default=0
set timeout=0
menuentry "Debian Auto Install - ${HOSTNAME}" {
linux /install.amd/vmlinuz auto=true priority=critical preseed/file=/cdrom/preseed.cfg ---
initrd /install.amd/initrd.gz
}
GRUBEOF
fi
# --- Patch isolinux (BIOS) - komplett ersetzen ---
TXT_CFG="$WORK_DIR/iso/isolinux/txt.cfg"
if [[ -f "$TXT_CFG" ]]; then
cat > "$TXT_CFG" << TXTEOF
default auto
label auto
menu label Auto Install - ${HOSTNAME}
kernel /install.amd/vmlinuz
append auto=true priority=critical preseed/file=/cdrom/preseed.cfg initrd=/install.amd/initrd.gz ---
TXTEOF
fi
ISOLINUX_CFG="$WORK_DIR/iso/isolinux/isolinux.cfg"
if [[ -f "$ISOLINUX_CFG" ]]; then
sed -i 's/timeout .*/timeout 1/' "$ISOLINUX_CFG"
fi
# --- Rebuild ISO ---
mkdir -p "$OUTPUT_DIR"
OUTPUT_ISO="$OUTPUT_DIR/debian-${DEBIAN_VERSION}-${HOSTNAME}-${IP}.iso"
cd "$WORK_DIR/iso"
# Fix MD5
find . -type f ! -name 'md5sum.txt' -exec md5sum {} \; > md5sum.txt 2>/dev/null || true
xorriso -as mkisofs \
-r -J \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-eltorito-alt-boot \
-e boot/grub/efi.img \
-no-emul-boot \
-isohybrid-gpt-basdat \
-o "$OUTPUT_ISO" \
. 2>/dev/null
echo ""
echo "============================================"
echo "ISO created: $OUTPUT_ISO"
echo "============================================"
echo "Host: $HOSTNAME"
echo "IP: $IP"
echo "Gateway: $GATEWAY"
echo "User: $USER"
echo "SSH Key: $(echo $SSH_KEY | cut -c1-40)..."
echo "============================================"
echo ""
if [[ -n "$PVE_HOST" ]]; then
echo "Uploading to node${NODE} (${PVE_HOST})..."
scp -o StrictHostKeyChecking=no "$OUTPUT_ISO" "root@${PVE_HOST}:/var/lib/vz/template/iso/" && \
echo "✅ ISO uploaded to node${NODE}" || \
{ echo "❌ Upload failed"; exit 1; }
if [[ "$CREATE_VM" == true ]]; then
ISO_NAME="$(basename "$OUTPUT_ISO")"
STORAGE="powerstore-node${NODE}"
PVE_NODE="node${NODE}"
# Auto-assign next free VMID if not specified
if [[ -z "$VMID" ]]; then
VMID=$(ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" "pvesh get /cluster/nextid" 2>/dev/null)
echo "Auto-assigned VMID: ${VMID}"
fi
echo ""
echo "Creating VM ${VMID} on ${PVE_NODE}..."
ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" "
# Create VM: q35, UEFI, virtio-scsi
pvesh create /nodes/${PVE_NODE}/qemu \
--vmid ${VMID} \
--name ${HOSTNAME} \
--machine q35 \
--bios ovmf \
--efidisk0 ${STORAGE}:1,efitype=4m,pre-enrolled-keys=0 \
--scsihw virtio-scsi-pci \
--scsi0 ${STORAGE}:${VM_DISK},cache=writeback \
--ide2 local:iso/${ISO_NAME},media=cdrom \
--net0 virtio,bridge=vmbr0 \
--cores ${VM_CORES} \
--memory ${VM_MEMORY} \
--cpu cputype=host \
--agent enabled=1 \
--boot order='scsi0;ide2' \
--ostype l26 \
--onboot 1 \
--numa 1 \
--balloon 0 \
--serial0 socket
" && echo "✅ VM ${VMID} created" || { echo "❌ VM creation failed"; exit 1; }
echo "Starting VM ${VMID}..."
ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" \
"pvesh create /nodes/${PVE_NODE}/qemu/${VMID}/status/start" && \
echo "✅ VM ${VMID} started - installing Debian..." || echo "❌ VM start failed"
echo ""
echo "============================================"
echo "Boot order: scsi0 → ide2 (disk first, ISO fallback)"
echo "1st boot: disk empty → boots ISO → installs Debian"
echo "2nd boot: disk has GRUB → boots from disk → done!"
echo ""
echo "After installation (~5 min):"
echo " ssh ${USER}@${IP}"
echo " ansible-playbook site.yml -l ${HOSTNAME}"
echo "============================================"
fi
else
echo "Upload to Proxmox:"
echo " scp $OUTPUT_ISO root@<proxmox-node>:/var/lib/vz/template/iso/"
fi

View file

@ -0,0 +1,74 @@
# Debian Preseed - Unattended Install
### Locale & Keyboard
d-i debian-installer/locale string de_DE.UTF-8
d-i keyboard-configuration/xkb-keymap select de
d-i console-setup/ask_detect boolean false
### Netzwerk (statisch)
d-i netcfg/choose_interface select auto
d-i netcfg/disable_autoconfig boolean true
d-i netcfg/get_ipaddress string {{IP}}
d-i netcfg/get_netmask string {{NETMASK}}
d-i netcfg/get_gateway string {{GATEWAY}}
d-i netcfg/get_nameservers string {{DNS}}
d-i netcfg/confirm_static boolean true
d-i netcfg/get_hostname string {{HOSTNAME}}
d-i netcfg/get_domain string local
### Clock
d-i clock-setup/utc boolean true
d-i time/zone string Europe/Berlin
d-i clock-setup/ntp boolean true
### User + Root
d-i passwd/root-login boolean true
d-i passwd/root-password-crypted string {{PASSWORD_HASH}}
d-i passwd/user-fullname string {{USER}}
d-i passwd/username string {{USER}}
d-i passwd/user-password-crypted string {{PASSWORD_HASH}}
### Partitioning (auto LVM)
d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
### Mirror
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
### Packages
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/include string sudo qemu-guest-agent curl wget ca-certificates gnupg openssh-server
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false
### Grub
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default
### Late commands - SSH Key, Sudo, Locale
d-i preseed/late_command string \
in-target mkdir -p /home/{{USER}}/.ssh; \
echo '{{SSH_KEY}}' > /target/home/{{USER}}/.ssh/authorized_keys; \
in-target chmod 700 /home/{{USER}}/.ssh; \
in-target chmod 600 /home/{{USER}}/.ssh/authorized_keys; \
in-target chown -R {{USER}}:{{USER}} /home/{{USER}}/.ssh; \
echo '{{USER}} ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/{{USER}}; \
in-target chmod 440 /etc/sudoers.d/{{USER}}; \
in-target systemctl enable ssh; \
in-target systemctl enable qemu-guest-agent; \
in-target sed -i 's/# de_DE.UTF-8/de_DE.UTF-8/' /etc/locale.gen; \
in-target locale-gen;
### Finish
d-i finish-install/reboot_in_progress note
d-i cdrom-detect/eject boolean true

264
iso-builder/readme.md Normal file
View file

@ -0,0 +1,264 @@
# Debian 13 Fully Unattended ISO EXTREM DETAILLIERTE ANLEITUNG
Ziel: Eine **komplett automatisierte Debian 13 ISO** bauen, die:
- ohne Benutzerinteraktion installiert (unattended)
- automatisch startet (kein GRUB-Menü)
- Sprache, Tastatur, Zeitzone setzt
- statische Netzwerkkonfiguration verwendet (IP / Gateway / DNS)
- Root + User inkl. Passwort erstellt
- automatisch partitioniert (LVM)
- GRUB installiert
- automatisch rebootet
Diese Anleitung ist bewusst **sehr detailliert und deterministisch**, damit auch eine einfache KI sie korrekt ausführen kann.
---
# 0. WICHTIGE GRUNDLOGIK
Die Automatisierung basiert auf:
1. **preseed.cfg** → enthält alle Antworten für den Debian Installer
2. **Bootparameter** → sorgen dafür, dass preseed geladen wird
3. **GRUB + ISOLINUX Anpassung** → damit KEIN Menü erscheint
---
# 1. SYSTEM VORBEREITEN
## 1.1 Pakete installieren
```bash
apt update
apt install -y xorriso syslinux isolinux grub-pc-bin grub-efi-amd64-bin mtools dosfstools
mkdir -p ~/debian-auto-iso/{src,iso,build}
cd ~/debian-auto-iso
~/debian-auto-iso/
├── src/ (Original ISO)
├── iso/ (entpackte ISO wird bearbeitet)
└── build/ (fertige ISO)
cd ~/debian-auto-iso/src
wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso
cd ~/debian-auto-iso
xorriso -osirrox on \
-indev src/debian-13.0.0-amd64-netinst.iso \
-extract / iso
chmod -R u+w iso
nano iso/preseed.cfg
### --------------------------------
### LOKALISIERUNG
### --------------------------------
d-i debian-installer/locale string de_DE.UTF-8
d-i keyboard-configuration/xkb-keymap select de
d-i console-setup/ask_detect boolean false
### --------------------------------
### NETZWERK (STATISCH!)
### --------------------------------
d-i netcfg/choose_interface select auto
d-i netcfg/disable_autoconfig boolean true
d-i netcfg/get_ipaddress string 192.168.100.10
d-i netcfg/get_netmask string 255.255.255.0
d-i netcfg/get_gateway string 192.168.100.1
d-i netcfg/get_nameservers string 192.168.100.1
d-i netcfg/confirm_static boolean true
d-i netcfg/get_hostname string debian-vm
d-i netcfg/get_domain string local
### --------------------------------
### ZEIT
### --------------------------------
d-i clock-setup/utc boolean true
d-i time/zone string Europe/Berlin
d-i clock-setup/ntp boolean true
### --------------------------------
### USER + PASSWÖRTER
### --------------------------------
d-i passwd/root-login boolean true
d-i passwd/root-password password rootpass
d-i passwd/root-password-again password rootpass
d-i passwd/user-fullname string Default User
d-i passwd/username string user
d-i passwd/user-password password userpass
d-i passwd/user-password-again password userpass
### --------------------------------
### PARTITIONIERUNG (AUTO LVM!)
### --------------------------------
d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/choose_partition select finish
### --------------------------------
### MIRROR
### --------------------------------
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
### --------------------------------
### PAKETE
### --------------------------------
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/include string vim curl htop sudo
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false
### --------------------------------
### GRUB INSTALLATION
### --------------------------------
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default
### --------------------------------
### INSTALLATION ABSCHLIESSEN
### --------------------------------
d-i finish-install/reboot_in_progress note
d-i cdrom-detect/eject boolean true
### --------------------------------
### LATE COMMAND (OPTIONAL)
### --------------------------------
d-i preseed/late_command string \
in-target systemctl enable ssh; \
in-target usermod -aG sudo user
nano iso/boot/grub/grub.cfg
set default=0
set timeout=0
menuentry "Debian Auto Install" {
linux /install.amd/vmlinuz auto=true priority=critical preseed/file=/cdrom/preseed.cfg ---
initrd /install.amd/initrd.gz
}
nano iso/isolinux/txt.cfg
default auto
timeout 0
label auto
menu label Auto Install Debian
kernel /install.amd/vmlinuz
append auto=true priority=critical preseed/file=/cdrom/preseed.cfg initrd=/install.amd/initrd.gz ---
cd ~/debian-auto-iso
xorriso -as mkisofs \
-r \
-V "DEBIAN_AUTO" \
-o build/debian-13-auto.iso \
-J -joliet-long -l \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-eltorito-alt-boot \
-e boot/grub/efi.img \
-no-emul-boot \
-isohybrid-gpt-basdat \
iso
10. ERWARTETES VERHALTEN
Nach Boot:
KEIN Menü erscheint
Installer startet automatisch
Keine Eingaben notwendig
Installation läuft komplett durch
System rebootet automatisch
Login möglich mit:
user / userpass
root / rootpass
11. HÄUFIGE FEHLER + DEBUG
Installer fragt trotzdem Dinge ab
→ Ursache:
falscher Parameter: preseed/file= fehlt
falscher Pfad: /cdrom/preseed.cfg
Netzwerk funktioniert nicht
→ Ursache:
falsches Interface
falsche IP Range
Gateway nicht erreichbar
ISO bootet nicht (UEFI)
→ Ursache:
efi.img fehlt
xorriso Flags falsch
GRUB Menü erscheint trotzdem
→ Ursache:
timeout != 0
falsche cfg bearbeitet
METHODE 2 (BESTE LÖSUNG): ISO NACH INSTALLATION "EJECTEN"
Idee:
Installer wirft CD automatisch aus → VM bootet von HDD
PRESEED ERWEITERN
In preseed.cfg hinzufügen:
d-i cdrom-detect/eject boolean true
ZUSÄTZLICH (WICHTIG!):
d-i finish-install/keep-consoles boolean true
d-i finish-install/reboot_in_progress note
👉 Ergebnis:
ISO wird logisch "ausgeworfen"
viele Hypervisor erkennen das
Boot fällt automatisch auf HDD zurück
👉 Achtung:
Funktioniert zuverlässig bei:
Proxmox ✅
KVM/QEMU ✅
VMware ⚠️ (teilweise)
VirtualBox ⚠️ (oft nicht)
✅ METHODE 3 (PROFI / 100% LÖSUNG): VM-SEITIG BOOT FIXEN
BESTE UND SAUBERSTE LÖSUNG
Nicht ISO lösen sondern VM korrekt konfigurieren.
🔧 PROXMOX
qm set <VMID> --boot order=scsi0
qm set <VMID> --cdrom none
Oder direkt beim Erstellen:
qm create 100 --boot order=scsi0

8
nvidia-docker.yml Normal file
View file

@ -0,0 +1,8 @@
---
- name: NVIDIA Treiber + Docker GPU Setup
hosts: all
become: yes
environment:
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
roles:
- nvidia

View file

@ -1,88 +0,0 @@
---
- name: NVIDIA + Docker Setup fuer Debian Trixie
hosts: all
become: true
vars:
docker_daemon_config:
default-runtime: nvidia
runtimes:
nvidia:
path: nvidia-container-runtime
runtimeArgs: []
tasks:
# --- Repos ----------------------------------------------------------------
- name: NVIDIA Container Toolkit GPG Key hinzufuegen
ansible.builtin.shell: |
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
args:
creates: /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
- name: NVIDIA Container Toolkit Repo hinzufuegen
ansible.builtin.shell: |
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
args:
creates: /etc/apt/sources.list.d/nvidia-container-toolkit.list
- name: apt update
ansible.builtin.apt:
update_cache: true
# --- Pakete ---------------------------------------------------------------
- name: Kernel Headers und DKMS installieren
ansible.builtin.apt:
name:
- linux-headers-{{ ansible_kernel }}
- dkms
state: present
- name: NVIDIA Treiber installieren
ansible.builtin.apt:
name:
- "nvidia-driver"
state: present
- name: NVIDIA Container Toolkit installieren
ansible.builtin.apt:
name:
- nvidia-container-toolkit
- nvidia-docker2
state: present
- name: NFS und CIFS Pakete installieren
ansible.builtin.apt:
name:
- nfs-common
- cifs-utils
state: present
# --- Docker konfigurieren -------------------------------------------------
- name: /etc/docker Verzeichnis sicherstellen
ansible.builtin.file:
path: /etc/docker
state: directory
mode: "0755"
- name: Docker daemon.json konfigurieren (NVIDIA als default runtime)
ansible.builtin.copy:
content: "{{ docker_daemon_config | to_nice_json }}"
dest: /etc/docker/daemon.json
mode: "0644"
notify: Docker neustarten
- name: nvidia-ctk runtime fuer Docker konfigurieren
ansible.builtin.command: nvidia-ctk runtime configure --runtime=docker
changed_when: false
handlers:
- name: Docker neustarten
ansible.builtin.service:
name: docker
state: restarted

View file

@ -1,45 +1,87 @@
[proxmox] [proxmox]
node1 ansible_host=10.10.1.1 node1 ansible_host=10.5.85.11
node2 ansible_host=10.5.85.200 node2 ansible_host=10.5.85.12
node4 ansible_host=10.5.85.100 node3 ansible_host=10.5.85.13
node5 ansible_host=10.5.85.101 node4 ansible_host=10.5.85.14
node5 ansible_host=10.5.85.15
node6 ansible_host=10.5.85.16
node7 ansible_host=10.5.85.17
[proxmox:vars] [proxmox:vars]
ansible_user=root ansible_user=root
[media] [media]
emby_sascha ansible_host=10.5.1.103 ansible_user=sascha emby-sascha ansible_host=10.6.1.103
emby_chris ansible_host=10.5.1.106 ansible_user=chris jellyfin ansible_host=10.5.1.112
jellyfin ansible_host=10.5.1.112 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 immich ansible_host=10.4.1.107
immich ansible_host=10.4.1.107 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 emby-chris ansible_host=10.7.1.106
emby_chris_new ansible_host=10.5.1.117 ansible_user=chris ansible_become_method=su ansible_become_password=Pimmelparty123 ansible_password=Pimmelparty123
[arr] [arr]
tdarr ansible_host=10.2.1.104 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 tdarr ansible_host=10.2.1.104
arrapps ansible_host=10.2.1.100 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 arrapps ansible_host=10.2.1.100
sabnzbd ansible_host=10.2.1.119 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 sabnzbd ansible_host=10.2.1.119
arr-chris ansible_host=10.7.1.100
arr-chris-live ansible_host=10.7.1.101
[docker] [docker]
dockhand ansible_host=10.4.1.116 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 ansible_password=GT500r8 dockhand ansible_host=10.4.1.116
[auto] [auto]
n8n ansible_host=10.4.1.113 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 ansible_password=GT500r8 n8n ansible_host=10.4.1.113
ansible ansible_host=localhost ansible_user=root openclaw ansible_host=10.4.1.100
monitoring ansible_host=10.1.1.111
automation ansible_host=10.1.1.115
automation1 ansible_host=10.5.85.5
[communication] [communication]
matrix ansible_host=10.4.1.110 ansible_user=sascha ansible_become_method=su ansible_become_password=GT500r8 ansible_password=GT500r8 matrix ansible_host=10.4.1.110
pihole ansible_host=10.1.1.10
[hetzner] [hetzner]
emby ansible_host=emby ansible_user=root pfannkuchen ansible_host=159.69.245.190 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_ed25519
proxy ansible_host=proxy ansible_user=root
[nvidia]
tdarr
emby-sascha
emby-chris
immich
[proxmox_gpu]
node2
node4
node6
node7
[frp]
emby-sascha
emby-chris
jellyfin
[wireguard]
node1
node2
node3
node4
node5
node6
node7
[all:children] [all:children]
media media
arr arr
media
docker docker
auto auto
communication
hetzner hetzner
#proxmox proxmox
[backup:children]
media
arr
docker
auto
communication
proxmox
hetzner
# Credentials liegen in group_vars/ (nicht im INI, da kein Jinja2-Support)

148
pfannkuchen.sh Executable file
View file

@ -0,0 +1,148 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
# Farben
R='\033[0;31m' G='\033[0;32m' Y='\033[1;33m' C='\033[0;36m' B='\033[1m' N='\033[0m'
usage() {
echo -e "${B}🥞 Pfannkuchen Ansible Wrapper${N}\n"
echo -e "${B}Usage:${N} $0 <command> [host/gruppe]\n"
echo -e "${B}Commands:${N}"
echo -e " ${C}setup${N} <host> Neue VM einrichten (base + docker + borg + hawser + sysctl)"
echo -e " ${C}base${N} <host> Nur Basis + Docker"
echo -e " ${C}gpu${N} <host> NVIDIA Treiber + Docker GPU Runtime"
echo -e " ${C}backup${N} [host] Borg Backup einrichten (default: alle backup-Hosts)"
echo -e " ${C}hawser${N} <host> Hawser installieren"
echo -e " ${C}pve${N} [host] Proxmox Post-Install (Repos, Nag, HA)"
echo -e " ${C}passthrough${N} [host] GPU PCI Passthrough vorbereiten"
echo -e " ${C}telegraf${N} [host] Telegraf Monitoring deployen"
echo -e " ${C}wstunnel${N} [host] wstunnel + WireGuard deployen"
echo -e " ${C}tune${N} <host> Sysctl Netzwerk-Tuning"
echo -e " ${C}pvetune${N} [host] Proxmox Host Tuning (sysctl, resolv, hosts)"
echo -e " ${C}update${N} [host] Dist-Upgrade (default: alle Hosts)"
echo -e " ${C}list${N} Inventory anzeigen"
echo -e " ${C}ping${N} [host] Erreichbarkeit testen"
echo -e " ${C}reboot${N} <host> Reboot durchfuehren"
echo -e " ${C}shell${N} <host> <cmd> Ad-hoc Shell-Befehl ausfuehren"
echo -e ""
echo -e "${B}Beispiele:${N}"
echo -e " $0 setup emby_sascha"
echo -e " $0 backup proxmox"
echo -e " $0 update"
echo -e " $0 gpu tdarr"
echo -e " $0 reboot nvidia"
echo -e " $0 shell media 'uptime'"
exit 1
}
run() {
local playbook="$1"; shift
echo -e "${G}${N} ansible-playbook ${playbook} $*"
ansible-playbook "$playbook" "$@"
}
[ $# -lt 1 ] && usage
CMD="$1"
HOST="${2:-}"
case "$CMD" in
setup)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host angeben${N}" && exit 1
run site.yml -l "$HOST"
;;
base)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host angeben${N}" && exit 1
run base-debian.yml -l "$HOST"
;;
gpu)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host angeben${N}" && exit 1
run nvidia-docker.yml -l "$HOST"
;;
backup)
if [ -n "$HOST" ]; then
run borg-backup.yml -l "$HOST"
else
run borg-backup.yml
fi
;;
hawser)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host angeben${N}" && exit 1
run hawser.yml -l "$HOST"
;;
tune)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host angeben${N}" && exit 1
run sysctl.yaml -l "$HOST"
;;
pvetune)
if [ -n "$HOST" ]; then
run sysctl-proxmox.yaml -l "$HOST"
else
run sysctl-proxmox.yaml
fi
;;
pve)
if [ -n "$HOST" ]; then
run pve-postinstall.yml -l "$HOST"
else
run pve-postinstall.yml
fi
;;
passthrough)
if [ -n "$HOST" ]; then
run pve-gpu-passthrough.yml -l "$HOST"
else
run pve-gpu-passthrough.yml
fi
;;
telegraf)
if [ -n "$HOST" ]; then
run telegraf.yml -l "$HOST"
else
run telegraf.yml
fi
;;
wstunnel)
if [ -n "$HOST" ]; then
run wstunnel.yml -l "$HOST"
else
run wstunnel.yml
fi
;;
update)
if [ -n "$HOST" ]; then
run update.yml -l "$HOST"
else
run update.yml
fi
;;
list)
ansible-inventory --list --yaml 2>/dev/null || ansible-inventory --graph
;;
ping)
if [ -n "$HOST" ]; then
ansible "$HOST" -m ping
else
ansible all -m ping
fi
;;
reboot)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host oder Gruppe angeben${N}" && exit 1
echo -e "${Y}⚠ Reboot von: $HOST${N}"
ansible "$HOST" -m reboot -b -a "msg='Reboot via Pfannkuchen'"
;;
shell)
[ -z "$HOST" ] && echo -e "${R}Fehler: Host und Befehl angeben${N}" && exit 1
SHCMD="${*:3}"
[ -z "$SHCMD" ] && echo -e "${R}Fehler: Befehl angeben${N}" && exit 1
echo -e "${G}${N} ansible $HOST -m shell -a '$SHCMD'"
ansible "$HOST" -m shell -b -a "$SHCMD"
;;
*)
echo -e "${R}Unbekannter Befehl: $CMD${N}"
usage
;;
esac

6
pihole-dns.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: Pi-hole DNS Records deployen
hosts: pihole
become: yes
roles:
- pihole_dns

6
pve-gpu-passthrough.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: GPU PCI Passthrough vorbereiten
hosts: proxmox_gpu
become: yes
roles:
- pve_gpu_passthrough

6
pve-postinstall.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: Proxmox Post-Install Setup
hosts: proxmox
become: yes
roles:
- pve_postinstall

View file

@ -0,0 +1,2 @@
---
base_user: sascha

134
roles/base/tasks/main.yml Normal file
View file

@ -0,0 +1,134 @@
---
- name: Hostname setzen
hostname:
name: "{{ inventory_hostname }}"
- name: /etc/hosts aktualisieren
lineinfile:
path: /etc/hosts
regexp: '^127\.0\.1\.1'
line: "127.0.1.1 {{ inventory_hostname }}.local {{ inventory_hostname }}"
- name: SSH Public Key für Benutzer {{ base_user }} hinterlegen
ansible.posix.authorized_key:
user: "{{ base_user }}"
state: present
key: "{{ vault_ssh_pubkey }}"
- name: SSH Private Key für Benutzer {{ base_user }} deployen
copy:
content: "{{ vault_ssh_privkey }}\n"
dest: "/home/{{ base_user }}/.ssh/id_ed25519"
owner: "{{ base_user }}"
group: "{{ base_user }}"
mode: "0600"
- name: SSH Public Key Datei für Benutzer {{ base_user }} deployen
copy:
content: "{{ vault_ssh_pubkey }}\n"
dest: "/home/{{ base_user }}/.ssh/id_ed25519.pub"
owner: "{{ base_user }}"
group: "{{ base_user }}"
mode: "0644"
- name: SSH Key auch für root hinterlegen
ansible.posix.authorized_key:
user: root
state: present
key: "{{ vault_ssh_pubkey }}"
- name: SSH Private Key für root deployen
copy:
content: "{{ vault_ssh_privkey }}\n"
dest: /root/.ssh/id_ed25519
owner: root
group: root
mode: "0600"
- name: Standard Debian Trixie Repositories setzen
copy:
dest: /etc/apt/sources.list
content: |
deb http://ftp.gwdg.de/debian/ trixie main non-free-firmware non-free contrib
deb-src http://ftp.gwdg.de/debian/ trixie main non-free-firmware non-free contrib
deb http://security.debian.org/debian-security trixie-security main non-free-firmware non-free contrib
deb-src http://security.debian.org/debian-security trixie-security main non-free-firmware non-free contrib
deb http://ftp.gwdg.de/debian/ trixie-updates main non-free-firmware non-free contrib
deb-src http://ftp.gwdg.de/debian/ trixie-updates main non-free-firmware non-free contrib
owner: root
group: root
mode: '0644'
register: repo_status
- name: Apt Cache aktualisieren (falls Repos geändert wurden)
apt:
update_cache: yes
when: repo_status.changed
- name: Installiere benötigte Basis-Pakete
apt:
name:
- curl
- gnupg
- ca-certificates
- sudo
- wget
- vim
- mc
state: present
update_cache: yes
- name: Locales-Paket sicherstellen
apt:
name: locales
state: present
- name: en_US.UTF-8 Locale generieren
locale_gen:
name: en_US.UTF-8
state: present
- name: Systemweite Sprache auf en_US.UTF-8 setzen
debconf:
name: locales
question: locales/default_environment_locale
value: en_US.UTF-8
vtype: select
- name: Locale-Datei manuell schreiben (Sicherheitsnetz)
copy:
dest: /etc/default/locale
content: |
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
- name: Gruppe sudo passwortloses sudo erlauben
lineinfile:
path: /etc/sudoers
state: present
regexp: '^%sudo'
line: '%sudo ALL=(ALL:ALL) NOPASSWD: ALL'
validate: '/usr/sbin/visudo -cf %s'
- name: Benutzer {{ base_user }} zu sudo Gruppe hinzufügen
user:
name: "{{ base_user }}"
groups: sudo
append: yes
- name: Unnötige Pakete entfernen
apt:
autoremove: yes
- name: QEMU Guest Agent installieren
apt:
name: qemu-guest-agent
state: present
- name: QEMU Guest Agent aktivieren
service:
name: qemu-guest-agent
state: started
enabled: yes

View file

@ -0,0 +1,14 @@
---
backup_source: /app-config
borg_ssh_key: /root/.ssh/id_borg
borg_passphrase: "{{ vault_borg_passphrase }}"
borg_repo: "ssh://storagebox/home/{{ inventory_hostname }}"
borg_retention_daily: 7
borg_retention_weekly: 4
borg_retention_monthly: 6
borg_compression: lz4
borg_remote_path: borg-1.4
borg_logfile: /var/log/borg-backup.log
hetzner_storage_host: "{{ vault_hetzner_storage_host }}"
hetzner_storage_user: "{{ vault_hetzner_storage_user }}"
hetzner_storage_port: 23

94
roles/borg/tasks/main.yml Normal file
View file

@ -0,0 +1,94 @@
---
- name: Borg installieren
apt:
name: borgbackup
state: present
update_cache: yes
- name: SSH Private Key deployen
copy:
src: id_rsa
dest: "{{ borg_ssh_key }}"
mode: '0600'
- name: SSH Public Key deployen
copy:
src: id_rsa.pub
dest: "{{ borg_ssh_key }}.pub"
mode: '0644'
- name: SSH Config für Storage Box
blockinfile:
path: /root/.ssh/config
create: yes
mode: '0600'
marker: "# {mark} BORG STORAGEBOX"
block: |
Host storagebox
Hostname {{ hetzner_storage_host }}
User {{ hetzner_storage_user }}
Port {{ hetzner_storage_port }}
IdentityFile {{ borg_ssh_key }}
IdentitiesOnly yes
StrictHostKeyChecking accept-new
- name: Backup-Verzeichnis auf Storage Box anlegen
command: ssh storagebox mkdir -p home/{{ inventory_hostname }}
changed_when: false
- name: Borg Repo initialisieren
environment:
BORG_PASSPHRASE: "{{ borg_passphrase }}"
command: borg init --encryption=repokey {{ borg_repo }}
register: borg_init
failed_when: borg_init.rc != 0 and 'already exists' not in borg_init.stderr
changed_when: borg_init.rc == 0
- name: Passphrase-Datei deployen
copy:
dest: /root/.borg-passphrase
content: "{{ borg_passphrase }}"
mode: '0400'
- name: Backup-Script deployen
template:
src: borg-backup.sh.j2
dest: /usr/local/bin/borg-backup.sh
mode: '0700'
- name: Systemd Timer Unit
copy:
dest: /etc/systemd/system/borg-backup.timer
content: |
[Unit]
Description=Borg Backup Timer
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
- name: Systemd Service Unit
copy:
dest: /etc/systemd/system/borg-backup.service
content: |
[Unit]
Description=Borg Backup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
Nice=19
IOSchedulingClass=idle
- name: Timer aktivieren und starten
systemd:
name: borg-backup.timer
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,33 @@
#!/bin/bash
set -euo pipefail
export BORG_PASSPHRASE=$(cat /root/.borg-passphrase)
REPO="{{ borg_repo }}"
LOGFILE="{{ borg_logfile }}"
echo "[$(date)] Starte Backup: {{ inventory_hostname }}" >> "$LOGFILE"
borg create \
--verbose \
--filter AME \
--remote-path={{ borg_remote_path }} \
--stats \
--show-rc \
--compression {{ borg_compression }} \
"${REPO}::{{ inventory_hostname }}-$(date +%Y-%m-%d_%H-%M)" \
{{ backup_source }} >> "$LOGFILE" 2>&1
BACKUP_RC=$?
borg prune -v --list "${REPO}" \
--keep-daily={{ borg_retention_daily }} \
--keep-weekly={{ borg_retention_weekly }} \
--keep-monthly={{ borg_retention_monthly }} >> "$LOGFILE" 2>&1
PRUNE_RC=$?
borg compact "${REPO}" >> "$LOGFILE" 2>&1
GLOBAL_RC=$(( BACKUP_RC > PRUNE_RC ? BACKUP_RC : PRUNE_RC ))
echo "[$(date)] Backup beendet mit Code $GLOBAL_RC" >> "$LOGFILE"
exit $GLOBAL_RC

View file

@ -0,0 +1,39 @@
---
- name: Verzeichnis für Keyrings erstellen
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Docker GPG Key herunterladen
get_url:
url: https://download.docker.com/linux/debian/gpg
dest: /etc/apt/keyrings/docker.asc
mode: '0644'
- name: Docker Repository Datei erstellen
copy:
dest: /etc/apt/sources.list.d/docker.list
content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable"
mode: '0644'
register: docker_repo
- name: Paketliste aktualisieren
apt:
update_cache: yes
when: docker_repo.changed
- name: Docker Engine installieren
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
- name: Benutzer sascha zu docker Gruppe hinzufügen
user:
name: sascha
groups: docker
append: yes

View file

@ -0,0 +1,6 @@
---
frp_version: "0.68.0"
frp_server_addr: "tunnel.sascha-lutz.de"
frp_server_port: 8443
frp_token: "{{ vault_frp_token }}"
frp_tc_device: "ens18"

View file

@ -0,0 +1,9 @@
---
- name: reload systemd
systemd:
daemon_reload: true
- name: restart frpc
systemd:
name: frpc
state: restarted

View file

@ -0,0 +1,104 @@
---
- name: frpc Binary herunterladen
get_url:
url: "https://github.com/fatedier/frp/releases/download/v{{ frp_version }}/frp_{{ frp_version }}_linux_amd64.tar.gz"
dest: /tmp/frp.tar.gz
- name: frpc entpacken
unarchive:
src: /tmp/frp.tar.gz
dest: /tmp/
remote_src: yes
- name: frpc Binary installieren
copy:
src: "/tmp/frp_{{ frp_version }}_linux_amd64/frpc"
dest: /usr/local/bin/frpc
mode: "0755"
remote_src: yes
- name: frpc Config Verzeichnis
file:
path: /etc/frp
state: directory
mode: "0755"
- name: frpc Config deployen
copy:
dest: /etc/frp/frpc.toml
content: |
serverAddr = "{{ frp_server_addr }}"
serverPort = {{ frp_server_port }}
auth.method = "token"
auth.token = "{{ frp_token }}"
transport.protocol = "quic"
transport.poolCount = 5
transport.tcpMux = true
{% for proxy in frp_proxies %}
[[proxies]]
name = "{{ proxy.name }}"
type = "tcp"
localIP = "{{ proxy.local_ip | default('127.0.0.1') }}"
localPort = {{ proxy.local_port }}
remotePort = {{ proxy.remote_port }}
transport.useCompression = true
transport.bandwidthLimit = "{{ proxy.bandwidth_limit | default('6MB') }}"
transport.bandwidthLimitMode = "server"
{% endfor %}
mode: "0600"
notify: restart frpc
- name: frpc systemd Service
copy:
dest: /etc/systemd/system/frpc.service
content: |
[Unit]
Description=frp Client Reverse Tunnel
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
[Install]
WantedBy=multi-user.target
mode: "0644"
notify:
- reload systemd
- restart frpc
- name: frpc aktivieren und starten
systemd:
name: frpc
enabled: true
state: started
daemon_reload: true
- name: iproute2 installieren (fuer tc)
apt:
name: iproute2
state: present
update_cache: yes
- name: tc Fair Queueing mit Per-Flow-Limit
shell: /sbin/tc qdisc replace dev {{ frp_tc_device | default('ens18') }} root fq maxrate {{ frp_tc_maxrate | default('50mbit') }}
changed_when: false
- name: tc Limit persistent via post-up
lineinfile:
path: /etc/network/interfaces
insertafter: "iface {{ frp_tc_device | default('ens18') }}"
line: " post-up /sbin/tc qdisc replace dev {{ frp_tc_device | default('ens18') }} root fq maxrate {{ frp_tc_maxrate | default('50mbit') }}"
regexp: "post-up.*tc qdisc"
- name: Temp-Dateien aufräumen
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/frp.tar.gz
- "/tmp/frp_{{ frp_version }}_linux_amd64"

View file

@ -0,0 +1,5 @@
---
frp_version: "0.68.0"
frp_bind_port: 7000
frp_quic_port: 8443
frp_token: "{{ vault_frp_token }}"

View file

@ -0,0 +1,9 @@
---
- name: reload systemd
systemd:
daemon_reload: true
- name: restart frps
systemd:
name: frps
state: restarted

View file

@ -0,0 +1,74 @@
---
- name: frps Binary herunterladen
get_url:
url: "https://github.com/fatedier/frp/releases/download/v{{ frp_version }}/frp_{{ frp_version }}_linux_amd64.tar.gz"
dest: /tmp/frp.tar.gz
- name: frps entpacken
unarchive:
src: /tmp/frp.tar.gz
dest: /tmp/
remote_src: yes
- name: frps Binary installieren
copy:
src: "/tmp/frp_{{ frp_version }}_linux_amd64/frps"
dest: /usr/local/bin/frps
mode: "0755"
remote_src: yes
- name: frps Config Verzeichnis
file:
path: /etc/frp
state: directory
mode: "0755"
- name: frps Config deployen
copy:
dest: /etc/frp/frps.toml
content: |
bindPort = {{ frp_bind_port }}
quicBindPort = {{ frp_quic_port }}
auth.method = "token"
auth.token = "{{ frp_token }}"
transport.tcpMux = true
transport.maxPoolCount = 10
mode: "0600"
notify: restart frps
- name: frps systemd Service
copy:
dest: /etc/systemd/system/frps.service
content: |
[Unit]
Description=frp Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
[Install]
WantedBy=multi-user.target
mode: "0644"
notify:
- reload systemd
- restart frps
- name: frps aktivieren und starten
systemd:
name: frps
enabled: true
state: started
daemon_reload: true
- name: Temp-Dateien aufräumen
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/frp.tar.gz
- "/tmp/frp_{{ frp_version }}_linux_amd64"

View file

@ -0,0 +1,2 @@
---
glances_port: 61208

View file

@ -0,0 +1,9 @@
---
- name: reload systemd
systemd:
daemon_reload: true
- name: restart glances
systemd:
name: glances
state: restarted

View file

@ -0,0 +1,45 @@
---
- name: Glances apt-Paket entfernen (fehlende Web-UI)
apt:
name: glances
state: absent
- name: pip installieren
apt:
name: python3-pip
state: present
update_cache: yes
- name: Glances mit Web-UI per pip installieren
pip:
name: "glances[web]"
state: present
extra_args: --break-system-packages
- name: Glances systemd Service (Web-Modus)
copy:
dest: /etc/systemd/system/glances.service
content: |
[Unit]
Description=Glances Web Server
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/glances -w -B 0.0.0.0 -p {{ glances_port }} --disable-plugin cloud
[Install]
WantedBy=multi-user.target
mode: "0644"
notify:
- reload systemd
- restart glances
- name: Glances aktivieren und starten
systemd:
name: glances
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,5 @@
---
- name: restart hawser
ansible.builtin.systemd:
name: hawser
state: restarted

View file

@ -0,0 +1,19 @@
---
- name: Hawser installieren (offizielles Install-Script)
ansible.builtin.shell:
cmd: curl -fsSL https://raw.githubusercontent.com/Finsys/hawser/main/scripts/install.sh | bash
creates: /usr/local/bin/hawser
- name: Hawser Token in Config setzen
ansible.builtin.lineinfile:
path: /etc/hawser/config
regexp: '^TOKEN='
line: "TOKEN={{ vault_hawser_token }}"
notify: restart hawser
- name: Hawser aktivieren und starten
ansible.builtin.systemd:
name: hawser
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,8 @@
---
cuda_keyring_deb_url: "https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/cuda-keyring_1.1-1_all.deb"
docker_daemon_config:
default-runtime: nvidia
runtimes:
nvidia:
path: nvidia-container-runtime
runtimeArgs: []

View file

@ -0,0 +1,5 @@
---
- name: Docker neustarten
ansible.builtin.service:
name: docker
state: restarted

View file

@ -0,0 +1,77 @@
---
- name: Nouveau Treiber blacklisten
ansible.builtin.copy:
dest: /etc/modprobe.d/blacklist-nouveau.conf
content: |
blacklist nouveau
options nouveau modeset=0
mode: "0644"
register: nouveau_blacklist
- name: initramfs-tools installieren
apt:
name: initramfs-tools
state: present
- name: initramfs aktualisieren
ansible.builtin.command: update-initramfs -u
when: nouveau_blacklist.changed
- name: CUDA Keyring herunterladen und installieren
ansible.builtin.apt:
deb: "{{ cuda_keyring_deb_url }}"
- name: NVIDIA Container Toolkit GPG Key hinzufuegen
ansible.builtin.shell: |
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
args:
creates: /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
- name: NVIDIA Container Toolkit Repo hinzufuegen
ansible.builtin.shell: |
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
args:
creates: /etc/apt/sources.list.d/nvidia-container-toolkit.list
- name: apt update
ansible.builtin.apt:
update_cache: true
- name: Kernel Headers und DKMS installieren
ansible.builtin.apt:
name:
- linux-headers-{{ ansible_kernel }}
- dkms
state: present
- name: NVIDIA Treiber installieren (CUDA Repo)
ansible.builtin.apt:
name: cuda-drivers
state: present
register: nvidia_driver
- name: NVIDIA Container Toolkit installieren
ansible.builtin.apt:
name:
- nvidia-container-toolkit
state: present
- name: Docker daemon.json konfigurieren (NVIDIA als default runtime)
ansible.builtin.copy:
content: "{{ docker_daemon_config | to_nice_json }}"
dest: /etc/docker/daemon.json
mode: "0644"
notify: Docker neustarten
- name: nvidia-ctk runtime fuer Docker konfigurieren
ansible.builtin.command: nvidia-ctk runtime configure --runtime=docker
changed_when: false
- name: Reboot nach Treiberinstallation
ansible.builtin.reboot:
msg: "Reboot nach NVIDIA Treiber-Installation"
reboot_timeout: 300
when: nvidia_driver.changed

View file

@ -0,0 +1,6 @@
---
pihole_dns_domain: local
pihole_custom_list: /etc/pihole/custom.list
pihole_extra_records: []
# - ip: 10.4.1.120
# name: authentik

View file

@ -0,0 +1,4 @@
---
- name: Reload Pi-hole DNS
ansible.builtin.command: pihole restartdns reload
listen: reload pihole dns

View file

@ -0,0 +1,9 @@
---
- name: Deploy Pi-hole custom DNS records
ansible.builtin.template:
src: custom.list.j2
dest: "{{ pihole_custom_list }}"
owner: root
group: root
mode: "0644"
notify: reload pihole dns

View file

@ -0,0 +1,11 @@
# Von Ansible generiert nicht manuell bearbeiten
{% for host in groups['all'] %}
{% if hostvars[host].ansible_host is defined %}
{{ hostvars[host].ansible_host }} {{ host }}.{{ pihole_dns_domain }}
{{ hostvars[host].ansible_host }} {{ host }}
{% endif %}
{% endfor %}
{% for entry in pihole_extra_records %}
{{ entry.ip }} {{ entry.name }}.{{ pihole_dns_domain }}
{{ entry.ip }} {{ entry.name }}
{% endfor %}

View file

@ -0,0 +1,3 @@
---
- name: Update GRUB
command: update-grub

View file

@ -0,0 +1,45 @@
---
- name: IOMMU Kernel-Parameter setzen (GRUB)
lineinfile:
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT='
line: 'GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"'
register: grub_updated
notify: Update GRUB
- name: VFIO Module in /etc/modules eintragen
copy:
dest: /etc/modules
content: |
# /etc/modules - VFIO fuer GPU Passthrough
vfio
vfio_iommu_type1
vfio_pci
mode: "0644"
register: modules_updated
- name: Alte fehlerhafte modprobe Configs aufraeumen
file:
path: "{{ item }}"
state: absent
loop:
- /etc/modprobe.d/block-nouveau.conf
- /etc/modprobe.d/nvidia-installer-disable-nouveau.conf
- name: NVIDIA und Nouveau auf Host blacklisten
copy:
dest: /etc/modprobe.d/gpu-passthrough.conf
content: |
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist rivafb
options nouveau modeset=0
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
mode: "0644"
register: modprobe_updated
- name: initramfs aktualisieren
command: update-initramfs -u -k all
when: grub_updated.changed or modules_updated.changed or modprobe_updated.changed

View file

@ -0,0 +1,122 @@
---
- name: Legacy .list Dateien finden
find:
paths: /etc/apt/sources.list.d
patterns: "*.list"
register: legacy_lists
- name: Legacy .list Dateien umbenennen
command: mv "{{ item.path }}" "{{ item.path }}.bak"
loop: "{{ legacy_lists.files }}"
when: legacy_lists.files | length > 0
args:
creates: "{{ item.path }}.bak"
- name: Legacy sources.list leeren
copy:
dest: /etc/apt/sources.list
content: ""
mode: "0644"
- name: Debian Trixie Quellen (deb822)
copy:
dest: /etc/apt/sources.list.d/debian.sources
content: |
Types: deb
URIs: http://deb.debian.org/debian/
Suites: trixie trixie-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: trixie-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
mode: "0644"
- name: Enterprise Repo deaktivieren (auskommentiert)
copy:
dest: /etc/apt/sources.list.d/pve-enterprise.sources
content: |
# Types: deb
# URIs: https://enterprise.proxmox.com/debian/pve
# Suites: trixie
# Components: pve-enterprise
# Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
mode: "0644"
- name: Ceph Enterprise Repo deaktivieren (auskommentiert)
copy:
dest: /etc/apt/sources.list.d/ceph.sources
content: |
# Types: deb
# URIs: https://enterprise.proxmox.com/debian/ceph-squid
# Suites: trixie
# Components: enterprise
# Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
mode: "0644"
- name: PVE No-Subscription Repo (deb822)
copy:
dest: /etc/apt/sources.list.d/proxmox.sources
content: |
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
mode: "0644"
- name: PVE Test Repo (disabled)
copy:
dest: /etc/apt/sources.list.d/pve-test.sources
content: |
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-test
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
mode: "0644"
- name: Subscription Nag Patch Script deployen
copy:
dest: /usr/local/bin/pve-remove-nag.sh
mode: "0755"
content: |
#!/bin/sh
WEB_JS=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
if [ -s "$WEB_JS" ] && ! grep -q NoMoreNagging "$WEB_JS"; then
sed -i -e "/data\.status/ s/!//" -e "/data\.status/ s/active/NoMoreNagging/" "$WEB_JS"
fi
- name: Nag Patch als DPkg Post-Invoke registrieren
copy:
dest: /etc/apt/apt.conf.d/no-nag-script
content: 'DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh"; };'
mode: "0644"
- name: Nag Patch einmalig ausfuehren
command: /usr/local/bin/pve-remove-nag.sh
changed_when: false
- name: HA Services deaktivieren
systemd:
name: "{{ item }}"
enabled: false
state: stopped
loop:
- pve-ha-lrm
- pve-ha-crm
- corosync
failed_when: false
- name: Apt Cache aktualisieren
apt:
update_cache: yes
- name: Dist-Upgrade ausfuehren
apt:
upgrade: dist
autoremove: yes

View file

@ -0,0 +1,41 @@
---
- name: BBR Kernel Modul laden
ansible.builtin.modprobe:
name: tcp_bbr
state: present
- name: BBR Modul beim Boot laden
ansible.builtin.copy:
content: "tcp_bbr\n"
dest: /etc/modules-load.d/bbr.conf
mode: "0644"
- name: Sysctl Parameter setzen
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/99-net-tuning.conf
reload: true
state: present
loop:
- { key: net.core.rmem_default, value: "262144" }
- { key: net.core.wmem_default, value: "262144" }
- { key: net.core.rmem_max, value: "67108864" }
- { key: net.core.wmem_max, value: "67108864" }
- { key: net.ipv4.tcp_rmem, value: "4096 87380 67108864" }
- { key: net.ipv4.tcp_wmem, value: "4096 65536 67108864" }
- { key: net.ipv4.tcp_window_scaling, value: "1" }
- { key: net.ipv4.tcp_congestion_control, value: "bbr" }
- { key: net.ipv4.tcp_slow_start_after_idle, value: "0" }
- { key: net.ipv4.tcp_fastopen, value: "3" }
- { key: net.core.netdev_max_backlog, value: "16384" }
- { key: net.core.somaxconn, value: "4096" }
- { key: net.ipv4.tcp_notsent_lowat, value: "16384" }
- { key: net.ipv4.tcp_fin_timeout, value: "15" }
- { key: net.ipv4.tcp_tw_reuse, value: "1" }
- { key: vm.swappiness, value: "1" }
- { key: vm.dirty_ratio, value: "15" }
- { key: vm.dirty_background_ratio, value: "5" }
- { key: net.ipv4.tcp_mtu_probing, value: "1" }
- { key: net.ipv4.ip_forward, value: "1" }
- { key: net.ipv6.conf.all.forwarding, value: "1" }

View file

@ -0,0 +1,6 @@
---
- name: sysctl reload
ansible.builtin.command: sysctl --system
- name: resolvconf update
ansible.builtin.command: resolvconf -u

View file

@ -0,0 +1,55 @@
---
- name: resolvconf installieren
ansible.builtin.apt:
name: resolvconf
state: present
- name: DNS in resolvconf head setzen
ansible.builtin.copy:
dest: /etc/resolvconf/resolv.conf.d/head
content: |
# Managed by Ansible
nameserver 1.1.1.1
nameserver 1.0.0.1
mode: "0644"
notify: resolvconf update
- name: /etc/hosts deployen
ansible.builtin.template:
src: hosts.j2
dest: /etc/hosts
mode: "0644"
- name: sysctl.conf inkludiert sysctl.d
ansible.builtin.lineinfile:
path: /etc/sysctl.conf
create: true
mode: "0644"
line: "# Managed by Ansible Drop-ins in /etc/sysctl.d/ werden automatisch geladen"
insertbefore: BOF
notify: sysctl reload
- name: Sysctl Parameter setzen
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/99-proxmox-tuning.conf
reload: true
state: present
loop:
- { key: vm.overcommit_memory, value: "1" }
- { key: vm.swappiness, value: "1" }
- { key: fs.file-max, value: "9999999" }
- { key: fs.inotify.max_user_watches, value: "524288" }
- { key: fs.inotify.max_user_instances, value: "512" }
- { key: net.ipv4.ip_forward, value: "1" }
- { key: net.ipv6.conf.all.forwarding, value: "1" }
- { key: net.bridge.bridge-nf-call-iptables, value: "0" }
- { key: net.bridge.bridge-nf-call-ip6tables, value: "0" }
- { key: vm.dirty_expire_centisecs, value: "3000" }
- { key: vm.dirty_writeback_centisecs, value: "500" }
- { key: net.ipv4.tcp_mtu_probing, value: "1" }
- { key: net.core.rmem_max, value: "67108864" }
- { key: net.core.wmem_max, value: "67108864" }
- { key: net.ipv4.tcp_rmem, value: "4096 87380 67108864" }
- { key: net.ipv4.tcp_wmem, value: "4096 65536 67108864" }

View file

@ -0,0 +1,9 @@
# Managed by Ansible
127.0.0.1 localhost
127.0.1.1 {{ inventory_hostname }}
::1 localhost ip6-localhost ip6-loopback
# Proxmox Hosts
{% for host in groups['proxmox'] %}
{{ hostvars[host]['ansible_host'] }} {{ host }}
{% endfor %}

View file

@ -0,0 +1,11 @@
---
telegraf_influx_url: "https://influx.sascha-lutz.de"
telegraf_influx_org: "influx.sascha-lutz.de"
telegraf_influx_token: "{{ vault_telegraf_influx_token }}"
telegraf_influx_bucket: "telegraf"
telegraf_interval: "10s"
telegraf_synology_snmp: false
telegraf_synology_host: ""
telegraf_snmp_sec_name: "{{ vault_snmp_sec_name | default('') }}"
telegraf_snmp_auth_password: "{{ vault_snmp_auth_password | default('') }}"
telegraf_snmp_priv_password: "{{ vault_snmp_priv_password | default('') }}"

View file

@ -0,0 +1,5 @@
---
- name: Telegraf neustarten
systemd:
name: telegraf
state: restarted

View file

@ -0,0 +1,51 @@
---
- name: InfluxData GPG Key hinzufuegen
get_url:
url: https://repos.influxdata.com/influxdata-archive_compat.key
dest: /etc/apt/keyrings/influxdata-archive-keyring.gpg
mode: "0644"
- name: InfluxData Repo hinzufuegen
copy:
dest: /etc/apt/sources.list.d/influxdata.list
content: "deb [signed-by=/etc/apt/keyrings/influxdata-archive-keyring.gpg] https://repos.influxdata.com/debian stable main"
mode: "0644"
register: influx_repo
- name: Apt Cache aktualisieren
apt:
update_cache: yes
when: influx_repo.changed
- name: Telegraf installieren
apt:
name: telegraf
state: present
- name: lm-sensors installieren (fuer inputs.sensors)
apt:
name: lm-sensors
state: present
- name: SNMP Pakete installieren (fuer Synology Monitoring)
apt:
name:
- snmp
- snmp-mibs-downloader
state: present
when: telegraf_synology_snmp
- name: Telegraf Config deployen
template:
src: telegraf.conf.j2
dest: /etc/telegraf/telegraf.conf
mode: "0640"
owner: root
group: telegraf
notify: Telegraf neustarten
- name: Telegraf aktivieren und starten
systemd:
name: telegraf
enabled: true
state: started

View file

@ -0,0 +1,469 @@
[global_tags]
[agent]
interval = "{{ telegraf_interval }}"
round_interval = true
metric_batch_size = 1000
metric_buffer_limit = 10000
collection_jitter = "0s"
flush_interval = "10s"
flush_jitter = "0s"
precision = "0s"
hostname = ""
omit_hostname = false
[[outputs.influxdb_v2]]
urls = ["{{ telegraf_influx_url }}"]
organization = "{{ telegraf_influx_org }}"
token = "{{ telegraf_influx_token }}"
bucket = "{{ telegraf_influx_bucket }}"
[[inputs.cpu]]
percpu = true
totalcpu = true
fielddrop = ["time_*"]
[[inputs.disk]]
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]
[[inputs.diskio]]
[[inputs.kernel]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.swap]]
[[inputs.system]]
[[inputs.nstat]]
[[inputs.sensors]]
{% if telegraf_synology_snmp %}
# ============================================
# Synology NAS SNMP Monitoring
# ============================================
[[inputs.snmp]]
agents = ["{{ telegraf_synology_host }}"]
interval = "30s"
timeout = "30s"
retries = 3
max_repetitions = 10
version = 3
sec_name = "{{ telegraf_snmp_sec_name }}"
auth_protocol = "MD5"
auth_password = "{{ telegraf_snmp_auth_password }}"
sec_level = "authPriv"
priv_protocol = "DES"
priv_password = "{{ telegraf_snmp_priv_password }}"
name = "snmp.Synology"
# --- SNMPv2 System Info ---
[[inputs.snmp.field]]
name = "sysName"
oid = "SNMPv2-MIB::sysName.0"
is_tag = true
[[inputs.snmp.field]]
name = "sysDescr"
oid = "SNMPv2-MIB::sysDescr.0"
[[inputs.snmp.field]]
name = "sysContact"
oid = "SNMPv2-MIB::sysContact.0"
[[inputs.snmp.field]]
name = "sysLocation"
oid = "SNMPv2-MIB::sysLocation.0"
[[inputs.snmp.field]]
name = "sysUpTime"
oid = "SNMPv2-MIB::sysUpTime.0"
# --- UCD-SNMP-MIB: System Stats ---
[[inputs.snmp.field]]
name = "systemStats.ssSwapIn"
oid = "UCD-SNMP-MIB::ssSwapIn.0"
[[inputs.snmp.field]]
name = "systemStats.ssSwapOut"
oid = "UCD-SNMP-MIB::ssSwapOut.0"
[[inputs.snmp.field]]
name = "systemStats.ssIOSent"
oid = "UCD-SNMP-MIB::ssIOSent.0"
[[inputs.snmp.field]]
name = "systemStats.ssIOReceive"
oid = "UCD-SNMP-MIB::ssIOReceive.0"
[[inputs.snmp.field]]
name = "systemStats.ssSysInterrupts"
oid = "UCD-SNMP-MIB::ssSysInterrupts.0"
[[inputs.snmp.field]]
name = "systemStats.ssSysContext"
oid = "UCD-SNMP-MIB::ssSysContext.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuUser"
oid = "UCD-SNMP-MIB::ssCpuUser.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuSystem"
oid = "UCD-SNMP-MIB::ssCpuSystem.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuIdle"
oid = "UCD-SNMP-MIB::ssCpuIdle.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawUser"
oid = "UCD-SNMP-MIB::ssCpuRawUser.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawNice"
oid = "UCD-SNMP-MIB::ssCpuRawNice.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawSystem"
oid = "UCD-SNMP-MIB::ssCpuRawSystem.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawIdle"
oid = "UCD-SNMP-MIB::ssCpuRawIdle.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawWait"
oid = "UCD-SNMP-MIB::ssCpuRawWait.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawKernel"
oid = "UCD-SNMP-MIB::ssCpuRawKernel.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawInterrupt"
oid = "UCD-SNMP-MIB::ssCpuRawInterrupt.0"
[[inputs.snmp.field]]
name = "systemStats.ssIORawSent"
oid = "UCD-SNMP-MIB::ssIORawSent.0"
[[inputs.snmp.field]]
name = "systemStats.ssIORawReceived"
oid = "UCD-SNMP-MIB::ssIORawReceived.0"
[[inputs.snmp.field]]
name = "systemStats.ssRawInterrupts"
oid = "UCD-SNMP-MIB::ssRawInterrupts.0"
[[inputs.snmp.field]]
name = "systemStats.ssRawContexts"
oid = "UCD-SNMP-MIB::ssRawContexts.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawSoftIRQ"
oid = "UCD-SNMP-MIB::ssCpuRawSoftIRQ.0"
[[inputs.snmp.field]]
name = "systemStats.ssRawSwapIn"
oid = "UCD-SNMP-MIB::ssRawSwapIn.0"
[[inputs.snmp.field]]
name = "systemStats.ssRawSwapOut"
oid = "UCD-SNMP-MIB::ssRawSwapOut.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawSteal"
oid = "UCD-SNMP-MIB::ssCpuRawSteal.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawGuest"
oid = "UCD-SNMP-MIB::ssCpuRawGuest.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuRawGuestNice"
oid = "UCD-SNMP-MIB::ssCpuRawGuestNice.0"
[[inputs.snmp.field]]
name = "systemStats.ssCpuNumCpus"
oid = "UCD-SNMP-MIB::ssCpuNumCpus.0"
# --- UCD-SNMP-MIB: Memory ---
[[inputs.snmp.field]]
name = "memory.memTotalSwap"
oid = "UCD-SNMP-MIB::memTotalSwapX.0"
[[inputs.snmp.field]]
name = "memory.memAvailSwap"
oid = "UCD-SNMP-MIB::memAvailSwapX.0"
[[inputs.snmp.field]]
name = "memory.memTotalReal"
oid = "UCD-SNMP-MIB::memTotalRealX.0"
[[inputs.snmp.field]]
name = "memory.memAvailReal"
oid = "UCD-SNMP-MIB::memAvailRealX.0"
[[inputs.snmp.field]]
name = "memory.memTotalFree"
oid = "UCD-SNMP-MIB::memTotalFreeX.0"
[[inputs.snmp.field]]
name = "memory.memMinimumSwap"
oid = "UCD-SNMP-MIB::memMinimumSwapX.0"
[[inputs.snmp.field]]
name = "memory.memShared"
oid = "UCD-SNMP-MIB::memSharedX.0"
[[inputs.snmp.field]]
name = "memory.memBuffer"
oid = "UCD-SNMP-MIB::memBufferX.0"
[[inputs.snmp.field]]
name = "memory.memCached"
oid = "UCD-SNMP-MIB::memCachedX.0"
# --- HOST-RESOURCES-MIB: System ---
[[inputs.snmp.field]]
name = "hrSystem.hrSystemUptime"
oid = "HOST-RESOURCES-MIB::hrSystemUptime.0"
[[inputs.snmp.field]]
name = "hrSystem.hrSystemNumUsers"
oid = "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
[[inputs.snmp.field]]
name = "hrSystem.hrSystemProcesses"
oid = "HOST-RESOURCES-MIB::hrSystemProcesses.0"
# --- SYNOLOGY-SYSTEM-MIB ---
[[inputs.snmp.field]]
name = "synoSystem.systemStatus"
oid = "SYNOLOGY-SYSTEM-MIB::systemStatus.0"
[[inputs.snmp.field]]
name = "synoSystem.temperature"
oid = "SYNOLOGY-SYSTEM-MIB::temperature.0"
[[inputs.snmp.field]]
name = "synoSystem.powerStatus"
oid = "SYNOLOGY-SYSTEM-MIB::powerStatus.0"
[[inputs.snmp.field]]
name = "synoSystem.systemFanStatus"
oid = "SYNOLOGY-SYSTEM-MIB::systemFanStatus.0"
[[inputs.snmp.field]]
name = "synoSystem.cpuFanStatus"
oid = "SYNOLOGY-SYSTEM-MIB::cpuFanStatus.0"
[[inputs.snmp.field]]
name = "synoSystem.modelName"
oid = "SYNOLOGY-SYSTEM-MIB::modelName.0"
[[inputs.snmp.field]]
name = "synoSystem.serialNumber"
oid = "SYNOLOGY-SYSTEM-MIB::serialNumber.0"
[[inputs.snmp.field]]
name = "synoSystem.version"
oid = "SYNOLOGY-SYSTEM-MIB::version.0"
[[inputs.snmp.field]]
name = "synoSystem.upgradeAvailable"
oid = "SYNOLOGY-SYSTEM-MIB::upgradeAvailable.0"
# --- Tables: Load ---
[[inputs.snmp.table]]
oid = "UCD-SNMP-MIB::laTable"
name = "snmp.Synology.load"
[[inputs.snmp.table.field]]
oid = "UCD-SNMP-MIB::laNames"
is_tag = true
# --- Tables: Network ---
[[inputs.snmp.table]]
oid = "IF-MIB::ifTable"
name = "snmp.Synology.network"
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifDescr"
is_tag = true
[[inputs.snmp.table]]
oid = "IF-MIB::ifXTable"
name = "snmp.Synology.network"
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifName"
is_tag = true
# --- Tables: Volume ---
[[inputs.snmp.table]]
oid = "HOST-RESOURCES-MIB::hrStorageTable"
name = "snmp.Synology.volume"
[[inputs.snmp.table.field]]
oid = "HOST-RESOURCES-MIB::hrStorageDescr"
is_tag = true
# --- Tables: Disk ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-DISK-MIB::diskTable"
name = "snmp.Synology.disk"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-DISK-MIB::diskID"
is_tag = true
# --- Tables: RAID ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-RAID-MIB::raidTable"
name = "snmp.Synology.raid"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-RAID-MIB::raidName"
is_tag = true
# --- Tables: SSD Cache ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheTable"
name = "snmp.Synology.cache"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheSpaceDev"
is_tag = true
# --- Tables: S.M.A.R.T. ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-SMART-MIB::diskSMARTTable"
name = "snmp.Synology.smart"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-SMART-MIB::diskSMARTInfoDevName"
is_tag = true
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-SMART-MIB::diskSMARTAttrName"
is_tag = true
# --- Tables: Space IO ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-SPACEIO-MIB::spaceIOTable"
name = "snmp.Synology.spaceIO"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-SPACEIO-MIB::spaceIODevice"
is_tag = true
# --- Tables: Storage IO ---
[[inputs.snmp.table]]
oid = "SYNOLOGY-STORAGEIO-MIB::storageIOTable"
name = "snmp.Synology.storageIO"
[[inputs.snmp.table.field]]
oid = "SYNOLOGY-STORAGEIO-MIB::storageIODevice"
is_tag = true
# --- Tables: eBox ---
[[inputs.snmp.table]]
name = "snmp.Synology.ebox"
oid = "SYNOLOGY-EBOX-MIB::eboxTable"
[[inputs.snmp.table.field]]
name = "snmp.Synology.ebox.eboxIndex"
oid = "SYNOLOGY-EBOX-MIB::eboxIndex"
[[inputs.snmp.table.field]]
name = "snmp.Synology.ebox.eboxModel"
oid = "SYNOLOGY-EBOX-MIB::eboxModel"
[[inputs.snmp.table.field]]
name = "snmp.Synology.ebox.eboxPower"
oid = "SYNOLOGY-EBOX-MIB::eboxPower"
[[inputs.snmp.table.field]]
name = "snmp.Synology.ebox.eboxRedundantPower"
oid = "SYNOLOGY-EBOX-MIB::eboxRedundantPower"
# --- Tables: Flash Cache ---
[[inputs.snmp.table]]
name = "snmp.Synology.flashcache"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheTable"
[[inputs.snmp.table.field]]
name = "flashCacheIndex"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheIndex"
[[inputs.snmp.table.field]]
name = "flashCacheSSDDev"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheSSDDev"
[[inputs.snmp.table.field]]
name = "flashCacheSpaceDev"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheSpaceDev"
[[inputs.snmp.table.field]]
name = "flashCacheReadHits"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheReadHits"
[[inputs.snmp.table.field]]
name = "flashCacheWriteHits"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheWriteHits"
[[inputs.snmp.table.field]]
name = "flashCacheTotalRead"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheTotalRead"
[[inputs.snmp.table.field]]
name = "flashCacheTotalWrite"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheTotalWrite"
[[inputs.snmp.table.field]]
name = "flashCacheReadHitRate"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheReadHitRate"
[[inputs.snmp.table.field]]
name = "flashCacheWriteHitRate"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheWriteHitRate"
[[inputs.snmp.table.field]]
name = "flashCacheSsdUuid"
oid = "SYNOLOGY-FLASHCACHE-MIB::flashCacheSsdUuid"
# --- Tables: GPU ---
[[inputs.snmp.table]]
name = "snmp.Synology.gpu"
[[inputs.snmp.table.field]]
name = "gpuUtilization"
oid = "SYNOLOGY-GPUINFO-MIB::gpuUtilization"
[[inputs.snmp.table.field]]
name = "gpuMemoryUtilization"
oid = "SYNOLOGY-GPUINFO-MIB::gpuMemoryUtilization"
[[inputs.snmp.table.field]]
name = "gpuMemoryFree"
oid = "SYNOLOGY-GPUINFO-MIB::gpuMemoryFree"
[[inputs.snmp.table.field]]
name = "gpuMemoryUsed"
oid = "SYNOLOGY-GPUINFO-MIB::gpuMemoryUsed"
[[inputs.snmp.table.field]]
name = "gpuMemoryTotal"
oid = "SYNOLOGY-GPUINFO-MIB::gpuMemoryTotal"
# --- Tables: iSCSI LUN ---
[[inputs.snmp.table]]
name = "snmp.Synology.iscsilun"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNTable"
[[inputs.snmp.table.field]]
name = "iSCSILUNInfoIndex"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNInfoIndex"
[[inputs.snmp.table.field]]
name = "iSCSILUNUUID"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNUUID"
[[inputs.snmp.table.field]]
name = "iSCSILUNName"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNName"
[[inputs.snmp.table.field]]
name = "iSCSILUNThroughputReadHigh"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNThroughputReadHigh"
[[inputs.snmp.table.field]]
name = "iSCSILUNThroughputReadLow"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNThroughputReadLow"
[[inputs.snmp.table.field]]
name = "iSCSILUNThroughputWriteHigh"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNThroughputWriteHigh"
[[inputs.snmp.table.field]]
name = "iSCSILUNThroughputWriteLow"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNThroughputWriteLow"
[[inputs.snmp.table.field]]
name = "iSCSILUNIopsRead"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNIopsRead"
[[inputs.snmp.table.field]]
name = "iSCSILUNIopsWrite"
oid = "SYNOLOGY-ISCSILUN-MIB::iSCSILUNIopsWrite"
# --- Tables: iSCSI Target ---
[[inputs.snmp.table]]
name = "snmp.Synology.iscsitarget"
oid = "SYNOLOGY-ISCSITarget-MIB::iSCSITargetTable"
[[inputs.snmp.table.field]]
name = "iSCSITargetName"
oid = "SYNOLOGY-ISCSITarget-MIB::iSCSITargetName"
[[inputs.snmp.table.field]]
name = "iSCSITargetIQN"
oid = "SYNOLOGY-ISCSITarget-MIB::iSCSITargetIQN"
[[inputs.snmp.table.field]]
name = "iSCSITargetConnectionStatus"
oid = "SYNOLOGY-ISCSITarget-MIB::iSCSITargetConnectionStatus"
# --- Tables: Services ---
[[inputs.snmp.table]]
name = "snmp.Synology.services"
oid = "SYNOLOGY-SERVICES-MIB::serviceTable"
[[inputs.snmp.table.field]]
name = "serviceName"
oid = "SYNOLOGY-SERVICES-MIB::serviceName"
is_tag = true
[[inputs.snmp.table.field]]
name = "serviceUsers"
oid = "SYNOLOGY-SERVICES-MIB::serviceUsers"
# --- HA ---
[[inputs.snmp.field]]
name = "ha.activeNodeName"
oid = "SYNOLOGY-SHA-MIB::activeNodeName"
[[inputs.snmp.field]]
name = "ha.passiveNodeName"
oid = "SYNOLOGY-SHA-MIB::passiveNodeName"
[[inputs.snmp.field]]
name = "ha.clusterAutoFailover"
oid = "SYNOLOGY-SHA-MIB::clusterAutoFailover"
[[inputs.snmp.field]]
name = "ha.clusterName"
oid = "SYNOLOGY-SHA-MIB::clusterName"
[[inputs.snmp.field]]
name = "ha.clusterStatus"
oid = "SYNOLOGY-SHA-MIB::clusterStatus"
[[inputs.snmp.field]]
name = "ha.heartbeatStatus"
oid = "SYNOLOGY-SHA-MIB::heartbeatStatus"
[[inputs.snmp.field]]
name = "ha.heartbeatTxRate"
oid = "SYNOLOGY-SHA-MIB::heartbeatTxRate"
[[inputs.snmp.field]]
name = "ha.heartbeatLatency"
oid = "SYNOLOGY-SHA-MIB::heartbeatLatency"
{% endif %}

View file

@ -0,0 +1,5 @@
---
wireguard_endpoint: "tunnel.sascha-lutz.de:51820"
wireguard_vps_pubkey: "{{ vault_wireguard_vps_pubkey }}"
wireguard_mtu: 1420
wireguard_persistent_keepalive: 25

View file

@ -0,0 +1,5 @@
---
- name: restart wireguard
systemd:
name: wg-quick@wg0
state: restarted

View file

@ -0,0 +1,32 @@
---
- name: WireGuard und resolvconf installieren
apt:
name:
- wireguard
- resolvconf
state: present
update_cache: yes
- name: WireGuard Config deployen
copy:
dest: /etc/wireguard/wg0.conf
content: |
[Interface]
PrivateKey = {{ wireguard_private_key }}
Address = {{ wireguard_address }}
DNS = 8.8.8.8
MTU = {{ wireguard_mtu }}
[Peer]
PublicKey = {{ wireguard_vps_pubkey }}
Endpoint = {{ wireguard_endpoint }}
AllowedIPs = {{ wireguard_allowed_ips }}
PersistentKeepalive = {{ wireguard_persistent_keepalive }}
mode: "0600"
notify: restart wireguard
- name: WireGuard aktivieren und starten
systemd:
name: wg-quick@wg0
enabled: true
state: started

19
site.yml Normal file
View file

@ -0,0 +1,19 @@
---
# Neue VM komplett einrichten
- name: VM Setup
hosts: all
become: yes
roles:
- base
- docker
- borg
- hawser
- sysctl
# Proxmox Hosts
- name: Proxmox Tuning
hosts: proxmox
become: yes
roles:
- sysctl_proxmox
- telegraf

View file

@ -1,27 +1,6 @@
--- ---
- name: Sysctl Tuning fuer Proxmox Hosts - name: Sysctl Tuning fuer Proxmox Hosts
hosts: proxmox hosts: proxmox
become: true become: yes
roles:
tasks: - sysctl_proxmox
- name: Sysctl Parameter setzen
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/99-proxmox-tuning.conf
reload: true
state: present
loop:
# KVM / Virtualisierung
- { key: vm.overcommit_memory, value: "1" }
- { key: vm.swappiness, value: "1" }
# Viele VMs = viele File Handles
- { key: fs.file-max, value: "9999999" }
- { key: fs.inotify.max_user_watches, value: "524288" }
- { key: fs.inotify.max_user_instances, value: "512" }
# Routing zwischen VMs / Bridges
- { key: net.ipv4.ip_forward, value: "1" }
# WireGuard MTU-Anpassung
# TODO: entfernen sobald WireGuard abgeschaltet wird
- { key: net.ipv4.tcp_mtu_probing, value: "1" }

View file

@ -1,41 +1,6 @@
--- ---
- name: Sysctl Tuning fuer Emby - name: Sysctl Tuning fuer Streaming-VMs
hosts: all hosts: all
become: true become: yes
roles:
tasks: - sysctl
- name: BBR Kernel Modul laden
ansible.builtin.modprobe:
name: tcp_bbr
state: present
- name: BBR Modul beim Boot laden
ansible.builtin.copy:
content: "tcp_bbr\n"
dest: /etc/modules-load.d/bbr.conf
mode: "0644"
- name: Sysctl Parameter setzen
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/99-net-tuning.conf
reload: true
state: present
loop:
- { key: net.core.rmem_default, value: "262144" }
- { key: net.core.wmem_default, value: "262144" }
- { key: net.core.rmem_max, value: "67108864" }
- { key: net.core.wmem_max, value: "67108864" }
- { key: net.ipv4.tcp_rmem, value: "4096 87380 67108864" }
- { key: net.ipv4.tcp_wmem, value: "4096 65536 67108864" }
- { key: net.ipv4.tcp_window_scaling, value: "1" }
- { key: net.ipv4.tcp_congestion_control, value: "bbr" }
- { key: net.ipv4.tcp_slow_start_after_idle, value: "0" }
- { key: net.ipv4.tcp_fastopen, value: "3" }
- { key: net.core.netdev_max_backlog, value: "16384" }
# RAM ist reichlich vorhanden, Transcoding auf tmpfs
- { key: vm.swappiness, value: "1" }
- { key: vm.dirty_ratio, value: "15" }
- { key: vm.dirty_background_ratio, value: "5" }

6
telegraf.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: Telegraf Monitoring Setup
hosts: proxmox
become: yes
roles:
- telegraf

1
tmp/n8n Normal file
View file

@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwNTBlYzdjZC0yNTgzLTQ0ZjEtOTIzZS02N2NlYWY0Y2FlYTEiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwianRpIjoiYTUxNDA4MjQtOTJlMC00MzA0LTg2OGUtMDc4ZmNhNTNjZjIxIiwiaWF0IjoxNzc0Njk5NzI4fQ.DTs-IlULYHVR52WVqBGPkEz0ieHc5tuhT0KRdUN6N6o

1
tmp/radarr Normal file
View file

@ -0,0 +1 @@
b417c7461d4942e1b8530d80d68c3717

2
tmp/radarr1080p Normal file
View file

@ -0,0 +1,2 @@
http://10.2.1.100:7879/
dadb191cff9c4b4ab309653f3f9da5ce

1
tmp/seer Normal file
View file

@ -0,0 +1 @@
MTczODM1NzY3NjczMTYxMzc4ZDZlLWFiYjgtNGQwNi05ZTRjLWMyNTQ1Zjk0M2Y4ZA==

13
tmp/seer-webhook Normal file
View file

@ -0,0 +1,13 @@
url:
http://10.4.1.113:5678/webhook/seerr-notify
Webhook:
{
"session": "default",
"chatId": "120363404705299449@newsletter",
"text": "*🎬 Jetzt Verfügbar*\n\n *Titel:* {{subject}}\n\n *Beschreibung:* {{message}}\n\n *Link zum Poster:* {{image}} \n\n ➡️ Viel Spaß!! \n\n",
"linkPreview": true,
"linkPreviewHighQuality": true
}

1
tmp/semaphore Normal file
View file

@ -0,0 +1 @@
z0yv8u3qswftq8lowwvjb4hm5iwrbwfg5i3gpnryu7u=

1
tmp/sonarr Normal file
View file

@ -0,0 +1 @@
cf2599df0a094dc388a62fb671f9d139

2
tmp/sonarr1080p Normal file
View file

@ -0,0 +1,2 @@
http://10.2.1.100:8990/
b679d30cbf0d4641948b76659de1a02a

View file

@ -8,3 +8,30 @@
update_cache: yes update_cache: yes
upgrade: dist upgrade: dist
autoremove: yes autoremove: yes
- name: Pruefen ob Reboot noetig ist
ansible.builtin.stat:
path: /var/run/reboot-required
register: reboot_required
- name: NVIDIA Kernel Headers nach Upgrade sicherstellen
hosts: nvidia
become: yes
tasks:
- name: Kernel Headers und DKMS installieren und Module bauen
ansible.builtin.shell: |
export PATH=$PATH:/usr/sbin
apt-get install -y linux-headers-$(uname -r) dkms
dkms autoinstall
register: headers_result
changed_when: "'newly installed' in headers_result.stdout"
- name: Reboot falls noetig
hosts: all
become: yes
tasks:
- name: Reboot durchfuehren
ansible.builtin.reboot:
msg: "Reboot nach Update"
reboot_timeout: 300
when: hostvars[inventory_hostname].reboot_required.stat.exists

6
wireguard.yml Normal file
View file

@ -0,0 +1,6 @@
---
- name: WireGuard deployen
hosts: wireguard
become: yes
roles:
- wireguard

32
wstunnel-cleanup.yml Normal file
View file

@ -0,0 +1,32 @@
---
- name: wstunnel entfernen
hosts: hysteria2
become: yes
tasks:
- name: wstunnel Service stoppen und deaktivieren
systemd:
name: wstunnel
state: stopped
enabled: false
ignore_errors: true
- name: wstunnel systemd Unit entfernen
file:
path: /etc/systemd/system/wstunnel.service
state: absent
notify: reload systemd
- name: wstunnel Binary entfernen
file:
path: /usr/local/bin/wstunnel
state: absent
- name: wstunnel Config entfernen
file:
path: /etc/wstunnel
state: absent
handlers:
- name: reload systemd
systemd:
daemon_reload: true