mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-08-12 02:37:01 +02:00
pg_dumpall dumped every database, and so_telegraf dominated the result: it is the only database that grows with grid size and metric volume, while everything else in the cluster is small and mostly static. Nothing is lost by skipping it. The data is transient metrics on a 14-day retention window, roles are globals so the per-minion telegraf logins are still dumped, and the database itself is rebuilt after a restore without operator action -- init-db.sh recreates it (run on every highstate by postgres_bootstrap_soc_db, not just on a fresh volume), telegraf_users.sls re-provisions the roles and schema, and Telegraf recreates its tables on first write.
79 lines
2.8 KiB
Bash
79 lines
2.8 KiB
Bash
#!/bin/bash
|
|
#
|
|
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
|
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
|
# https://securityonion.net/license; you may not use this file except in compliance with the
|
|
# Elastic License 2.0.
|
|
|
|
. /usr/sbin/so-common
|
|
|
|
# Without pipefail, a pipeline's exit status is gzip's. A failed pg_dumpall would
|
|
# otherwise be masked by a successful gzip, silently producing a valid .gz that
|
|
# holds a truncated dump.
|
|
set -o pipefail
|
|
|
|
# Backups contain role password hashes and full chat data; keep them 0600.
|
|
umask 0077
|
|
|
|
TODAY=$(date '+%Y_%m_%d')
|
|
BACKUPDIR=/nsm/backup
|
|
BACKUPFILE="$BACKUPDIR/so-postgres-backup-$TODAY.sql.gz"
|
|
TMPFILE="$BACKUPFILE.tmp"
|
|
MAXBACKUPS=7
|
|
LOGFILE=/opt/so/log/postgres/backup.log
|
|
|
|
log() {
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOGFILE"
|
|
}
|
|
|
|
mkdir -p "$BACKUPDIR"
|
|
|
|
# Remove any temp files left behind by a previously crashed run
|
|
rm -f "$BACKUPDIR"/so-postgres-backup-*.sql.gz.tmp
|
|
|
|
# Skip if already backed up today
|
|
if [ -f "$BACKUPFILE" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Skip if container isn't running
|
|
if ! docker ps --format '{{.Names}}' | grep -q '^so-postgres$'; then
|
|
exit 0
|
|
fi
|
|
|
|
# Always clean up the temp file on exit; the success path clears this trap
|
|
# after the atomic rename so the finished backup is not deleted.
|
|
trap 'rm -f "$TMPFILE"' EXIT
|
|
|
|
# Dump all databases and roles, compress. Write to a temp file so the final
|
|
# filename only ever appears for a complete, verified backup.
|
|
#
|
|
# so_telegraf is excluded: it is transient metrics on a short retention window,
|
|
# it dominates the dump size, and it is rebuilt automatically after a restore --
|
|
# init-db.sh recreates the database and Telegraf recreates its tables on first
|
|
# write. Roles are globals, so the per-minion telegraf logins are still dumped.
|
|
if ! docker exec so-postgres pg_dumpall -U postgres --exclude-database=so_telegraf | gzip > "$TMPFILE"; then
|
|
log "ERROR: pg_dumpall/gzip failed; backup aborted"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify the compressed stream is intact before publishing it
|
|
if ! gzip -t "$TMPFILE"; then
|
|
log "ERROR: backup failed gzip integrity check; backup aborted"
|
|
exit 1
|
|
fi
|
|
|
|
# Atomically publish the verified backup
|
|
mv "$TMPFILE" "$BACKUPFILE"
|
|
trap - EXIT
|
|
log "OK: wrote $BACKUPFILE"
|
|
|
|
# Retention cleanup (only reached after a successful backup). The glob is
|
|
# restricted to finished backups so an in-progress .tmp can never be counted.
|
|
NUMBACKUPS=$(find "$BACKUPDIR" -type f -name "so-postgres-backup-*.sql.gz" | wc -l)
|
|
while [ "$NUMBACKUPS" -gt "$MAXBACKUPS" ]; do
|
|
OLDEST=$(find "$BACKUPDIR" -type f -name "so-postgres-backup-*.sql.gz" -printf '%T+ %p\n' | sort | head -n 1 | awk -F" " '{print $2}')
|
|
rm -f "$OLDEST"
|
|
NUMBACKUPS=$(find "$BACKUPDIR" -type f -name "so-postgres-backup-*.sql.gz" | wc -l)
|
|
done
|