Initial commit: Clownfischserver v0.5.0

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 14:32:33 +02:00
co-authored by Claude Haiku 4.5
commit 91eeb4834f
22 changed files with 4109 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
#!/bin/bash
# =============================================================================
# 🐠 CLOWNFISCHSERVER – HomeIP Auto-Update
# Purpose: Auto-update firewall rule for dynamic home IP (myfritz.net)
# Runs: Every 5 minutes (via cron)
# Author: Mehlzoerwer-Claude
# License: GPL-3.0
# =============================================================================
set -e
# --- Configuration ---
INSTALL_DIR="/opt/clownfischserver"
ENV_FILE="$INSTALL_DIR/config/.env"
STATE_FILE="/var/lib/clownfischserver-homeip-state"
LOG_FILE="/var/log/clownfischserver-homeip-updater.log"
SSH_PORT=22
# --- Helpers ---
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}
# --- Load .env ---
load_env() {
if [ ! -f "$ENV_FILE" ]; then
log "ERROR: .env not found at $ENV_FILE"
exit 1
fi
# Load and trim CRLF/LF
MYFRITZ_DOMAIN=$(grep "^CLOWNFISCHSERVER_HOME_IP_DOMAIN=" "$ENV_FILE" | cut -d= -f2 | tr -d '\r\n')
if [ -z "$MYFRITZ_DOMAIN" ]; then
log "ERROR: CLOWNFISCHSERVER_HOME_IP_DOMAIN not set in .env"
exit 1
fi
}
# --- Detect Firewall ---
detect_firewall() {
if systemctl is-active --quiet ufw; then
echo "ufw"
elif systemctl is-active --quiet firewalld; then
echo "firewalld"
elif command -v iptables &>/dev/null; then
echo "iptables"
else
log "ERROR: No firewall detected (ufw/firewalld/iptables)"
exit 1
fi
}
# --- Resolve DNS with fallbacks ---
resolve_ip() {
local domain="$1"
# Try dig first
if command -v dig &>/dev/null; then
IP=$(dig +short "$domain" A | grep -oE '^[0-9.]+$' | head -1)
[ -n "$IP" ] && echo "$IP" && return 0
fi
# Try nslookup
if command -v nslookup &>/dev/null; then
IP=$(nslookup "$domain" 2>/dev/null | grep "Address:" | tail -1 | awk '{print $2}' | grep -oE '^[0-9.]+$')
[ -n "$IP" ] && echo "$IP" && return 0
fi
# Try getent
if command -v getent &>/dev/null; then
IP=$(getent hosts "$domain" | awk '{print $1}' | grep -oE '^[0-9.]+$' | head -1)
[ -n "$IP" ] && echo "$IP" && return 0
fi
log "ERROR: Could not resolve $domain"
return 1
}
# --- Load previous state ---
load_state() {
if [ -f "$STATE_FILE" ]; then
PREVIOUS_IP=$(cat "$STATE_FILE" | grep "^IP=" | cut -d= -f2)
echo "$PREVIOUS_IP"
else
echo ""
fi
}
# --- Save state ---
save_state() {
local ip="$1"
echo "IP=$ip" > "$STATE_FILE"
chmod 600 "$STATE_FILE"
}
# --- UFW Functions ---
ufw_add_rule() {
local ip="$1"
ufw allow from "$ip" to any port "$SSH_PORT" comment "clownfisch-homeip" >/dev/null 2>&1
log "UFW: Added rule for $ip:$SSH_PORT"
}
ufw_remove_rule() {
local ip="$1"
echo "y" | ufw delete allow from "$ip" to any port "$SSH_PORT" >/dev/null 2>&1
log "UFW: Deleted rule for $ip"
}
# --- Firewalld Functions ---
firewalld_add_rule() {
local ip="$1"
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='$ip' port protocol='tcp' port='$SSH_PORT' accept" >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
log "firewalld: Added rule for $ip:$SSH_PORT"
}
firewalld_remove_rule() {
local ip="$1"
firewall-cmd --permanent --remove-rich-rule="rule family='ipv4' source address='$ip' port protocol='tcp' port='$SSH_PORT' accept" >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
log "firewalld: Deleted rule for $ip"
}
# --- iptables Functions ---
iptables_add_rule() {
local ip="$1"
iptables -I INPUT -s "$ip" -p tcp --dport "$SSH_PORT" -j ACCEPT
log "iptables: Added rule for $ip:$SSH_PORT"
}
iptables_remove_rule() {
local ip="$1"
iptables -D INPUT -s "$ip" -p tcp --dport "$SSH_PORT" -j ACCEPT 2>/dev/null || true
log "iptables: Deleted rule for $ip"
}
# --- Main ---
main() {
log "=== HomeIP Auto-Update started ==="
load_env
FIREWALL=$(detect_firewall)
NEW_IP=$(resolve_ip "$MYFRITZ_DOMAIN") || exit 1
PREVIOUS_IP=$(load_state)
if [ "$NEW_IP" = "$PREVIOUS_IP" ]; then
log "No change: $NEW_IP (same as previous)"
exit 0
fi
log "IP change detected: $PREVIOUS_IP → $NEW_IP (firewall: $FIREWALL)"
# Remove old rule
if [ -n "$PREVIOUS_IP" ]; then
case "$FIREWALL" in
ufw) ufw_remove_rule "$PREVIOUS_IP" ;;
firewalld) firewalld_remove_rule "$PREVIOUS_IP" ;;
iptables) iptables_remove_rule "$PREVIOUS_IP" ;;
esac
fi
# Add new rule
case "$FIREWALL" in
ufw) ufw_add_rule "$NEW_IP" ;;
firewalld) firewalld_add_rule "$NEW_IP" ;;
iptables) iptables_add_rule "$NEW_IP" ;;
esac
# Save state
save_state "$NEW_IP"
log "=== HomeIP Auto-Update completed successfully ==="
}
main "$@"