From 05f986658bc04e2dc514b2c3e7777007cb6fb65c Mon Sep 17 00:00:00 2001 From: Carlo Landmeter Date: Mon, 29 Jan 2018 15:31:27 +0000 Subject: Initial commit --- LICENSE | 21 +++++++++++++ README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ alpine-backup | 47 ++++++++++++++++++++++++++++ profiles/backup-archive | 13 ++++++++ profiles/backup-cleanup | 4 +++ profiles/backup-list | 4 +++ profiles/backup-mysql | 10 ++++++ profiles/backup-sync | 19 ++++++++++++ rbu.conf | 22 ++++++++++++++ 9 files changed, 221 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100755 alpine-backup create mode 100755 profiles/backup-archive create mode 100755 profiles/backup-cleanup create mode 100755 profiles/backup-list create mode 100755 profiles/backup-mysql create mode 100755 profiles/backup-sync create mode 100644 rbu.conf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e45ac6b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Carlo Landmeter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..caddbbd --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Alpine Linux Backup + +## Introduction + +Alpine remote backup (RBU) is a set of scripts based on LBU +(Alpine Local Backup). It consist of a main script which need to be run from +cron and a set of profiles which can be used as pre/post scripts for LBU. +The idea of profiles is to simplify the backup process and to make sure +services inside Alpine infrastructure are setup in a similar way. Some of the +features/profiles of RBU are: + +* Dump databases to a cache directory +* Synchronise backups to remote location +* Copy backup status log to remote location +* Send status notifications over mqtt + +## Usage + +### Prerequisite + +* POSIX shell +* LBU (default on Alpine installation) +* rsync `apk add rsync` +* mosquitto-pub `apk add mosquitto-clients` + +### Configuration + +RBU needs a configuration file named `rbu.conf`. This configuration should +(by default) be put in the lbu configuration directory `/etc/lbu`. Inside this +configuration should be a list of exported configuration variables which are +needed for lbu and its pre/post scripts to function properly. + +### Profiles + +Profiles are a setup of LBU pre/post scripts. To use these scripts you should +symlink (or copy) them from the profile directory to one of the following dirs: + +* `pre-package.d` (scripts run before LBU creates its backup) +* `post-package.d` (scripts run after LBU created its backup) + +These profiles/scripts will be run by busybox run-parts which means they cannot +have an extension and need to be executable. If you need to run these scripts +in a specific order you should prefix them with a number like `10-scriptname`. + +### Encryption + +To make sure the backups are safe we use openssl encryption option provided by +LBU. Please make sure you provide a secure password in `rbu.conf` and make sure +you have a backup of that password in case you will need it when restoring. + +### Include non etc files + +By default LBU will only backup files located in the `/etc` directory and have +been modified. To include other files you need to lbu_include files and +directories. Exclusion is possible via lbu_exclude. Please check Alpine wiki for +more information regarding LBU usage. + +### Backup server + +Backups will be kept in the specified backup location in rbu.conf. After the +backup is successful these backups will be rsynced to the remote backup server. +To be able to automatically rsync backups a ssh key needs to be generated per +backup. For generating a Ed25519 key exec: `ssh-keygen -t ed25519` and copy the +public key to the backup server. + +### Enable daily backups + +Copy the provided `alpine-backup.cron` as `/etc/periodic/daily/alpine-backup` to +make the backup run every night. + +## Backup monitoring + +Backups are are automatically monitored by Alpine monitoring system. After the +backup has finished it will send a message with metadata to our mqtt server +which will be picked up by Alpine monitoring system. + +## NOTE + +Older version of LBU have a bug which prevents the cleanup of older local +encrypted backups. see: +https://git.alpinelinux.org/cgit/alpine-conf/commit/?id=cd395b \ No newline at end of file diff --git a/alpine-backup b/alpine-backup new file mode 100755 index 0000000..22af5a5 --- /dev/null +++ b/alpine-backup @@ -0,0 +1,47 @@ +#!/bin/sh + +RBU_CONF=/etc/lbu/rbu.conf + +if [ -f "$RBU_CONF" ]; then + . "$RBU_CONF" +else + echo "Cannot read $RBU_CONF" + exit 1 +fi + +# set the suffix of backup files when ecryptions is enabled +[ -n "$ENCRYPTION" ] && export SUFFIX=".$ENCRYPTION" + +# convert hostname to intra FQDN +HOSTNAME=$(hostname) +if [ -n $HOST ]; then + export FQDN="${HOSTNAME}.${HOST}.intra.alpinelinux.org" +else + export FQDN="${HOSTNAME}.intra.alpinelinux.org" +fi + +START_TS=$(date +%s) + +LOGFILE="$BACKUP_CACHE/$FQDN-$(date -u "+%Y%m%d%H%M%S").log" +lbu_commit >>"$LOGFILE" 2>&1 +ret=$? + +DURATION=$(($(date +%s)-$START_TS)) +SIZE=0 +STATUS=failed + +if [ $ret = 0 ]; then + [ -n "$ENCRYPTION" ] && SUFFIX=".$ENCRYPTION" + BACKUP="$BACKUP_LOCATION/$(hostname).apkovl.tar.gz${SUFFIX}" + SIZE=$(stat -c %s $BACKUP) + STATUS=success +fi + +ssh "$BACKUP_SERVER" mkdir -p $FQDN/logs +scp -q $LOGFILE "$BACKUP_SERVER:$FQDN/logs" + +PAYLOAD=$(printf '{ "status": "%s", "size": %u, "duration": %u }' "$STATUS" "$SIZE" "$DURATION") +JSON=$(printf '{ "host": "%s", "key": "backup", "payload": %s }' "$FQDN" "$PAYLOAD") + +mosquitto_pub -h msg.alpinelinux.org -t "monitoring/updates" -m "$JSON" + diff --git a/profiles/backup-archive b/profiles/backup-archive new file mode 100755 index 0000000..ed72be6 --- /dev/null +++ b/profiles/backup-archive @@ -0,0 +1,13 @@ +#!/bin/sh + +DATE=$(date -u "+%Y%m%d%H%M%S") +SRC="$(hostname).apkovl.tar.gz${SUFFIX}" +DEST="$(hostname).${DATE}.tar.gz${SUFFIX}" + +# Keep a montly copy of the first day of the month +if [ "$(date +%d)" = "01" ]; then + echo "Archiving montly backup" + echo "$SRC" + echo "$DEST" + ssh "$BACKUP_SERVER" install -Dm644 "$FQDN"/week/"$SRC" "$FQDN"/month/"$DEST" +fi diff --git a/profiles/backup-cleanup b/profiles/backup-cleanup new file mode 100755 index 0000000..b590b22 --- /dev/null +++ b/profiles/backup-cleanup @@ -0,0 +1,4 @@ +#!/bin/sh + +echo "Removing mysqldump file" +rm -f $BACKUP_CACHE/mysql.sql diff --git a/profiles/backup-list b/profiles/backup-list new file mode 100755 index 0000000..30afb37 --- /dev/null +++ b/profiles/backup-list @@ -0,0 +1,4 @@ +#!/bin/sh +echo "Files included in this backup:" +apk audit --backup --quiet --recursive --check-permissions +echo "" diff --git a/profiles/backup-mysql b/profiles/backup-mysql new file mode 100755 index 0000000..ff6edd6 --- /dev/null +++ b/profiles/backup-mysql @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ -n "$MYSQL_PASSWORD" ]; then + echo "Dumping databases to: $BACKUP_CACHE/mysql.sql" + [ -d "$CACHE_DIR" ] || mkdir -p "$BACKUP_CACHE" + mysqldump -u root -p${MYSQL_PASSWORD} --all-databases -r \ + $BACKUP_CACHE/mysql.sql +else + echo "MySQL password not set, skipping mysqldump." +fi diff --git a/profiles/backup-sync b/profiles/backup-sync new file mode 100755 index 0000000..d4514ea --- /dev/null +++ b/profiles/backup-sync @@ -0,0 +1,19 @@ +#!/bin/sh + +echo "Sending backup to backup server" + +# make sure the destination directory exists +ssh "$BACKUP_SERVER" mkdir -p $FQDN/week + +# get the name of the renamed backup and also rename +# this backup on the remote to prevent dulpicate upload +CURR_BACKUP=$(ls -t "$BACKUP_LOCATION" | sed -n 2p) +if [ -n "$CURR_BACKUP" ]; then + HOSTNAME=$(hostname) + ssh "$BACKUP_SERVER" mv -f \ + $FQDN/week/$HOSTNAME.apkovl.tar.gz${SUFFIX} \ + $FQDN/week/$CURR_BACKUP +fi + +rsync -av --delete-after "$BACKUP_LOCATION/" \ + "$BACKUP_SERVER":"$FQDN"/week diff --git a/rbu.conf b/rbu.conf new file mode 100644 index 0000000..c54e2af --- /dev/null +++ b/rbu.conf @@ -0,0 +1,22 @@ +# enable and set encryption +export ENCRYPTION=aes-256-cbc +# Encryption password +export PASSWORD=mysecurepassword +# backup location +export LBU_BACKUPDIR=/var/lib/backup +# amount of backups to store +export BACKUP_LIMIT=7 +# if this host is a guest set HOST to hostname of host +# export HOST= +# the location of the backup cache +export BACKUP_CACHE=/var/cache/backup +# backup directory to be picked up by pre script +export BACKUP_LOCATION="$LBU_BACKUPDIR" +# ssh backup server to send backups to +export BACKUP_SERVER="user@backupserver.tld" + +# Custom profile settings + +# the mysql password to be picked up by pre script +# export MYSQL_PASSWORD=mysecurepasword + -- cgit v1.2.3