66 Commits

Author SHA1 Message Date
patrick 2fa79afd66 miyagi-backup.sh aktualisiert 2026-02-04 17:09:40 +01:00
patrick 15e86400f0 miyagi-backup.sh aktualisiert 2026-02-04 00:51:31 +01:00
patrick 1166a9c03e config.example aktualisiert 2026-01-07 11:46:23 +01:00
patrick c519aac1d9 config.example aktualisiert 2026-01-07 11:44:55 +01:00
patrick effc9646be miyagi-backup.sh aktualisiert 2026-01-07 11:44:25 +01:00
patrick e2960c19b4 miyagi-backup.sh aktualisiert
add function run_pbs_log_clear, last 60 days
2025-12-11 01:19:43 +01:00
patrick 7d42984bfa miyagi-backup.sh aktualisiert
PBS PORT
2025-11-26 13:26:37 +01:00
patrick f0890c61ff config.example aktualisiert 2025-11-26 13:09:03 +01:00
patrick eab882aa44 config.example aktualisiert 2025-11-26 13:08:47 +01:00
patrick 87eb3307cb miyagi-backup.sh aktualisiert 2025-08-22 23:01:50 +02:00
patrick 067fe8f1af v2.05
fix piggyback_external in main
2025-08-22 23:00:55 +02:00
patrick e2f64a4491 config.example aktualisiert 2025-08-22 16:33:16 +02:00
patrick ca8c2854a9 v2.04 2025-08-22 00:05:43 +02:00
patrick 3fbb50e2bb config.example aktualisiert 2025-08-19 01:12:13 +02:00
patrick f7b9acd73b README.md aktualisiert 2025-08-11 12:03:51 +02:00
patrick 76a081468d README.md aktualisiert 2025-08-06 01:24:11 +02:00
patrick 75534137ff miyagi-backup.sh aktualisiert 2025-08-04 00:13:17 +02:00
patrick beb53968a4 miyagi-backup.sh aktualisiert 2025-08-02 01:09:12 +02:00
patrick dcae51da9f v2.03
wait replace
2025-08-02 00:50:14 +02:00
patrick 87c2d13069 v2.02
shutdown stop by zpool replace
2025-08-02 00:36:40 +02:00
patrick 602cfe21b0 Dateien nach "/" hochladen 2025-07-30 01:31:44 +02:00
patrick 7d3e53b42a v2.01
Press ENTER to continue.
2025-07-26 02:01:58 +02:00
patrick a15a7bad54 2.0 2025-07-24 12:52:46 +02:00
patrick 849b5e9570 miyagi-backup.sh aktualisiert 2025-07-17 11:11:11 +02:00
patrick 1221a6cf23 config.example aktualisiert
REPLEXCLUDE=$BACKUPEXCLUDE
2025-07-15 15:13:32 +02:00
patrick 0db8fee019 config.example aktualisiert
PBSPORT
2025-07-15 01:49:56 +02:00
patrick 564939053a miyagi-check.sh aktualisiert 2025-07-15 01:49:11 +02:00
patrick 127431689a miyagi-convert.sh aktualisiert
fix BACKUPEXCLUDE
2025-07-15 00:01:56 +02:00
patrick f999ff6a87 miyagi-check.sh aktualisiert 2025-07-14 18:19:37 +02:00
patrick 8e121cb75c miyagi-convert.sh aktualisiert 2025-07-14 17:49:44 +02:00
patrick 2329aa7377 miyag-convert.sh hinzugefügt 2025-07-08 23:56:52 +02:00
patrick 7eb712fae4 miyagi-backup.sh aktualisiert 2025-07-02 15:55:49 +02:00
patrick 93780dad1b miyagi-check.sh aktualisiert 2025-07-02 15:43:47 +02:00
patrick 802b7b8c63 config.example aktualisiert 2025-07-02 15:07:17 +02:00
patrick e6842f1de8 miyagi-backup.sh aktualisiert 2025-06-30 14:55:07 +02:00
patrick 25220beae2 miyagi-backup.sh aktualisiert
ssh -p "$SSHPORT""$SOURCEHOST" hostname)
2025-06-30 13:37:35 +02:00
patrick 2954647d87 miyagi-backup.sh aktualisiert
extra monitoring hosts
2025-06-29 20:52:26 +02:00
patrick 0efa02e0c2 config.example aktualisiert
extra monitoring hosts
2025-06-29 20:50:29 +02:00
patrick cdf6d522cb config.example aktualisiert 2025-06-29 14:19:49 +02:00
patrick b0fdaedfaf config.example aktualisiert 2025-06-29 14:19:17 +02:00
patrick db358d77a3 miyagi-backup.sh aktualisiert
zpushfilter fix
2025-06-29 13:52:41 +02:00
patrick 65ff6cf9e2 miyagi-backup.sh aktualisiert
fix apt updates
2025-06-29 01:20:23 +02:00
patrick 0055ac78a4 miyagi-backup.sh aktualisiert
fix hostname with ssh and maintenance then backupserver yes
2025-06-29 00:50:45 +02:00
patrick fdec6e6d9d miyagi-check.sh aktualisiert
add check_pveversion
2025-06-19 23:52:34 +02:00
patrick 76ad3cfc82 miyagi-backup.sh aktualisiert
fix Backupserver in main menu
2025-06-19 23:41:09 +02:00
patrick 59d2e72e62 miyagi-check.sh aktualisiert 2025-06-19 18:15:14 +02:00
patrick e62f6f9c2d miyagi-check.sh aktualisiert 2025-06-19 18:13:50 +02:00
patrick 97eced5d12 miyagi-check.sh aktualisiert 2025-06-19 18:06:21 +02:00
patrick 01d78f433a README aktualisiert 2025-06-19 17:55:30 +02:00
patrick 133312ac4b miyagi-check.sh hinzugefügt
new
2025-06-19 17:28:24 +02:00
patrick 56bee24fbb README aktualisiert
add miyagi-check
2025-06-19 17:25:08 +02:00
patrick b6b995f670 config.example aktualisiert
fix config
2025-06-19 11:34:19 +02:00
patrick 8acfffe8d2 config.example aktualisiert 2025-06-19 10:28:50 +02:00
patrick c707cbd8df miyagi-backup.sh aktualisiert
shutdown fix and sleep 10 seconds for pbs store
2025-06-18 01:06:41 +02:00
patrick 4e023cd4ef miyagi-backup.sh aktualisiert
fix piggyback data send
2025-06-12 23:17:01 +02:00
patrick dbcb105ee8 miyagi-backup.sh aktualisiert
fix piggyback_data
2025-06-12 23:14:54 +02:00
patrick 3714b62b2c miyagi-backup.sh aktualisiert 2025-06-12 11:56:57 +02:00
patrick 2994dbffcf miyagi-backup.sh aktualisiert
sleep timer 60
2025-06-12 11:50:48 +02:00
patrick cfbc115bc9 config.example aktualisiert 2025-06-11 23:03:00 +02:00
patrick 9fa0cfbf71 miyagi-backup.sh aktualisiert
add WOL on Interfaces
2025-06-11 00:12:06 +02:00
patrick 3f9b7c5e64 miyagi-backup.sh aktualisiert
fallback without metadata
2025-06-06 00:49:18 +02:00
patrick 15b3b5b684 miyagi-backup.sh aktualisiert
scrub fix
2025-06-03 23:57:47 +02:00
patrick a318cea773 Dateien nach "/" hochladen
fix pbs_storage
2025-06-03 23:28:12 +02:00
patrick b0e336279b miyagi-backup.sh aktualisiert
add scrub stop src und scrub stop local
2025-05-31 19:33:31 +02:00
patrick f775f7ed4b config.example hinzugefügt 2025-05-31 10:55:00 +02:00
patrick 22fc8bc6fc miyagi-backup.sh aktualisiert
fix scrub
2025-05-31 10:54:07 +02:00
7 changed files with 1319 additions and 117 deletions
-9
View File
@@ -1,9 +0,0 @@
🧪 Beispielhafte Aufrufe
Befehl Wirkung
./miyagi_backup.sh -c /etc/miyagi.conf Führt den vollständigen Backup-Prozess aus
./miyagi_backup.sh help Listet alle verfügbaren Funktionen
./miyagi_backup.sh run_updates Führt nur Updates lokal aus
./miyagi_backup.sh run_pbs_backup Führt nur das vzdump-PBS-Backup aus
./miyagi_backup.sh run_maintenance Nur Wartung (Prune + GC)
./miyagi_backup.sh shutdown_if_requested Prüft ob Shutdown nötig ist
+29
View File
@@ -0,0 +1,29 @@
# Beispielhafte Aufrufe
Befehl Wirkung
```
./miyagi-backup.sh -c /etc/miyagi.conf Führt den vollständigen Backup-Prozess aus
./miyagi-backup.sh help Listet alle verfügbaren Funktionen
./miyagi-backup.sh run_updates Führt nur Updates lokal aus
./miyagi-backup.sh run_pbs_backup Führt nur das vzdump-PBS-Backup aus
./miyagi-backup.sh run_maintenance Nur Wartung (Prune + GC)
./miyagi-backup.sh shutdown_if_requested Prüft ob Shutdown nötig ist
```
####
# miyagi-check.sh
Dieses Skript überprüft eine Bash-basierte Konfigurationsdatei für ein Backup-System:
## Funktionen:
- Validiert alle benötigten Variablen
- Testet SSH-Verbindungen zu `SOURCEHOST` und optional `PBSHOST`
- Prüft und installiert bei Bedarf den lokalen SSH-Key (`~/.ssh/id_rsa.pub`)
- Erkennt unsichere `PermitRootLogin yes` Einstellungen
- Bietet automatische Umstellung auf `prohibit-password` an
## Nutzung:
```bash
./miyagi-check.sh /pfad/zur/config
```
+46
View File
@@ -0,0 +1,46 @@
#Edit all Variables for best Experience
UPDATES='yes' # Proxmox VE und PBS Updates nach dem Lauf
SHUTDOWN='no' # System nach Ausführung herunterfahren?
DYNROUTE='no' #Dynroute setzen?
DDNS_GATEWAY='192.168.66.1' # Gateway
DDNS_SERVER='1.1.1.1' # Externer DNS Server
REBOOT='no' # Reboot nach Update?
# Quelle (Proxmox VE System, das gesichert wird)
SOURCEPORT='22' # SSH-Port, normalerweise 22
SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
# Replikation (ZFS)
ZFSROOT='rpool/data' # Erstes Dataset vom Quellsystem
ZFSSECOND='rpool-hdd/data' # Optional zweites Dataset
ZFSTRGT='rpool/repl/pve200' # Zielpfad für Replikation
# ZFS Zsync Replikation
ZSYNC='yes' # ZSYNC aktivieren (ja/nein)
ZPUSHTAG='bashclub:zsync' # Benutzer-Tag für ZFS
ZPUSHMINKEEP='3' # Mindestens zu behaltende Snapshots
ZPUSHKEEP='14' # Snapshots mit dem Tag, die behalten werden
ZPUSHLABEL='zsync' # Suffix für Snapshot-Autoengine
ZPUSHFILTER='hourly|daily|weekly|monthly' # Weitere Filter (leer lassen oder Muster wie daily| weekly etc.)
# Backup mit Proxmox Backup Server
BACKUPSERVER='no' # Backup via PBS aktivieren?
MAINTDAY='7' # Wartungstag (1=Mo, 7=So)
PBSHOST='192.168.50.199' # IP des Proxmox Backup Servers
PBSPORT='22'
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
BACKUPSTOREPBS='backup' # Datastore auf PBS
BACKUPEXCLUDE='9999,99988' # VM/CT-IDs, die vom Backup ausgeschlossen sind
REPLEXCLUDE=$BACKUPEXCLUDE # Diese auch von Replikation ausschließen
# Zusätzliche Monitoring-Ziele
# External Piggyback Host
EPIGGYBACK='no' # Piggyback-Daten an Monitoring-Ziel senden?
EPIGGYBACK_PORT='222' # SSH-Port für EPIGGYBACK_HOST
EPIGGYBACK_HOST='192.168.66.10' # Monitoring-Zielhost für Piggyback
# External Checkzfs Host
ECHECKZFS='no' # check_zfs-Output an Monitoring-Ziel senden?
ECHECKZFS_PORT='2222' # SSH-Port für ECHECKZFS_HOST
ECHECKZFS_HOST='192.168.50.210' # Monitoring-Zielhost für check_zfs
+660 -108
View File
@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -uo pipefail
IFS=$'\n\t' IFS=$'\n\t'
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
@@ -7,158 +7,710 @@ SCRIPT_NAME=$(basename "$0")
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log" LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
log() { log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
} }
usage() { usage() {
echo "Usage:" echo "Usage:"
echo " $0 -c /path/to/config # Full backup run" echo " $0 -c /path/to/config # Full backup run"
echo " $0 [function] # Run individual function" echo " $0 [function] # Run individual function"
echo " $0 help # Show available functions" echo " $0 help # Show available functions"
exit 1 exit 1
} }
# Konfigurationsdatei initialisieren (nur wenn -c übergeben)
CONFIG_FILE="" CONFIG_FILE=""
while getopts "c:" opt; do while getopts "c:" opt; do
case "$opt" in case "$opt" in
c) CONFIG_FILE="$OPTARG" ;; c) CONFIG_FILE="$OPTARG" ;;
*) usage ;; *) usage ;;
esac esac
done done
shift $((OPTIND - 1))
# Bei Einzelaufruf muss Konfiguration geladen sein
if [[ -n "${CONFIG_FILE:-}" ]]; then if [[ -n "${CONFIG_FILE:-}" ]]; then
if [[ ! -f "$CONFIG_FILE" ]]; then if [[ ! -f "$CONFIG_FILE" ]]; then
log "ERROR: Configuration file not found: $CONFIG_FILE" log "ERROR: Configuration file not found: $CONFIG_FILE"
exit 1 exit 1
fi fi
source "$CONFIG_FILE"
if ! bash -n "$CONFIG_FILE"; then
log "Syntax error in configuration file $CONFIG_FILE"
exit 1
fi
source "$CONFIG_FILE"
REQUIRED_VARS=(
SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
SOURCEHOST ZFSROOT ZFSSECOND ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE DYNROUTE REBOOT DDNS_GATEWAY
)
if [[ "${DYNROUTE,,}" == "yes" ]]; then
REQUIRED_VARS+=(DDNS_GATEWAY)
fi fi
# Funktionen MISSING_VARS=()
for var in "${REQUIRED_VARS[@]}"; do
if [[ -z "${!var:-}" ]]; then
MISSING_VARS+=("$var")
fi
done
if [[ "${#MISSING_VARS[@]}" -gt 0 ]]; then
log "Missing configuration variables:"
for var in "${MISSING_VARS[@]}"; do
log " - $var"
done
log "Aborting — please check your configuration file."
exit 1
else
log "All required configuration variables are set."
fi
fi
rssh() { ssh -p "$SOURCEPORT" "$@"; }
rscp() { scp -P "$SOURCEPORT" "$@"; }
get_sourcehostname() {
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
log "SOURCEHOSTNAME is empty, retrieving via SSH from $SOURCEHOST..."
SOURCEHOSTNAME=$(rssh "$SOURCEHOST" hostname)
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
fi
}
zfs_replace() {
local status
status=$(zpool status)
if echo "$status" | grep -q 'scan: resilver in progress'; then
return 0
fi
if echo "$status" | grep -qE 'replacing-[0-9]'; then
return 0
fi
return 1
}
wait_replace() {
local interval_seconds=300 # Alle 5 Minuten prüfen
local waited_minutes=0
log "ZFS Replace-Vorgang erkannt warte unbegrenzt auf Abschluss..."
while true; do
if ! zfs_replace; then
log "Replace abgeschlossen nach $waited_minutes Minuten fahre jetzt herunter."
return 0
fi
log "Replace läuft noch erneut prüfen in $((interval_seconds/60)) Minuten..."
sleep "$interval_seconds"
((waited_minutes+=interval_seconds/60))
done
}
set_wol_g_enabled() {
log "Checking if ethtool is installed..."
if ! command -v ethtool >/dev/null 2>&1; then
log "ethtool is not installed, attempting installation..."
apt update && apt install -y ethtool || {
log "Error: ethtool could not be installed."
return 1
}
else
log "ethtool is already installed."
fi
log "Setting Wake-on-LAN (WOL) to 'g' on interfaces with static IP..."
for iface in $(ls /sys/class/net | grep -vE '^(lo|tap|vmbr|veth|br|docker|bond|wl)'); do
if [[ -e "/sys/class/net/$iface/device" ]]; then
log "Processing physical interface: $iface"
current_wol=$(ethtool "$iface" 2>/dev/null | awk '/Wake-on:/ {print $2}')
if [[ "$current_wol" != "g" ]]; then
log "Setting WOL to 'g' for $iface..."
ethtool -s "$iface" wol g || log "Error setting WOL on $iface"
else
log "WOL already set to 'g' for $iface"
fi
if grep -qE "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces; then
if ! grep -A 5 -E "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces | grep -q "post-up ethtool -s $iface wol g"; then
log "Adding WOL command in static block for $iface..."
sed -i "/^\s*iface\s\+$iface\s\+inet\s\+static/a \ post-up ethtool -s $iface wol g" /etc/network/interfaces
else
log "WOL command already present in static block for $iface."
fi
else
log "No static entry found for $iface, no changes made."
fi
fi
done
}
write_zsync_config() { write_zsync_config() {
local conf_file="/etc/bashclub/$SOURCEHOST.conf" get_sourcehostname
log "Writing zsync config to $conf_file" local conf_file="/etc/bashclub/$SOURCEHOST.conf"
{ log "Writing zsync config to $conf_file"
echo "target=$ZFSTRGT" {
echo "source=root@$SOURCEHOST" echo "target=$ZFSTRGT"
echo "sshport=$SSHPORT" echo "source=root@$SOURCEHOST"
echo "tag=$ZPUSHTAG" echo "sshport=$SOURCEPORT"
echo "snapshot_filter=$ZPUSHFILTER" echo "tag=$ZPUSHTAG"
echo "min_keep=$ZPUSHMINKEEP" echo "snapshot_filter=\"$ZPUSHFILTER\""
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP" echo "min_keep=$ZPUSHMINKEEP"
echo "zfs_auto_snapshot_label=$ZPUSHLABEL" echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
echo "zfs_auto_snapshot_engine=internal" echo "zfs_auto_snapshot_label=$ZPUSHLABEL"
echo "checkzfs_disabled=0" echo "zfs_auto_snapshot_engine=internal"
echo "checkzfs_local=0" echo "checkzfs_disabled=0"
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG" echo "checkzfs_local=0"
echo "checkzfs_max_age=1500,2000" echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
echo "checkzfs_max_snapshot_count=180,200" echo "checkzfs_max_age=1500,2000"
echo "checkzfs_spool=1" echo "checkzfs_max_snapshot_count=180,200"
echo "checkzfs_spool_maxage=90000" echo "checkzfs_spool=1"
} > "$conf_file" echo "checkzfs_spool_maxage=90000"
} > "$conf_file"
} }
run_zsync() { run_zsync() {
if [[ "$ZSYNC" != "no" ]]; then write_zsync_config
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf" if [[ "$ZSYNC" != "no" ]]; then
else /usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
log "Zsync is disabled" else
fi log "Zsync is disabled."
} fi
run_updates() {
log "Running updates..."
apt update && apt dist-upgrade -y
apt autopurge -y
} }
run_remote_updates() { run_remote_updates() {
if [[ "$UPDATES" == "yes" ]]; then if [[ "${UPDATES,,}" == "yes" ]]; then
ssh "$PBSHOST" apt update && apt dist-upgrade -y log "Running updates on local system..."
else
log "Remote updates disabled" TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
SNAPSHOT_TAG="pve-update-via-miyagi"
MAX_SNAPSHOTS=5
ZFS_DATASETS=("rpool/ROOT" "rpool/pveconf")
REBOOT_FLAG="${REBOOT^^}"
LOGFILE="/var/log/proxmox_update.log"
log_msg() {
local level="$1"
local message="$2"
local timestamp
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp] $message"
echo "[$timestamp] [$level] $message" >> "$LOGFILE"
}
# Prüfen, ob Updates verfügbar sind
if ! apt update | tee -a "$LOGFILE" | grep -qi "upgradable"; then
log_msg "INFO" "Keine Updates verfügbar."
return
fi fi
log_msg "INFO" "Updates verfügbar. Erstelle Snapshots."
for dataset in "${ZFS_DATASETS[@]}"; do
snapshot="${dataset}@${SNAPSHOT_TAG}-${TIMESTAMP}"
log_msg "INFO" "Erstelle Snapshot: $snapshot"
zfs snapshot "$snapshot"
# Alte Snapshots bereinigen
log_msg "INFO" "Bereinige alte Snapshots in $dataset"
old_snaps=$(zfs list -t snapshot -o name -s creation | grep "^${dataset}@${SNAPSHOT_TAG}-")
snap_count=$(echo "$old_snaps" | wc -l)
if (( snap_count > MAX_SNAPSHOTS )); then
snaps_to_delete=$(echo "$old_snaps" | head -n $((snap_count - MAX_SNAPSHOTS)))
while IFS= read -r snap; do
log_msg "INFO" "Lösche alten Snapshot: $snap"
zfs destroy "$snap"
done <<< "$snaps_to_delete"
fi
done
# System-Upgrade
log_msg "INFO" "Starte dist-upgrade"
if ! apt dist-upgrade -y | tee -a "$LOGFILE"; then
log_msg "ERROR" "Fehler während dist-upgrade"
return 1
fi
log_msg "INFO" "Starte autoremove"
apt autoremove -y | tee -a "$LOGFILE"
# Kernel-Update prüfen
if apt list --upgradable 2>/dev/null | grep -q "linux-image-"; then
log_msg "WARN" "Kernel-Update erkannt. Neustart empfohlen."
if [[ "$REBOOT_FLAG" == "YES" ]]; then
log_msg "INFO" "REBOOT=YES erkannt. System wird neugestartet."
reboot
fi
else
log_msg "INFO" "Update abgeschlossen. Kein Neustart erforderlich."
fi
# PBS-Host-Update
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
log "Running updates on PBS host ($PBSHOST)..."
ssh -p $PBSPORT root@"$PBSHOST" apt update && ssh -p $PBSPORT root@"$PBSHOST" apt dist-upgrade -y || {
log "Error during updates on $PBSHOST"
}
else
log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
fi
else
log "Updates disabled (UPDATES=$UPDATES)"
fi
} }
send_piggyback_data() { send_piggyback() {
local fname="90000_miyagi-$SOURCEHOSTNAME-$(hostname)" get_sourcehostname
log "Sending piggyback data to $SOURCEHOST..." local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
echo "<<<<miyagi-$SOURCEHOSTNAME-$(hostname)>>>" > "$fname" local filename="90000_${combined_host}"
/usr/bin/check_mk_agent >> "$fname"
echo "<<<<>>>>" >> "$fname" log "Reminder: Add a host named ${combined_host} in CMK (without Agent, Piggyback enabled)!"
scp "$fname" "$SOURCEHOST:/var/lib/check_mk_agent/spool/" log "Creating piggyback file: $filename"
{
echo "<<<<${combined_host}>>>>"
/usr/bin/check_mk_agent
echo "<<<<>>>>"
} > "$filename"
if rscp "$filename" "$SOURCEHOST:/var/lib/check_mk_agent/spool/"; then
log "Piggyback data successfully sent to $SOURCEHOST"
else
log "ERROR: Failed to send piggyback data to $SOURCEHOST"
fi
rm -f "$filename"
}
run_pbs_log_clear() {
log "Running clear LOG on PBS host ($PBSHOST)..."
if ! ssh -p "$PBSPORT" root@"$PBSHOST" \
find /var/log/proxmox-backup/tasks/ -type f -mtime +60 -delete; then
log "Error clearing PBS LOG on $PBSHOST"
fi
} }
run_pbs_backup() { run_pbs_backup() {
log "Running PBS vzdump job..." if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
ssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \ log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)"
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \ return 0
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \ fi
--notes-template '{{guestname}}'
if [[ $? -eq 0 ]]; then get_sourcehostname
echo "0 DailyPBS - Daily Backup" > /tmp/cmk_tmp.out log "Starte PBS Backup auf Host: $SOURCEHOST"
# PBS-Storage prüfen und ggf. aktivieren
log "Prüfe, ob PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST aktiviert ist..."
if rssh root@"$SOURCEHOST" "pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then
log "PBS-Storage '$BACKUPSTORE' ist deaktiviert aktiviere temporär und warte 10 Sekunden..."
rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"
pbs_enabled_by_script=true
else
log "PBS-Storage '$BACKUPSTORE' ist bereits aktiviert."
pbs_enabled_by_script=false
fi
vzdump_success=false
# Versuch: vzdump mit Change Detection
## Eintrag muss in /etc/vzdump.conf entries-max 100000000
log "Starte vzdump mit Change Detection..."
if rssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
--notes-template '{{guestname}}'; then
log "vzdump (Change Detection) erfolgreich."
vzdump_success=true
else
log "vzdump (Change Detection) fehlgeschlagen versuche Fallback ohne Change Detection..."
if rssh root@"$SOURCEHOST" vzdump \
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
--notes-template '{{guestname}}'; then
log "Fallback-vzdump erfolgreich."
vzdump_success=true
else else
echo "2 DailyPBS - Daily Backup FAILED" > /tmp/cmk_tmp.out log "FEHLER: vzdump fehlgeschlagen auch im Fallback."
fi fi
fi
( echo "<<<local>>>" ; cat /tmp/cmk_tmp.out ) > /tmp/90000_checkpbs # PBS-Storage ggf. wieder deaktivieren
scp /tmp/90000_checkpbs root@"$SOURCEHOST":/var/lib/check_mk_agent/spool if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
log "Deaktiviere temporär aktiviertes PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST..."
rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1"
fi
# Monitoring-Ausgabe
if [[ "$vzdump_success" == true ]]; then
echo "0 DailyPBS - Daily Backup erfolgreich" > /tmp/cmk_tmp.out
else
echo "2 DailyPBS - Daily Backup FEHLGESCHLAGEN" > /tmp/cmk_tmp.out
fi
{
echo "<<<local>>>"
cat /tmp/cmk_tmp.out
} > /tmp/90000_checkpbs
rscp /tmp/90000_checkpbs root@"$SOURCEHOST":/var/lib/check_mk_agent/spool \
|| log "Fehler beim Übertragen des Monitoring-Outputs via SCP"
rm -f /tmp/cmk_tmp.out /tmp/90000_checkpbs
#write_pbs_status
} }
run_maintenance() { run_maintenance() {
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
log "Running maintenance..." log "PBS Backup wird übersprungen (BACKUPSERVER=$BACKUPSERVER)"
PRUNEJOB=$(ssh "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4) return
ssh root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB" fi
ssh root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
ssh root@"$PBSHOST" proxmox-backup-manager verify backup if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
log "Running maintenance..."
PRUNEJOB=$(ssh -p $PBSPORT "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB"
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager verify backup
else
log "No maintenance scheduled for today."
fi
}
run_scrub_stop() {
local mode="$1" # "local" oder "remote"
local ssh_cmd=()
if [[ "$mode" == "remote" ]]; then
ssh_cmd=(ssh -p "$SOURCEPORT" root@"$SOURCEHOST")
fi
"${ssh_cmd[@]}" bash -c '
for pool in $(zpool list -H -o name); do
echo "Stopping scrub on pool: $pool"
if zpool status "$pool" | grep -q "scrub in progress"; then
if zpool scrub -s "$pool"; then
echo "Scrub stopped on $pool"
else
echo "Error stopping scrub on $pool"
fi
else
echo "No active scrub on $pool"
fi
done
'
}
write_pbs_status() {
local spool_file="/var/lib/check_mk_agent/spool/90000_checkpbs_local"
local repo="$BACKUPSTOREPBS"
local tmpfile
tmpfile=$(mktemp)
if ! command -v proxmox-backup-client >/dev/null 2>&1; then
log "proxmox-backup-client nicht installiert PBS-Status-Ausgabe übersprungen."
return
fi
echo "<<<local>>>" > "$tmpfile"
local now
now=$(date +%s)
# === Schwellenwert(e) parsen ===
local warn_threshold=0
local crit_threshold=86400 # Default kritisch ab 24h
if [[ -n "${PBSBACKUP_STATUS:-}" ]]; then
IFS=',' read -r t1 t2 <<< "$PBSBACKUP_STATUS"
if [[ "$t1" =~ ^[0-9]+$ && -z "$t2" ]]; then
# Nur ein Wert vorhanden
crit_threshold=$t1
log "Verwende einfachen Schwellenwert: CRIT=${crit_threshold}s"
elif [[ "$t1" =~ ^[0-9]+$ && "$t2" =~ ^[0-9]+$ ]]; then
warn_threshold=$t1
crit_threshold=$t2
log "Verwende gestaffelte Schwellenwerte: WARN=${warn_threshold}s, CRIT=${crit_threshold}s"
else else
log "No maintenance scheduled for today." log "WARNUNG: Ungültiges PBSBACKUP_STATUS-Format Fallback: CRIT=${crit_threshold}s"
fi
fi
# Backup-Daten extrahieren
rssh root@"$SOURCEHOST" proxmox-backup-client list --repository "$repo" --output-format json 2>/dev/null | \
grep -E '"backup-id"|"backup-type"|"backup-time"' | \
sed -E 's/[",]//g; s/^ *//' > "$tmpfile.json"
local backup_type="" backup_id="" backup_time=""
while read -r line; do
case "$line" in
backup-type:*)
backup_type="${line#*: }"
;;
backup-id:*)
backup_id="${line#*: }"
;;
backup-time:*)
backup_time="${line#*: }"
if [[ -n "$backup_type" && -n "$backup_id" && -n "$backup_time" ]]; then
local age status timestamp msg
age=$((now - backup_time))
timestamp=$(date -d "@$backup_time" "+%Y-%m-%d %H:%M:%S")
if (( age < warn_threshold )); then
status=0
elif (( age < crit_threshold )); then
status=1
else
status=2
fi
msg="$backup_type/$backup_id last backup $timestamp (age: $((age / 3600))h)"
echo "$status PBS_${backup_type}_${backup_id} - $msg" >> "$tmpfile"
# Reset
backup_type="" backup_id="" backup_time=""
fi
;;
esac
done < "$tmpfile.json"
rm -f "$tmpfile.json"
mv "$tmpfile" "$spool_file"
log "PBS Backup Status lokal geschrieben: $spool_file"
}
shutdown_now() {
if [[ "${SHUTDOWN,,}" == "yes" ]]; then
if zfs_replace; then
log "ZFS Replace-Vorgang erkannt warte bis zum Abschluss..."
if ! wait_replace; then
log "Shutdown abgebrochen Replace ist nach max. Wartezeit noch nicht abgeschlossen."
return
fi
fi
send_piggyback
send_piggyback_external
send_checkzfs_external
log "Shutting down now...in 60sec"
sleep 60
shutdown now
else
log "No shutdown requested."
fi
}
send_piggyback_external() {
if [[ "${EPIGGYBACK,,}" != "yes" ]]; then
log "Externer Piggyback-Export deaktiviert."
return
fi
write_zsync_config
get_sourcehostname
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local spool_file="90000_${combined_host}_external"
local temp_dir
temp_dir=$(mktemp -d)
log "Erzeuge temporäre Piggyback-Datei: $temp_dir/$spool_file"
{
echo "<<<<${combined_host}>>>>"
/usr/bin/check_mk_agent
echo "<<<<>>>>"
} > "$temp_dir/$spool_file"
if scp -P "$EPIGGYBACK_PORT" "$temp_dir/$spool_file" "$EPIGGYBACK_HOST:/var/lib/check_mk_agent/spool/"; then
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet: $spool_file"
else
log "Fehler beim Übertragen der Piggyback-Daten an $EPIGGYBACK_HOST"
rm -rf "$temp_dir"
return 1
fi
rm -rf "$temp_dir"
return 0
}
send_checkzfs_external() {
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
log "Externer Piggyback-Export deaktiviert."
return
fi
local config="/etc/bashclub/${SOURCEHOST}.conf"
if [[ ! -f "$config" ]]; then
log "Konfigurationsdatei fehlt: $config"
return 1
fi
source "$config"
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
return
fi
write_zsync_config
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
if [[ -z "$checkzfs_cmd" || ! -x "$checkzfs_cmd" ]]; then
log "checkzfs nicht gefunden oder nicht ausführbar Abbruch."
return 1
fi
log "Verwende checkzfs: $checkzfs_cmd"
# Filter anhand ZFS-Tags auf dem SOURCEHOST aufbauen
local filter=""
while IFS=$'\t' read -r name value source; do
if [[ "$source" == "local" && "$value" == "subvols" ]]; then
filter+="${name}/|"
elif [[ "$source" == "local" && "$value" == "all" ]]; then
filter+="${name}|"
fi
done < <(ssh -p "$SOURCEPORT" "$SOURCEHOST" "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
filter="#${filter%|}"
log "Generierter ZFS-Filter: $filter"
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
local spoolfile="/tmp/${combined}_checkzfs_external"
local spooldest="120000_${combined}_checkzfs_external"
log "Führe checkzfs aus..."
{
echo "<<<local>>>"
"$checkzfs_cmd" \
--source root@"${SOURCEHOST}:${SOURCEPORT}" \
--filter "$filter" \
--replicafilter "^${ZFSTRGT}" \
--prefix "$checkzfs_prefix" \
--threshold "$checkzfs_max_age" \
--maxsnapshots "$checkzfs_max_snapshot_count" \
--output checkmk
} > "$spoolfile"
if scp -P "$ECHECKZFS_PORT" "$spoolfile" root@"$ECHECKZFS_HOST":/var/lib/check_mk_agent/spool/"$spooldest"; then
log "Spool-Datei erfolgreich übertragen an $ECHECKZFS_HOST:$spooldest"
else
log "Fehler beim Übertragen der Spool-Datei an $ECHECKZFS_HOST"
fi
rm -f "$spoolfile"
}
wait() {
echo
echo "###########################################"
echo "# Das Backup startet in 60 Sekunden... #"
echo "# Drücke [Enter], um sofort fortzufahren, #"
echo "# oder warte, um automatisch zu starten. #"
echo "###########################################"
read -t 60 -p "Fortfahren (Enter drücken) oder warten... " input
if [[ $? -eq 0 ]]; then
log "Press ENTER to continue."
else
log "No press ENTER, wait for 60 Sec.."
sleep 60
fi fi
} }
set_dynamic_route() {
local ddns_hostname="$SOURCEHOST"
local gateway="$DDNS_GATEWAY"
local dns_server="$DDNS_SERVER"
run_scrub() { if [[ -z "$ddns_hostname" || -z "$gateway" ]]; then
ssh root@"$SOURCEHOST" zpool scrub -s "$ZPOOLSRC" log "Fehler: SOURCEHOST oder DDNS_GATEWAY nicht gesetzt"
zpool scrub -s "$ZPOOLDST" return 1
} fi
shutdown_if_requested() { log "Setze temporäre Route zu DNS-Server $dns_server via $gateway..."
if [[ "$SHUTDOWN" == "yes" ]]; then if ! ip route | grep -q "^$dns_server"; then
send_piggyback_data ip route add "$dns_server" via "$gateway" \
log "Shutting down now..." && log "Route zu $dns_server via $gateway gesetzt" \
shutdown now || log "Fehler beim Setzen der DNS-Route"
else else
log "No shutdown requested." log "Route zu $dns_server bereits vorhanden"
fi
log "Löse IP von $ddns_hostname über DNS $dns_server..."
CURRENT_IP=$(dig +short @"$dns_server" "$ddns_hostname")
if [[ -z "$CURRENT_IP" ]]; then
log "Fehler: Konnte IP für $ddns_hostname nicht auflösen"
return 1
fi
log "Ermittelte IP: $CURRENT_IP setze Route via $gateway..."
if ! ip route | grep -q "^$CURRENT_IP"; then
ip route add "$CURRENT_IP" via "$gateway" \
&& log "Route erfolgreich gesetzt: $CURRENT_IP via $gateway" \
|| log "Fehler beim Setzen der Route zu $CURRENT_IP"
else
log "Route zu $CURRENT_IP bereits vorhanden"
fi fi
} }
# Main execution:
main() { if [[ $# -eq 0 ]]; then
log "Starting full backup routine..." if [[ -n "$CONFIG_FILE" ]]; then
wait
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname) log "Running full backup using configuration file: $CONFIG_FILE"
if [[ "${DYNROUTE,,}" == "yes" ]]; then
log "DYNROUTE ist aktiviert setze dynamische Route für $SOURCEHOST..."
set_dynamic_route
else
log "DYNROUTE ist deaktiviert oder nicht gesetzt."
fi
write_zsync_config write_zsync_config
run_zsync run_zsync
run_updates
run_maintenance if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
run_scrub log "BACKUPSERVER ist aktiviert, führe Backup aus..."
run_pbs_backup run_pbs_backup
run_maintenance
run_pbs_log_clear
else
log "BACKUPSERVER ist nicht aktiviert (BACKUPSERVER=$BACKUPSERVER), überspringe Backup."
fi
run_remote_updates run_remote_updates
shutdown_if_requested if [[ "${EPIGGYBACK,,}" == "yes" ]]; then
} send_piggyback_external
fi
# Funktionsbasierter Aufruf if [[ "${ECHECKZFS,,}" == "yes" ]]; then
if [[ "${1:-}" == "help" ]]; then send_checkzfs_external
echo "Verfügbare Funktionen:" fi
declare -F | awk '{print " - " $3}' | grep -v "^ - _" shutdown_now
exit 0 else
elif [[ "${1:-}" =~ ^[a-zA-Z0-9_]+$ && "$(type -t "$1")" == "function" ]]; then usage
shift fi
"$@"
else else
main case "$1" in
help)
declare -F | awk '{print $3}' | grep -v "^_" | grep -v "^main$"
;;
*)
if declare -F "$1" >/dev/null 2>&1; then
"$@"
else
log "Function $1 not found."
usage
fi
;;
esac
fi fi
+220
View File
@@ -0,0 +1,220 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
PERMITROOT_YES_HOSTS=()
LOG() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
CONFIG_FILE="${1:-}"
if [[ -z "$CONFIG_FILE" ]]; then
LOG "Keine Konfigurationsdatei übergeben."
echo "Usage: $0 /pfad/zur/config"
exit 1
fi
if [[ ! -f "$CONFIG_FILE" ]]; then
LOG "Konfigurationsdatei nicht gefunden: $CONFIG_FILE"
exit 1
fi
if ! bash -n "$CONFIG_FILE"; then
LOG "Syntaxfehler in der Konfigurationsdatei!"
exit 1
fi
source "$CONFIG_FILE"
REQUIRED_VARS=(
SOURCEPORT
BACKUPSERVER
ZSYNC
MAINTDAY
SHUTDOWN
UPDATES
SOURCEHOST
PBSHOST
BACKUPSTORE
BACKUPSTOREPBS
BACKUPEXCLUDE
REPLEXCLUDE
)
MISSING=()
for var in "${REQUIRED_VARS[@]}"; do
if ! declare -p "$var" &>/dev/null || [[ -z "${!var}" ]]; then
MISSING+=("$var")
fi
done
if [[ ${#MISSING[@]} -gt 0 ]]; then
LOG " Fehlende Variablen in Konfiguration:"
for v in "${MISSING[@]}"; do echo " - $v"; done
exit 1
else
LOG " Alle Variablen gesetzt."
fi
if [[ ${#MISSING[@]} -gt 0 ]]; then
LOG " Fehlende Variablen in Konfiguration:"
for v in "${MISSING[@]}"; do echo " - $v"; done
exit 1
else
LOG " Alle Variablen gesetzt."
fi
# Prüfung, ob SHUTDOWN auch tatsächlich in der Datei definiert wurde
if ! grep -qE '^\s*SHUTDOWN\s*=' "$CONFIG_FILE"; then
LOG "Hinweis: Die Variable 'SHUTDOWN' ist zwar gesetzt, aber nicht direkt in der Konfigurationsdatei '$CONFIG_FILE' definiert."
LOG " → Bitte prüfen, ob dies gewollt ist oder von einer übergeordneten Quelle stammt."
fi
# Anzeigen, ob SHUTDOWN aktiv ist
LOG "SHUTDOWN-Status: ${SHUTDOWN:-nicht gesetzt}"
case "${SHUTDOWN,,}" in
yes)
LOG "Nach dem Backup wird das System heruntergefahren."
;;
no)
LOG "Kein automatischer Shutdown nach dem Backup."
;;
*)
LOG "Unbekannter SHUTDOWN-Wert: '${SHUTDOWN}' , erwartet: 'yes' oder 'no'"
;;
esac
check_ssh_connection() {
local host=$1
LOG "Prüfe SSH-Verbindung zu $host ..."
if ssh -p "$SOURCEPORT" -o BatchMode=yes -o ConnectTimeout=5 "$host" "echo OK" 2>/dev/null | grep -q OK; then
LOG " SSH-Verbindung zu $host erfolgreich."
return 0
else
LOG " SSH-Verbindung zu $host fehlgeschlagen!"
return 1
fi
}
check_and_copy_ssh_key() {
local host=$1
local keyfile="$HOME/.ssh/id_rsa.pub"
if [[ ! -f "$keyfile" ]]; then
LOG " Lokaler SSH-Public-Key ($keyfile) nicht gefunden!"
return 1
fi
local pubkey
pubkey=$(<"$keyfile")
LOG " Prüfe, ob SSH-Key auf $host autorisiert ist ..."
if ssh -p "$SOURCEPORT" "$host" "grep -qF '$pubkey' ~/.ssh/authorized_keys" 2>/dev/null; then
LOG " SSH-Key ist bereits auf $host hinterlegt."
else
LOG " SSH-Key nicht auf $host vorhanden."
read -rp " Möchtest du den SSH-Key jetzt via ssh-copy-id übertragen? [j/N] " ans
if [[ "$ans" =~ ^[JjYy]$ ]]; then
ssh-copy-id -p "$SOURCEPORT" "$host"
else
LOG " SSH-Key nicht übertragen."
fi
fi
}
check_sshd_config_recommendation() {
local host=$1
LOG " Prüfe sshd_config auf $host bzgl. 'PermitRootLogin'..."
local current_setting
current_setting=$(ssh -p "$SOURCEPORT" "$host" "grep -i '^PermitRootLogin' /etc/ssh/sshd_config" 2>/dev/null || echo "")
if [[ -z "$current_setting" ]]; then
LOG " Keine explizite 'PermitRootLogin'-Einstellung gefunden."
elif echo "$current_setting" | grep -qi "PermitRootLogin yes"; then
LOG " Aktuell ist PermitRootLogin=YES erlaubt auf $host"
LOG " Empfehlung: auf 'prohibit-password' umstellen."
PERMITROOT_YES_HOSTS+=("$host")
else
LOG " PermitRootLogin-Einstellung ist: $current_setting"
fi
}
check_pveversion() {
local host=$1
LOG "Prüfe PVE-Version auf $host ..."
if ssh -p "$SOURCEPORT" "$host" "command -v pveversion >/dev/null"; then
ssh -p "$SOURCEPORT" "$host" "pveversion" | while read -r line; do
LOG " $host: $line"
done
else
LOG " 'pveversion' ist auf $host nicht verfügbar kein Proxmox?"
fi
}
check_pbs_version() {
local host=$1
local port=$2
LOG "Prüfe PBS-Version auf $host ..."
if ssh -p "$port" "$host" "command -v proxmox-backup-manager >/dev/null"; then
ssh -p "$port" "$host" "proxmox-backup-manager version" | while read -r line; do
LOG " $host: $line"
done
else
LOG " 'proxmox-backup-manager' ist auf $host nicht verfügbar kein PBS?"
fi
}
run_host_check() {
local host=$1
local type=${2:-pve}
local port=$SOURCEPORT
local pbsport=$PBSPORT
LOG ""
LOG "=== Prüfung für Host: $host (Typ: $type) ==="
if check_ssh_connection "$host" "$port"; then
check_and_copy_ssh_key "$host"
check_sshd_config_recommendation "$host"
if [[ "$type" == "pve" ]]; then
check_pveversion "$host" "$port"
elif [[ "$type" == "pbs" ]]; then
check_pbs_version "$host" "$pbsport"
else
LOG " Unbekannter Host-Typ: $type"
fi
fi
echo ""
}
run_host_check "$SOURCEHOST" pve
if [[ "$BACKUPSERVER" == "yes" ]]; then
run_host_check "$PBSHOST" pbs
else
LOG " BACKUPSERVER ist deaktiviert PBSHOST wird übersprungen."
fi
# NACHLAUF: Frage zur automatischen Änderung von PermitRootLogin
if [[ ${#PERMITROOT_YES_HOSTS[@]} -gt 0 ]]; then
echo ""
echo " Die folgenden Hosts erlauben derzeit root-Login per Passwort:"
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
echo " - $h"
done
read -rp " Möchtest du PermitRootLogin auf diesen Hosts auf 'prohibit-password' setzen und sshd neustarten? [j/N] " change_ans
if [[ "$change_ans" =~ ^[JjYy]$ ]]; then
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
echo "Ändere sshd_config auf $h ..."
ssh -p "$SOURCEPORT" "$h" "sed -i 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && systemctl reload sshd && echo '✅ sshd auf $h neu geladen.' || echo '❌ Fehler bei $h'"
done
else
echo " Änderung von sshd_config übersprungen."
fi
fi
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
log() {
echo "[INFO] $*"
}
error_exit() {
echo "[ERROR] $*" >&2
exit 1
}
sanitize_value() {
echo "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
load_config() {
local config_file="$1"
if [[ ! -f "$config_file" ]]; then
error_exit "Konfigurationsdatei nicht gefunden: $config_file"
fi
log "Lade und bereinige Konfigurationsdatei: $config_file"
while IFS='=' read -r key value; do
# nur gültige Variablennamen parsen
if [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
# Kommentar nach Wert entfernen
value="${value%%#*}"
value="$(sanitize_value "$value")"
# Variable setzen (für den write_new_config Zugriff)
eval "$key=\"$value\""
fi
done < "$config_file"
}
write_new_config() {
local out_file="$1"
cat > "$out_file" <<EOF
#Edit all Variables for best Experience
UPDATES='${UPDATES}' # Proxmox VE und PBS Updates nach dem Lauf
SHUTDOWN='${SHUTDOWN}' # System nach Ausführung herunterfahren?
# Quelle (Proxmox VE System, das gesichert wird)
SOURCEPORT='${SOURCEPORT}' # SSH-Port, normalerweise 22
SOURCEHOST='${SOURCEHOST}' # IP des Quell-Proxmox-Servers
# Replikation (ZFS)
ZFSROOT='${ZFSROOT}' # Erstes Dataset vom Quellsystem
ZFSSECOND='${ZFSSECOND}' # Optional zweites Dataset
ZFSTRGT='${ZFSTRGT}' # Zielpfad für Replikation
# ZFS Zsync Replikation
ZSYNC='${ZSYNC}' # ZSYNC aktivieren (ja/nein)
ZPUSHTAG='${ZPUSHTAG}' # Benutzer-Tag für ZFS
ZPUSHMINKEEP='${ZPUSHMINKEEP}' # Mindestens zu behaltende Snapshots
ZPUSHKEEP='${ZPUSHKEEP}' # Snapshots mit dem Tag, die behalten werden
ZPUSHLABEL='${ZPUSHLABEL}' # Suffix für Snapshot-Autoengine
ZPUSHFILTER='${ZPUSHFILTER}' # Weitere Filter (leer lassen oder Muster wie daily| weekly etc.)
# Backup mit Proxmox Backup Server
BACKUPSERVER='${BACKUPSERVER}' # Backup via PBS aktivieren?
MAINTDAY='${MAINTDAY}' # Wartungstag (1=Mo, 7=So)
PBSHOST='${PBSHOST}' # IP des Proxmox Backup Servers
BACKUPSTORE='${BACKUPSTORE}' # Datastore auf Quell-Proxmox
BACKUPSTOREPBS='${BACKUPSTOREPBS}' # Datastore auf PBS
BACKUPEXCLUDE='${BACKUPEXCLUDE}' # VM/CT-IDs, die vom Backup ausgeschlossen sind
REPLEXCLUDE=\$BACKUPEXCLUDE # Diese auch von Replikation ausschließen
# Zusätzliche Monitoring-Ziele
# External Piggyback Host
EPIGGYBACK='${EPIGGYBACK}' # Piggyback-Daten an Monitoring-Ziel senden?
EPIGGYBACK_PORT='${EPIGGYBACK_PORT}' # SSH-Port für EPIGGYBACK_HOST
EPIGGYBACK_HOST='${EPIGGYBACK_HOST}' # Monitoring-Zielhost für Piggyback
# External Checkzfs Host
ECHECKZFS='${ECHECKZFS}' # check_zfs-Output an Monitoring-Ziel senden?
ECHECKZFS_PORT='${ECHECKZFS_PORT}' # SSH-Port für ECHECKZFS_HOST
ECHECKZFS_HOST='${ECHECKZFS_HOST}' # Monitoring-Zielhost für check_zfs
EOF
}
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <configfile>"
exit 1
fi
ORIGINAL="$1"
read -rp "Soll die aktuelle Datei als Backup gesichert werden (umbenennen)? (ja/nein): " RESPONSE
RESPONSE="${RESPONSE,,}"
if [[ "$RESPONSE" == "ja" || "$RESPONSE" == "j" ]]; then
BACKUPFILE="${ORIGINAL}.bak"
mv "$ORIGINAL" "$BACKUPFILE"
log "Originaldatei wurde umbenannt in: $BACKUPFILE"
CONFIG_TO_READ="$BACKUPFILE"
else
log "Keine Sicherung der Originaldatei durchgeführt. Original bleibt unverändert."
CONFIG_TO_READ="$ORIGINAL"
fi
NEWFILE="${ORIGINAL}.convert"
load_config "$CONFIG_TO_READ"
write_new_config "$NEWFILE"
log "Neue Konfiguration geschrieben in: $NEWFILE"
+255
View File
@@ -0,0 +1,255 @@
#!/bin/bash
# Author: (C) 2025 Patrick Perlbach <patrick@perlbach24.de>
# --- Konfiguration und Initiale Variablen ---
FORCE_YES=0
DRY_RUN=0
CONFIG_FILE=""
# --- Hilfsfunktionen ---
usage() {
cat <<EOF
Usage: $0 -c <config> [--yes] [--dry-run]
-c <file> Pfad zur Konfigurationsdatei (Pflicht)
--yes Automatische Ausführung ohne Rückfrage
--dry-run Zeigt nur, was gemacht würde
--help Diese Hilfe anzeigen
EOF
exit 1
}
run_or_echo() {
if [[ "$DRY_RUN" == "1" ]]; then
echo "[DRY-RUN] $*"
else
eval "$@"
fi
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
-c) CONFIG_FILE="$2"; shift 2 ;;
--yes) FORCE_YES=1; shift ;;
--dry-run) DRY_RUN=1; shift ;;
--help) usage ;;
*) echo "[ERROR] Unbekannter Parameter: $1"; usage ;;
esac
done
[[ -z "$CONFIG_FILE" || ! -f "$CONFIG_FILE" ]] && {
echo "[ERROR] Konfigurationsdatei fehlt oder ungültig!"
exit 1
}
source "$CONFIG_FILE"
}
confirm() {
if [[ "$FORCE_YES" -eq 1 ]]; then
return 0
fi
while true; do
read -rp "$1 [j/N]: " yn
case $yn in
[JjYy]*) return 0 ;;
[Nn]*|"") return 1 ;;
*) echo "Bitte j (ja) oder n (nein) eingeben." ;;
esac
done
}
install_cmk() {
echo "[INFO] Lade cmk.deb herunter..."
run_or_echo "curl -fsSL \"$CMK_URL\" -o \"$CMK_FILE\""
echo "[INFO] Installiere cmk.deb..."
run_or_echo "sudo apt install -y \"$CMK_FILE\""
}
install_fix_interface_names() {
echo "[INFO] Installiere Fix für Interface-Namen..."
run_or_echo "wget -O /tmp/debian-fix-interface-names.sh \"$FIX_INTERFACE_NAMES_URL\""
run_or_echo "chmod +x /tmp/debian-fix-interface-names.sh"
run_or_echo "bash /tmp/debian-fix-interface-names.sh"
}
install_miyagi_scripts() {
echo "[INFO] Lade Miyagi-Backup-Skripte..."
run_or_echo "wget -O miyagi-backup.sh \"$MIYAGI_BACKUP_URL\""
run_or_echo "wget -O miyagi-check.sh \"$MIYAGI_CHECK_URL\""
run_or_echo "wget -O miyagi-convert.sh \"$MIYAGI_CONVERT_URL\""
run_or_echo "mkdir -p /root/miyagi-backup"
run_or_echo "mv miyagi-backup.sh miyagi-check.sh miyagi-convert.sh /root/miyagi-backup/"
run_or_echo "chmod +x /root/miyagi-backup/*.sh"
}
install_checkmk_plugins() {
echo "[INFO] Installiere disk_smart_info Plugin..."
run_or_echo "curl -fsSL \"$DISK_SMART_INFO_URL\" -o /usr/lib/check_mk_agent/plugins/disk_smart_info"
run_or_echo "chmod +x /usr/lib/check_mk_agent/plugins/disk_smart_info"
}
install_cleansnaps() {
echo "[INFO] Installiere cleansnaps Skript..."
run_or_echo "wget -O /usr/local/bin/cleansnaps \"$CLEANSNAPS_URL\""
run_or_echo "chmod +x /usr/local/bin/cleansnaps"
}
install_check_snapshot_age() {
echo "[INFO] Installiere check-snapshot-age..."
run_or_echo "wget -O /usr/local/bin/check-snapshot-age \"$CHECK_SNAPSHOT_AGE_URL\""
run_or_echo "chmod +x /usr/local/bin/check-snapshot-age"
}
install_checkzfs() {
echo "[INFO] Installiere checkzfs..."
run_or_echo "wget -O /usr/local/bin/checkzfs \"$CHECKZFS_URL\""
run_or_echo "chmod +x /usr/local/bin/checkzfs"
}
check_ssh() {
echo "[INFO] Teste SSH-Verbindung zu $SOURCEHOST ..."
if ssh -p "$SOURCEPORT" -o BatchMode=yes -o ConnectTimeout=5 root@"$SOURCEHOST" "echo Verbindung erfolgreich" 2>/dev/null; then
echo "[OK] SSH-Verbindung über Schlüssel funktioniert."
else
echo "[WARN] Keine Schlüsselbasierte SSH-Verbindung möglich oder abgelehnt."
echo "[HINWEIS] Eine Passwortabfrage erfolgt jetzt dies ist normal."
if ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "echo Verbindung erfolgreich"; then
echo "[OK] SSH-Verbindung per Passwort erfolgreich."
if confirm "Möchtest du den lokalen SSH-Schlüssel auf $SOURCEHOST kopieren (per ssh-copy-id)?"; then
run_or_echo "ssh-copy-id -p \"$SOURCEPORT\" root@\"$SOURCEHOST\""
echo "[INFO] Bitte erneut starten, um die Schlüsselverbindung zu nutzen."
exit 0
else
echo "[HINWEIS] Es wird weiterhin Passwortabfrage benötigt."
fi
else
echo "[FEHLER] SSH-Verbindung nicht möglich bitte Zugang prüfen!"
exit 1
fi
fi
}
select_zfs_pools() {
echo "[INFO] Lade ZFS-Datasets vom Remote-Host..."
LOCAL_HOSTNAME=$(hostname -s)
REMOTE_HOSTNAME=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "hostname -s")
TAG_KEY="bashclub:miyagi-${REMOTE_HOSTNAME}-${LOCAL_HOSTNAME}"
TAG_VALUE="subvols"
# Hole alle relevanten Pool-Namen
mapfile -t pools < <(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" \
"zfs list -H -o name" | \
grep -E '/(vm|subvol)-' | \
grep -viE 'repl|replica' | \
sed 's:/[^/]*$::' | \
sort -u)
if [[ ${#pools[@]} -eq 0 ]]; then
echo "[WARN] Keine geeigneten Datasets gefunden!"
return
fi
local options=()
local tagged_pools=()
for pool in "${pools[@]}"; do
local tag status
tag=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs get -H -o value "$TAG_KEY" "$pool" 2>/dev/null)
if [[ "$tag" == "$TAG_VALUE" ]]; then
status="on"
tagged_pools+=("$pool")
else
status="off"
fi
options+=("$pool" "$([[ "$status" == "on" ]] && echo 'TAGGED' || echo 'untagged')" "$status")
done
local selected
selected=$(whiptail --title "ZFS Pools auswählen" \
--checklist "Wähle Datasets zum Taggen aus:" 20 78 12 \
"${options[@]}" \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
echo "[INFO] Auswahl abgebrochen."
return
fi
local selected_array=()
eval "selected_array=($selected)"
echo "[INFO] Verarbeite Tagging für ausgewählte Pools:"
for ((i = 0; i < ${#options[@]}; i += 3)); do
local pool="${options[i]}"
local prev_status="${options[i+2]}"
local is_selected=0
for sel in "${selected_array[@]}"; do
if [[ "$sel" == "$pool" ]]; then
is_selected=1
break
fi
done
if [[ "$is_selected" -eq 1 && "$prev_status" == "off" ]]; then
echo " → Tagge $pool"
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs set "$TAG_KEY=$TAG_VALUE" "$pool"
elif [[ "$is_selected" -eq 0 && "$prev_status" == "on" ]]; then
echo " → Entferne Tag von $pool"
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs inherit "$TAG_KEY" "$pool"
fi
done
}
main() {
# URLs und Dateipfade als Variablen, ggf. in Kopfbereich auslagern
CMK_URL="https://nc.sysops.de/index.php/s/YofRT5LBfX5ZDQs/download/cmk.deb"
CMK_FILE="/tmp/cmk.deb"
FIX_INTERFACE_NAMES_URL="https://raw.githubusercontent.com/bashclub/trmm-scripts/refs/heads/main/debian-fix-interface-names.sh"
MIYAGI_BACKUP_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-backup.sh"
MIYAGI_CHECK_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-check.sh"
MIYAGI_CONVERT_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-convert.sh"
DISK_SMART_INFO_URL="https://raw.githubusercontent.com/bashclub/checkmk-smart/main/disk_smart_info.py"
CLEANSNAPS_URL="https://raw.githubusercontent.com/bashclub/zfs-housekeeping/refs/heads/main/cleansnaps.sh"
CHECK_SNAPSHOT_AGE_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/check-snapshot-age"
CHECKZFS_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/checkzfs.py"
echo "[INFO] Starte Installation mit Konfig: $CONFIG_FILE"
install_cmk
install_fix_interface_names
install_miyagi_scripts
install_checkmk_plugins
install_cleansnaps
install_check_snapshot_age
install_checkzfs
check_ssh
select_zfs_pools
echo "[OK] Installation abgeschlossen."
}
# Start
parse_args "$@"
main