diff options
| -rwxr-xr-x | alpine-diskless.sh | 5 | ||||
| -rwxr-xr-x | alpine.sh | 27 | ||||
| -rwxr-xr-x | backup.sh | 101 | ||||
| -rwxr-xr-x | clean.sh | 12 | ||||
| -rwxr-xr-x | openbsd.sh | 34 | ||||
| -rw-r--r-- | readme.md | 41 |
6 files changed, 220 insertions, 0 deletions
diff --git a/alpine-diskless.sh b/alpine-diskless.sh new file mode 100755 index 0000000..d360bdd --- /dev/null +++ b/alpine-diskless.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# alpine diskless backup... +# Save the tarball to <hostname>.apkovl.tar.gz to restore +set -e +lbu package - diff --git a/alpine.sh b/alpine.sh new file mode 100755 index 0000000..11ab4b1 --- /dev/null +++ b/alpine.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# this requires the gnu tools: +# apk add coreutils tar +IFS=' +' +_paths='/etc +/home +/root +/var/acme +/var/service +/var/sshkeys +/var/spool/cron' + +_file_list="$(mktemp)" +ref="$(mktemp)" +trap 'rm "$list" "$ref"' EXIT INT +touch -d '-24 hours' "$ref" +trap 'rm -f "$_file_list"' EXIT INT + +if [ "$1" = daily ] ; then + find $_paths -newer "$ref" -print0 > "$_file_list" +else + find $_paths -print0 > "$_file_list" +fi + +tar --null -T "$_file_list" -cf - \ + | zstd -3c diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..5aa1f96 --- /dev/null +++ b/backup.sh @@ -0,0 +1,101 @@ +#!/bin/sh +set -e +svr="" +ssh_conf="ssh/config" +ssh_key="ssh/id_ed25519" +_backup_type="daily" +_suffix="tar.zst" +force=false + + +_date_for_type() { +case "$1" in +daily) date +%Y-%m-%d ;; +weekly) date +%Y-%U ;; +monthly) date +%Y-%m ;; +*) date +%s ;; # somehow, a bad type so just default to unix timestamp +esac +} + +__help() { +cat <<EOF +$0 -svr <server> [opts] + +Create a backup for <server>, defaults to daily. Calls <server>/backup.sh +on the remote host and saves the stdout. It's up to the server's +specific backup script to determine the specifics of that format. Tarballs +are suggested, but snapshots, cpio archives, etc can all be used. + +Where [opts] are one of: + +-ssh_conf <filename> # For ssh configuration i.e. -F flag on ssh +-ssh_key <filename> # SSH key to use, passed in as -i to ssh +-type <daily|monthly|weekly> # Passed to the subscript, and changes output dir +-suffix <suffix> # Defaults to 'tar.zst', but could be anything, should match + # the output of the server's specific backup script +-force # Normally we bail if a backup exists, this will force overwrite + +Normal usage is that weekly and monthly backups are full, and the daily +backups only include files that were changed in the past 24 hours, usually +via find command and piping a list of input files to tar. + +A separate ./cleanup.sh script can take care of removing the old backups +on a schedule that you prefer. +EOF +exit 2 +} + +while [ $# -gt 0 ] ; do case $1 in + -svr) svr="$2"; shift ; shift ;; + -ssh_conf) ssh_conf="$2"; shift ; shift ;; + -ssh_key) ssh_key="$2"; shift ; shift ;; + -type) _backup_type="$2"; shift ; shift ;; + -suffix) _suffix="$2"; shift ; shift ;; + -force) force=true ; shift ;; + *) __help ;; +esac ; done + +_err=0 +if [ -z "$svr" ] ; then + echo "-svr flag needs to be set" + _err=1 +fi +if [ -z "$ssh_conf" ] ; then + echo "-ssh_conf flag needs to be set" + _err=1 +fi +if [ -z "$ssh_key" ] ; then + echo "-ssh_key flag needs to be set" + _err=1 +fi + +if ! [ -x "$svr/backup.sh" ] ; then + echo "$svr/backup.sh should exist and be executable!" + _err=1 +fi + +case "$_backup_type" in + daily|weekly|monthly) true ;; + *) echo "Bad backup type: '$_backup_type' valid options are daily, weekly, or monthly"; _err=1 ;; +esac + +if [ "$_err" -ne 0 ] ; then + exit "$_err" +fi + +ssh -F "$ssh_conf" -i "$ssh_key" "$svr" "mkdir -p /root/.backup" +ssh -F "$ssh_conf" -i "$ssh_key" "$svr" "cat > /root/.backup/script.sh" < "$svr/backup.sh" +ssh -F "$ssh_conf" -i "$ssh_key" "$svr" "chmod +x /root/.backup/script.sh" + + +outfile="$svr/$_backup_type/$(_date_for_type "$_backup_type").$_suffix" +mkdir -p "$(dirname "$outfile")" +if [ -e "$outfile" ] ; then + echo "BACKUP FILE EXISTS ALREADY: $outfile" + if $force ; then + echo "OVERWRITING" + else + exit 1 + fi +fi +ssh -F "$ssh_conf" -i "$ssh_key" "$svr" "/root/.backup/script.sh \"$_backup_type\"" | pv > "$outfile" diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..a6b85fc --- /dev/null +++ b/clean.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +svrs='change +me +example.com +weeeee.example.com' + +for _svr in $svrs ; do + find ./"$_svr"/monthly -type f -mtime +$((30*6)) -delete + find ./"$_svr"/weekly -type f -mtime +$((7*4)) -delete + find ./"$_svr"/daily -type f -mtime +7 -delete +done diff --git a/openbsd.sh b/openbsd.sh new file mode 100755 index 0000000..a876f14 --- /dev/null +++ b/openbsd.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# OpenBSD backup script. Symlink into <svr>/backup.sh for use. +# Make sure you update /etc/fstab appropriately before rebooting after +# restore +# To restore packages: pkg_add -l /root/packages.lst +IFS=' +' +_paths='/root +/etc +/home +/var/redis +/var/cron +/var/mail +/var/nsd +/var/service +/var/sshkeys +/var/unbound +/var/www/htdocs +/var/rspamd +/var/spool/smtpd' + + +list="$(mktemp)" +ref="$(mktemp)" +trap 'rm "$list" "$ref"' EXIT INT +touch -d $(date -r $(($(date +%s) - 86400)) +%Y-%m-%dT%H:%M:%S) "$ref" +pkg_info -mz > /root/packages.lst +if [ "$1" = daily ] ; then + find $_paths -newer "$ref" -type f > "$list" +else + find $_paths -type f > "$list" +fi +tar -I "$list" -cf - \ + | zstd -3c diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2df285f --- /dev/null +++ b/readme.md @@ -0,0 +1,41 @@ +# Backup scripts + +Annoyed by complicated backup solutions, I've written my own simple scripts +to back up only what I care about in a format that's easily unpacked on a fresh +install of the system, that is, a tarball. There's no reason you couldn't +use `dump`, `btrfs send`, `zfs send` or similar, though and this script +supports handling that as long as you can pipe it over stdout and save to a file. + +Not for everyone, and I don't even use it for backups of my largest files, for +instance photos, videos, etc I use rclone and rsync instead of these scripts. + +## HOW TO + +Clone this repo down to the system that'll run the backups, set up a +ssh key in `ssh/id_ed25519` ( `ssh-keygen -t ed25519 -f ssh/id_ed25519` or so ) +and then set up your SSH config ( jump boxes, etc ) in `ssh/config` + +From there create a directory for each server that you're backing up, for +instance if I want to back up a server called `example.com`, `mkdir example.com`. + +From there, we need a script that will generate a tarball, examples can be +found in `openbsd.sh`, and `alpine.sh`, copy it to `example.com/backup.sh`, +and then `chmod +x` the file. This will be copied and then executed as root +on the remote machine. + +Another example worth calling out directly since it utilizes a slightly +different method, `alpine-diskless.sh` can be used to back up the config +using alpine's built in `lbu`, though it's worth passing in `-suffix tar.gz` +to the backup script. + + +Example crontab: + +``` +@monthly sh -c 'set -e;cd /home/backupdir;./backup.sh -type monthly -svr example.com' +@weekly sh -c 'set -e;cd /home/backupdir;./backup.sh -type weekly -svr example.com' +@daily sh -c 'set -e;cd /home/backupdir;./backup.sh -type daily -svr example.com' + +# Clean up the old backups daily, see the clean.sh for an example +@daily sh -c 'set -e;cd /home/backupdir;./clean.sh +``` |
