From d14d9b8fa7732345fe7871963c77f512c5cad731 Mon Sep 17 00:00:00 2001 From: Ashten Date: Thu, 9 Jan 2025 08:16:08 -0800 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ README.md | 79 ++++++++++++++++++++++++++++++++++++++++++ backup.sh | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ config_example | 35 +++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 backup.sh create mode 100644 config_example diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2fc4c88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config +databases/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f3413d --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# About: +This is a very basic backup script that: +1. Exports databases, stores the md5sums, and then compresses them. +2. Uploads the databases, along with specified directories to S3. +3. Prunes old backups. + +It will send an email to the specified email if there's an error. + +This is the backup script in use on Woem.men's servers. + +# Prerequisites: +**Ubuntu Packages:** +```bash +sudo apt install restic postgresql-client sendemail +``` +**External Services:** +1. An S3 provider. (Amazon S3, Backblaze, etc) +1. Email provider which allows SMTP. (Gmail, Fastmail, etc) + +# Setup: +You'll need to create the required directory, and pull the files from this repository: +```bash +sudo mkdir /backups/ +cd /backups/ +git clone +``` +Copy the `config_example` to `config`, verify permissions, and then edit using your favorite text editor: +```bash +sudo su +cp config_example config +chown root:root config +chmod 700 config +nano config +``` +Next, you'll need to run initial setup on the S3 repository: +```bash +sudo su # if not already root +set -a +source config +restic -r "$S3_BUCKET" init +``` +And finally, set your crontab file with `sudo crontab -e`: +``` +0 * * * * /backups/backup.sh > /backups/logfile.log +``` + +# Restoring: +You'll need to grab your config variables before restoring: +```bash +sudo su # if not already root +cd /backups/ + +set -a +source config +``` +After that, fetch a list of backups available to you by running `restic -r "$S3_BUCKET" snapshots`. + +**Example:** +``` +root@guilty-spark:/backups# restic -r "$S3_BUCKET" snapshots +repository 17b028ae opened (version 2, compression level auto) +ID Time Host Tags Paths +--------------------------------------------------------------- +724327bb 2025-01-09 07:10:04 guilty-spark /backups + /etc + /home + /root +--------------------------------------------------------------- +``` +Select one, and run the following command, making adjustments as needed for your setup: +Using one of the snapshots, restore the files you're missing using `restic restore`. I recommend reading the man file for `restic-restore`. + +**Examples:** +``` +root@guilty-spark:/backups# restic -r "$S3_BUCKET" restore 724327bb -i /home/service/example_file.txt -t /tmp/restore/ +``` +``` +root@guilty-spark:/backups# restic -r "$S3_BUCKET" restore 724327bb -t /tmp/restore/ +``` \ No newline at end of file diff --git a/backup.sh b/backup.sh new file mode 100644 index 0000000..bee0bf9 --- /dev/null +++ b/backup.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +set -a +source .config +set +a +set -e + +if [ "$DEBUGGING" -eq 1 ]; then + set -x +fi + +# Remove lock: +remove_lock() { + rm /tmp/backup.lock +} + +# Error Handling: +error_handling() { + echo "$(date) -> An error occurred!" >&2 + + if [ "$ENABLE_EMAILS" -eq 1 ]; then + subject="Backup Error -- $SERVER_NAME" + body="An error occurred during backup of $SERVER_NAME!\nThe current time is $(date).\nPlease check server logs!" + if [ "$DEBUGGING" -eq 1 ]; then + sendemail -f $SMTP_FROM -t $SMTP_RECIPIENT -u "$subject" -s $SMTP_SERVER -m "$body" -xu $SMTP_USER -xp $SMTP_PASSWORD -v -o message-charset=$CHARSET + else + sendemail -f $SMTP_FROM -t $SMTP_RECIPIENT -u "$subject" -s $SMTP_SERVER -m "$body" -xu $SMTP_USER -xp $SMTP_PASSWORD -q -o message-charset=$CHARSET + fi + echo "$(date) -> Email sent!" + fi + remove_lock + exit 1 +} +trap "error_handling" ERR +trap "remove_lock" SIGINT + +if [ "$TEST_ERROR_HANDLING" -eq 1 ]; then + error_handling + exit 1 +fi + +# Test, then create Lockfile +if [ -e /tmp/backup.lock ]; then + echo "Backups are already running! If in error, manually remove the lockfile!" +fi +touch /tmp/backup.lock + +# Backups: +if [ ! -z "$DATABASES" ]; then + mkdir -p /backups/databases/ + touch /backups/databases/delete + rm /backups/databases/* + + echo "$(date) -> Dumping databases..." + + for database in "${DATABASES[@]}"; do + echo "$(date) -> $database" + sudo -u postgres pg_dump -d "$database" -f /home/tmp/"$database".pgdump + md5sum /home/tmp/"$database".pgdump > /backups/databases/md5sums + zstd /home/tmp/"$database".pgdump -o /backups/databases/"$database".pgdump.zst + rm /home/tmp/"$database".pgdump + done + + echo "$(date) -> Dumped!" +else + echo "No databases selected! Skipping DB export!" +fi + +echo "$(date) -> Uploading..." + +if [ ! -z "$BACKUP_EXCLUDE" ]; then + for exclude in "${BACKUP_EXCLUDE[@]}"; do + exclude_parsed=$exclude_parsed' -e '$exclude + done +fi + +echo "$exclude_parsed" + +if [ ! "$MAX_SPEED_KILOBYTES_SECOND" -eq 0 ]; then + speed_parsed='--limit-upload '$(echo $MAX_SPEED_KILOBYTES_SECOND) +fi + +restic -r "$S3_BUCKET" backup $BACKUP_LIST $exclude_parsed $speed_parsed + +echo "$(date) -> Pruning old backups..." + +restic -r "$S3_BUCKET" forget --keep-hourly $HOURLY --keep-daily $DAILY --keep-monthly $MONTHLY --keep-yearly $YEARLY +restic -r "$S3_BUCKET" prune +restic -r "$S3_BUCKET" unlock + +remove_lock + +echo "$(date) -> Done!" \ No newline at end of file diff --git a/config_example b/config_example new file mode 100644 index 0000000..9764516 --- /dev/null +++ b/config_example @@ -0,0 +1,35 @@ +# Logging: +SERVER_NAME="Example Server" + +# Credentials +S3_BUCKET="s3:s3.example.com" +AWS_ACCESS_KEY_ID="000000000000" +AWS_SECRET_ACCESS_KEY="00000000000000" +RESTIC_PASSWORD="Password" + +# Backup Settings: +BACKUP_LIST="/etc/ /backups/ /home/ /root/" +BACKUP_EXCLUDE=(/home/bad_user/ /home/example_user/cache/) +MAX_SPEED_KILOBYTES_SECOND=2500 + +# Database Exports: +DATABASES=( database1 database2 ) + +# Retaining Backups: +# For GDPR, set monthly and yearly to 0, daily to 30. +HOURLY=48 +DAILY=30 +MONTHLY=12 +YEARLY=5 + +# SMTP Settings: +ENABLE_EMAILS=0 +SMTP_SERVER="smtp.example.com:587" +SMTP_USER="example@example.com" +SMTP_FROM="noreply@example.com" +SMTP_PASSWORD="password" +SMTP_RECIPIENT="example@example.com" + +# Troubleshooting: +DEBUGGING=0 +TEST_ERROR_HANDLING=0 \ No newline at end of file