Compare commits
7 Commits
33482f373e
...
yamlfix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04b741b28b | ||
|
|
189bd41ea1 | ||
|
|
45f496590d | ||
|
|
61ebf046b8 | ||
|
|
7cbd5cd5be | ||
|
|
2daaadc3b3 | ||
|
|
1473eed0e0 |
@@ -1,8 +1,4 @@
|
|||||||
# template_repository
|
Dokumentation wird bei Gelegenheit nachgereicht.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Wichtig: Link für Lizenz anpassen.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
316
livekit-ip-watch.v1.sh
Normal file
316
livekit-ip-watch.v1.sh
Normal 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 "$@"
|
||||||
Reference in New Issue
Block a user