Compare commits

..

15 Commits

Author SHA1 Message Date
09d3ced858 Merge pull request 'dokufix' (#6) from doc into main
Reviewed-on: #6
2025-10-31 17:13:26 +00:00
scriptos
f88a0df2d9 dokufix 2025-10-31 18:12:43 +01:00
754c9c1f9e Merge pull request 'Dokumentation' (#5) from doc into main
Reviewed-on: #5
2025-10-29 10:15:01 +00:00
scriptos
ed1ac2364a Dokumentation 2025-10-29 11:14:43 +01:00
48cea89a36 Merge pull request 'Doku.' (#4) from yamlfix into main
Reviewed-on: #4
2025-10-27 21:01:08 +00:00
scriptos
04b741b28b Doku. 2025-10-27 22:00:40 +01:00
f72287637b Merge pull request 'Funktion "detect_compose_cmd" nachträglich committed.' (#3) from yamlfix into main
Reviewed-on: #3
2025-10-27 19:34:17 +00:00
scriptos
189bd41ea1 Funktion "detect_compose_cmd" nachträglich committed. 2025-10-27 20:33:38 +01:00
a9e9cf15a0 Merge pull request 'Funktionen "update_yaml_with_yq" und "update_yaml_fallback" überarbeitet' (#2) from yamlfix into main
Reviewed-on: #2
2025-10-27 19:25:19 +00:00
scriptos
45f496590d Funktionen "update_yaml_with_yq" und "update_yaml_fallback" überarbeitet 2025-10-27 20:24:14 +01:00
6f85457ed9 Merge pull request 'yamlfix' (#1) from yamlfix into main
Reviewed-on: #1
2025-10-27 18:45:46 +00:00
scriptos
61ebf046b8 healthcheck auf HTTP mit Custom URL umgestellt. 2025-10-27 19:45:26 +01:00
scriptos
7cbd5cd5be fix update_yaml_fallback 2025-10-27 19:37:57 +01:00
scriptos
2daaadc3b3 HTTP Healthcheck entfernt, wird nicht benötigt. 2025-10-27 19:13:17 +01:00
scriptos
1473eed0e0 Initial. 2025-10-27 19:07:55 +01:00
3 changed files with 412 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 scriptos
Copyright (c) 2025 scriptos
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,9 +1,102 @@
# template_repository
# 🧩 LiveKit IP Watch
Ein einfaches Bash-Script, das bei dynamischen Internetanschlüssen (z. B. DSL) automatisch die öffentliche IP-Adresse ermittelt, in die LiveKit-`config.yaml` einträgt und anschließend den Dienst neu startet.
Nach dem Neustart prüft das Script per HTTP-Healthcheck, ob LiveKit wieder erreichbar ist.
Dieses Repository ist **begleitend zu meinem Blogbeitrag**, den du hier lesen kannst:
👉 [https://www.cleveradmin.de/blog/2025/10/livekit-dynamische-ip-automatisieren](https://www.cleveradmin.de/blog/2025/10/livekit-dynamische-ip-automatisieren)
Wichtig: Link für Lizenz anpassen.
---
## ⚙️ Konfiguration
Öffne die Scriptdatei `livekit-ip-watch.v1.sh` und passe folgende Variablen an:
| Variable | Beschreibung |
|---------|--------------|
| `CONFIG_FILE` | Pfad zur LiveKit-Konfigurationsdatei (`config.yaml`). |
| `RUNTIME` | Modus: `"compose"` für Docker Compose oder `"docker"` für Plain Docker. |
| `COMPOSE_FILE_PATH` | Vollständiger Pfad zur `docker-compose.yaml`, falls `RUNTIME="compose"`. |
| `COMPOSE_SERVICE` | Name des LiveKit-Services in deiner Compose-Datei. |
| `CONTAINER_NAME` | Nur relevant, wenn `RUNTIME="docker"` Name des Containers. |
| `ENABLE_IPV6` | `true` für IPv6, `false` für IPv4. |
| `WAIT_AFTER_RESTART` | Wartezeit (Sekunden) vor dem Healthcheck. |
| `HEALTHCHECK_DOMAIN` | Nur den DNS angeben, z. B. `rtc.matrix.techniverse.net`. Das Script prüft automatisch `/livekit/sfu/`. |
| `HEALTHCHECK_TIMEOUT` | Timeout in Sekunden für den HTTP-Check. |
| `NTFY_URL` / `NTFY_TOKEN` | Optional für Benachrichtigungen via ntfy. |
---
## 📦 Voraussetzungen
- bash
- git
- yq (optional, aber empfohlen für saubere YAML-Bearbeitung)
---
## 🚀 Verwendung
Script herunterladen und starten:
```bash
git clone https://git.techniverse.net/scriptos/livekit-ip-watch.git
cd livekit-ip-watch
chmod +x livekit-ip-watch.v1.sh
bash livekit-ip-watch.v1.sh
```
Beispielausgabe:
```bash
➜ bash livekit-ip-watch.v1.sh
[2025-10-27 19:56:30] INFO: Neue IP erkannt: 152.53.118.246 (alt: none)
[2025-10-27 19:56:30] INFO: Backup erstellt: /home/scripts/livekit-ip-watch/backups/config.yaml.20251027195630.bak
[2025-10-27 19:56:30] WARN: yq nicht gefunden Fallback-Editor wird verwendet.
[2025-10-27 19:56:30] INFO: Restart des Dienstes...
[+] Restarting 1/1
✔ Container matrix-element-call-livekit Started 46.8s
[2025-10-27 19:57:17] INFO: Warte 20 Sekunden...
[2025-10-27 19:57:37] INFO: Healthcheck OK.
```
[![asciicast](https://asciinema.techniverse.net/a/68.svg)](https://asciinema.techniverse.net/a/68)
---
## ⏰ Automatische Ausführung (Cronjob)
Damit die IP regelmäßig geprüft und aktualisiert wird, kannst du das Script z. B. alle 15 Minuten ausführen:
```bash
crontab -e
```
Und füge diese Zeile hinzu:
```bash
*/15 * * * * /home/scripts/livekit-ip-watch/livekit-ip-watch.v1.sh >> /var/log/livekit-ip-watch.log 2>&1
```
---
## 🧠 Hinweis
Das Script erstellt bei jeder Änderung ein Backup der `config.yaml` und führt bei einem Fehler automatisch ein Rollback durch.
---
Autor: Patrick Asmus
Repository: https://git.techniverse.net/scriptos/livekit-ip-watch.git
---
## 💬 Support & Community
Du hast Fragen, brauchst Unterstützung bei der Einrichtung oder möchtest dich einfach mit anderen austauschen, die ähnliche Projekte betreiben? Dann schau gerne in unserer Techniverse Community vorbei:
👉 **Matrix-Raum:** [#community:techniverse.net](https://matrix.to/#/#community:techniverse.net)
Wir freuen uns auf deinen Besuch und helfen dir gerne weiter!
<p align="center">

316
livekit-ip-watch.v1.sh Normal file
View File

@@ -0,0 +1,316 @@
#!/usr/bin/env bash
# Beschreibung: Aktualisiert livekit.yaml (rtc.ips.includes) bei IP-Wechsel (IPv4 ODER IPv6),
# restarte den LiveKit-Dienst (Docker oder Docker Compose), wartet konfigurierbar und prüft Health.
# Mit Backup + Rollback, optionalen ntfy-Notifications, YAML-Update via yq (Fallback eingebaut).
# Synapse: https://git.techniverse.net/scriptos/livekit-ip-watch.git
# Autor: Patrick Asmus
# Web: https://www.cleveradmin.de
# Version: 1.2.1
# Datum: 27.10.2025
# Modifikation: Funktion "detect_compose_cmd" nachträglich committed.
#####################################################
set -euo pipefail
############################
# Konfiguration (Variablen)
############################
# Pfad zur LiveKit-Konfiguration
CONFIG_FILE="/home/docker-container/matrix-rtc/data/matrix-element-call-livekit/config.yaml"
# Betriebsmodus: "compose" oder "docker"
RUNTIME="compose"
# Compose: Kompletter Pfad zur Compose-Datei + Service-Name
COMPOSE_FILE_PATH="/home/docker-container/matrix-rtc/docker-compose.yaml"
COMPOSE_SERVICE="matrix-element-call-livekit"
# Plain-Docker: Container-Name (nur genutzt, wenn RUNTIME="docker")
CONTAINER_NAME="matrix-element-call-livekit"
# IP-Variante: IPv4 oder IPv6
ENABLE_IPV6=false
# Wartezeit nach Neustart (Sekunden), dann Healthcheck
WAIT_AFTER_RESTART=20
# Healthcheck-Konfiguration: HTTP
HEALTHCHECK_DOMAIN="rtc.matrix.techniverse.net"
HEALTHCHECK_TIMEOUT=3
# ntfy (optional)
NTFY_URL=""
NTFY_TOKEN=""
############################
# Hauptscript
############################
# Skriptverzeichnis, Lock- und State-Dateien
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCKFILE="$SCRIPT_DIR/livekit-ip-watch.lock"
STATE_FILE_V4="$SCRIPT_DIR/last_ip_v4"
STATE_FILE_V6="$SCRIPT_DIR/last_ip_v6"
BACKUP_DIR="$SCRIPT_DIR/backups"
mkdir -p "$BACKUP_DIR"
log() { printf '[%(%Y-%m-%d %H:%M:%S)T] %s\n' -1 "$*" >&2; }
die() { log "ERROR: $*"; notify "error" "$*"; exit 1; }
warn() { log "WARN: $*"; notify "warn" "$*"; }
info() { log "INFO: $*"; }
# Ntfy
notify() {
local level="${1:-info}"
shift || true
local msg="${*:-}"
[[ -z "$NTFY_URL" ]] && return 0
local hdr=(-H "Title: livekit-ip-watch" -H "Priority: default" -H "Tags: $level")
[[ -n "$NTFY_TOKEN" ]] && hdr+=(-H "Authorization: Bearer $NTFY_TOKEN")
curl -fsS -X POST "${hdr[@]}" -d "$msg" "$NTFY_URL" >/dev/null 2>&1 || true
}
# Lock
acquire_lock() {
if command -v flock >/dev/null 2>&1; then
exec 9>"$LOCKFILE"
if ! flock -n 9; then
info "Läuft bereits (Lock vorhanden): $LOCKFILE"
exit 0
fi
else
set -o noclobber
if ! : > "$LOCKFILE"; then
info "Läuft bereits (Lock vorhanden): $LOCKFILE"
exit 0
fi
set +o noclobber
fi
trap 'release_lock' EXIT
}
release_lock() {
rm -f "$LOCKFILE" 2>/dev/null || true
}
# IP-Ermittlung
get_ipv4() {
local ip
ip="$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null || true)"
[[ -z "$ip" ]] && ip="$(curl -fsS https://api.ipify.org 2>/dev/null || true)"
[[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || ip=""
echo "$ip"
}
get_ipv6() {
local ip
ip="$(curl -fsS -6 https://api6.ipify.org 2>/dev/null || true)"
if [[ "$ip" =~ : ]]; then
echo "$ip"
else
echo ""
fi
}
have_yq() { command -v yq >/dev/null 2>&1; }
backup_config() {
[[ -r "$CONFIG_FILE" ]] || die "Config nicht lesbar: $CONFIG_FILE"
local ts backup
ts="$(date +%Y%m%d%H%M%S)"
backup="$BACKUP_DIR/$(basename "$CONFIG_FILE").$ts.bak"
cp -a "$CONFIG_FILE" "$backup"
echo "$backup"
}
is_mikefarah_yq() {
yq --version 2>&1 | grep -qi 'mikefarah'
}
update_yaml_with_yq() {
local cidr="$1"
if is_mikefarah_yq; then
CIDR="$cidr" yq eval -i '
.rtc = (.rtc // {}) |
.rtc.ips = (.rtc.ips // {}) |
.rtc.ips.includes = [env(CIDR)]
' "$CONFIG_FILE"
else
yq -y -i --arg CIDR "$cidr" '
.rtc = (.rtc // {}) |
.rtc.ips = (.rtc.ips // {}) |
.rtc.ips.includes = [$CIDR]
' "$CONFIG_FILE"
fi
}
update_yaml_fallback() {
local cidr="$1"
local tmp
tmp="$(mktemp)"
awk -v newcidr="$cidr" '
function indent_of(s, m,sp) { match(s,/^([ ]*)/,m); sp=length(m[1]); return sp }
BEGIN { in_rtc=0; rtc_indent=-1; skipping_ips=0; ips_indent=-1; injected=0 }
{
line=$0
ind=indent_of(line)
if ($0 ~ /^[[:space:]]*rtc:[[:space:]]*$/) {
in_rtc=1; rtc_indent=ind; injected=0
print line
next
}
if (in_rtc) {
if (skipping_ips) {
if (ind <= ips_indent) {
if (!injected) {
print " ips:"
print " includes:"
print " - " newcidr
injected=1
}
skipping_ips=0
} else { next }
}
if ($0 ~ /^[[:space:]]*ips:[[:space:]]*$/ && ind > rtc_indent) {
skipping_ips=1; ips_indent=ind
next
}
if (ind <= rtc_indent && $0 ~ /^[[:alnum:]]_+:[[:space:]]*$/) {
if (!injected) {
print " ips:"
print " includes:"
print " - " newcidr
injected=1
}
in_rtc=0; rtc_indent=-1
print line
next
}
print line
next
}
print line
}
END {
if (in_rtc && !injected) {
print " ips:"
print " includes:"
print " - " newcidr
}
}
' "$CONFIG_FILE" > "$tmp"
mv "$tmp" "$CONFIG_FILE"
}
detect_compose_cmd() {
if command -v docker-compose >/dev/null 2>&1; then
echo "docker-compose"
else
echo "docker compose"
fi
}
restart_service() {
if [[ "$RUNTIME" == "compose" ]]; then
local dir file
dir="$(dirname "$COMPOSE_FILE_PATH")"
file="$(basename "$COMPOSE_FILE_PATH")"
local COMPOSE_CMD
COMPOSE_CMD="$(detect_compose_cmd)"
( cd "$dir" && "$COMPOSE_CMD" -f "$file" restart "$COMPOSE_SERVICE" )
else
docker restart "$CONTAINER_NAME"
fi
}
healthcheck_http() {
local url="https://${HEALTHCHECK_DOMAIN}/livekit/sfu/"
curl -fsS --max-time "$HEALTHCHECK_TIMEOUT" "$url" >/dev/null
}
main() {
acquire_lock
[[ -f "$CONFIG_FILE" ]] || die "Config-Datei nicht gefunden: $CONFIG_FILE"
if [[ "$RUNTIME" == "compose" ]]; then
[[ -f "$COMPOSE_FILE_PATH" ]] || die "Compose-Datei nicht gefunden: $COMPOSE_FILE_PATH"
[[ -n "$COMPOSE_SERVICE" ]] || die "COMPOSE_SERVICE ist leer."
else
[[ -n "$CONTAINER_NAME" ]] || die "CONTAINER_NAME ist leer."
fi
local current_ip cidr state_file
if [[ "$ENABLE_IPV6" == true ]]; then
current_ip="$(get_ipv6)"
[[ -z "$current_ip" ]] && die "Konnte keine öffentliche IPv6 ermitteln."
cidr="${current_ip}/128"
state_file="$STATE_FILE_V6"
else
current_ip="$(get_ipv4)"
[[ -z "$current_ip" ]] && die "Konnte keine öffentliche IPv4 ermitteln."
cidr="${current_ip}/32"
state_file="$STATE_FILE_V4"
fi
local last_ip=""
[[ -f "$state_file" ]] && last_ip="$(cat "$state_file" 2>/dev/null || true)"
if [[ "$current_ip" == "$last_ip" && -n "$last_ip" ]]; then
info "IP unverändert: $current_ip"
notify "info" "Keine Änderung. Öffentliche IP bleibt $current_ip."
exit 0
fi
info "Neue IP erkannt: $current_ip (alt: ${last_ip:-none})"
local backup
backup="$(backup_config)"
info "Backup erstellt: $backup"
if have_yq; then
info "Aktualisiere YAML mit yq → .rtc.ips.includes = [ \"$cidr\" ]"
update_yaml_with_yq "$cidr"
else
warn "yq nicht gefunden Fallback-Editor wird verwendet."
update_yaml_fallback "$cidr"
fi
info "Restart des Dienstes..."
restart_service
info "Warte $WAIT_AFTER_RESTART Sekunden..."
sleep "$WAIT_AFTER_RESTART"
if healthcheck_http; then
info "Healthcheck OK."
echo "$current_ip" > "$state_file"
notify "info" "IP geändert auf $current_ip, Dienst gesund."
exit 0
fi
warn "Healthcheck fehlgeschlagen führe Rollback durch."
cp -a "$backup" "$CONFIG_FILE"
info "Rollback-Konfiguration wiederhergestellt: $backup"
info "Restart nach Rollback..."
restart_service
info "Warte $WAIT_AFTER_RESTART Sekunden (Rollback)..."
sleep "$WAIT_AFTER_RESTART"
if healthcheck_http; then
warn "Rollback erfolgreich. System läuft wieder mit alter Konfiguration."
notify "warn" "Rollback erfolgreich. Bitte prüfen. Alte IP bleibt ${last_ip:-unbekannt}."
exit 10
else
die "Rollback fehlgeschlagen. Manuelles Eingreifen erforderlich."
fi
}
main "$@"