From c57b68c35c982b34486fdb110517cbfa802f5869 Mon Sep 17 00:00:00 2001 From: darta Date: Wed, 17 Nov 2021 22:24:38 +0100 Subject: [PATCH] chore(backupninja): added automated backups for db and disks with backupninja MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simó Albert i Belran --- .gitlab-ci.yml | 4 +- build.sh | 13 +++ docker-compose-parts/backupninja.build.yml | 7 ++ docker-compose-parts/backupninja.yml | 31 ++++++ docker/backupninja/Dockerfile | 41 +++++++ docker/backupninja/backup.sh | 58 ++++++++++ docker/backupninja/run.sh | 123 +++++++++++++++++++++ isardvdi.cfg.example | 13 +++ 8 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 docker-compose-parts/backupninja.build.yml create mode 100644 docker-compose-parts/backupninja.yml create mode 100644 docker/backupninja/Dockerfile create mode 100755 docker/backupninja/backup.sh create mode 100755 docker/backupninja/run.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1bed2926e8..213ff860a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -95,7 +95,7 @@ docker-compose-build: - echo "FLAVOUR=video-standalone" >> isardvdi.video-standalone.build.cfg - cp isardvdi.build.cfg isardvdi.toolbox.build.cfg - echo "FLAVOUR=toolbox" >> isardvdi.toolbox.build.cfg - - ./build.sh + - BACKUP_DB_ENABLED=true ./build.sh artifacts: paths: - docker-compose*.yml @@ -111,6 +111,7 @@ docker-image: IMAGE: - api - authentication + - backupninja - db - engine - grafana @@ -159,6 +160,7 @@ docker-tag: - IMAGE: - api - authentication + - backupninja - db - engine - grafana diff --git a/build.sh b/build.sh index ec0bd7787a..be4eb13959 100755 --- a/build.sh +++ b/build.sh @@ -41,6 +41,7 @@ ALLINONE_PARTS=" vpn guac toolbox + backupninja " HYPERVISOR_KEY="hypervisor" HYPERVISOR_PARTS=" @@ -252,6 +253,14 @@ create_docker_compose_file(){ then ENABLE_STATS="true" fi + if [ -z "$BACKUP_DB_ENABLED" ] + then + BACKUP_DB_ENABLED="false" + fi + if [ -z "$BACKUP_DISKS_ENABLED" ] + then + BACKUP_DISKS_ENABLED="false" + fi if [ -z "$FLAVOUR" ] then FLAVOUR="all-in-one" @@ -288,6 +297,10 @@ create_docker_compose_file(){ then parts="$(echo $parts | sed 's/stats//')" fi + if [ "$BACKUP_DB_ENABLED" = "false" ] && [ "$BACKUP_DISKS_ENABLED" = "false" ] + then + parts="$(echo $parts | sed 's/backupninja//')" + fi flavour "$config_name" $parts } diff --git a/docker-compose-parts/backupninja.build.yml b/docker-compose-parts/backupninja.build.yml new file mode 100644 index 0000000000..3d3500f95a --- /dev/null +++ b/docker-compose-parts/backupninja.build.yml @@ -0,0 +1,7 @@ +version: '3.5' +services: + isard-backupninja: + build: + context: ${BUILD_ROOT_PATH} + dockerfile: docker/backupninja/Dockerfile + target: production diff --git a/docker-compose-parts/backupninja.yml b/docker-compose-parts/backupninja.yml new file mode 100644 index 0000000000..7b5e1c80ff --- /dev/null +++ b/docker-compose-parts/backupninja.yml @@ -0,0 +1,31 @@ +version: '3.5' +services: + isard-backupninja: + container_name: isard-backupninja + image: ${DOCKER_IMAGE_PREFIX}backupninja:${DOCKER_IMAGE_TAG:-latest} + logging: + options: + max-size: "100m" + networks: + isard-network: + ipv4_address: ${DOCKER_NET:-172.31.255}.88 + restart: unless-stopped + volumes: + - ${BACKUP_DIR-/opt/isard-local/backup}:/backup:rw + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - /opt/isard/templates:/opt/isard/templates:ro + - /opt/isard/groups:/opt/isard/groups:ro + - /opt/isard/media:/opt/isard/media:ro + environment: + - BACKUP_DIR=${BACKUP_DIR-/opt/isard-local/backup} + - BACKUP_DB_ENABLED=${BACKUP_DB_ENABLED-false} + - BACKUP_REPORT_EMAIL=${BACKUP_REPORT_EMAIL-root} + - BACKUP_DB_WHEN=${BACKUP_DB_WHEN-everyday at 04:01} + - BACKUP_DB_PRUNE=${BACKUP_DB_PRUNE---keep-weekly=8 --keep-monthly=12 --keep-within=14d --save-space} + - BACKUP_DISKS_ENABLED=${BACKUP_DISKS_ENABLED-false} + - BACKUP_DISKS_WHEN=${BACKUP_DISKS_WHEN-everyday at 04:01} + - BACKUP_DISKS_PRUNE=${BACKUP_DISKS_PRUNE---keep-weekly=4 --keep-monthly=3 --keep-within=7d --save-space} + - BACKUP_DISKS_TEMPLATES_ENABLED=${BACKUP_DISKS_TEMPLATES_ENABLED-false} + - BACKUP_DISKS_GROUPS_ENABLED=${BACKUP_DISKS_GROUPS_ENABLED-false} + - BACKUP_DISKS_MEDIA_ENABLED=${BACKUP_DISKS_MEDIA_ENABLED-false} diff --git a/docker/backupninja/Dockerfile b/docker/backupninja/Dockerfile new file mode 100644 index 0000000000..a3228a00cd --- /dev/null +++ b/docker/backupninja/Dockerfile @@ -0,0 +1,41 @@ +FROM alpine:3.14.0 as build +MAINTAINER isard +RUN apk add --no-cache \ + automake \ + autoconf \ + bash \ + gcc \ + git \ + make +RUN git clone https://0xacab.org/liberate/backupninja.git && \ + cd backupninja && \ + bash ./autogen.sh && bash configure && \ + make && make install + +FROM alpine:3.14.0 as production +MAINTAINER isard +COPY --from=build /usr/local/sbin/backupninja /usr/local/sbin/backupninja +COPY --from=build /usr/local/sbin/ninjahelper /usr/local/sbin/ninjahelper +COPY --from=build /usr/local/etc/backupninja.conf /usr/local/etc/backupninja.conf +COPY --from=build /usr/local/lib/backupninja /usr/local/lib/backupninja +COPY --from=build /usr/local/share/backupninja /usr/local/share/backupninja +RUN sed -i '/^reportemail =/s/^/#/' /usr/local/etc/backupninja.conf +RUN ln -s /usr/local/sbin/backupninja /etc/periodic/hourly/backupninja +RUN mkdir -p -m700 /usr/local/etc/backup.d +RUN mkdir -p /usr/local/var/log +RUN mkdir /backup +RUN mkdir /dbdump +WORKDIR / +CMD [ "run.sh" ] +COPY docker/backupninja/run.sh /usr/local/bin/ +COPY docker/backupninja/backup.sh /usr/local/bin/ +RUN apk add --no-cache \ + bash \ + borgbackup \ + coreutils \ + flock \ + py3-pip +## Optional packages +#cryptsetup duplicity flashrom gzip hwinfo rdiff-backup restic rsync sfdisk +RUN pip3 install --no-cache-dir \ + rethinkdb diff --git a/docker/backupninja/backup.sh b/docker/backupninja/backup.sh new file mode 100755 index 0000000000..f18393c08d --- /dev/null +++ b/docker/backupninja/backup.sh @@ -0,0 +1,58 @@ +#!/bin/sh +export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes + +if [ "$2" = "db" ]; then + BACKUP_PATH="/backup/db" +elif [ "$2" = "disks" ]; then + BACKUP_PATH="/backup/disks" +else + echo "The second parameter should be db or disks" + exit 1 +fi + +## DISKS +if [ "$BACKUP_DISKS_TEMPLATES_ENABLED" = "false" ]; then + BACKUP_DISKS_TEMPLATES_ENABLED="" +else + BACKUP_DISKS_TEMPLATES_ENABLED="/opt/isard/templates" +fi +if [ "$BACKUP_DISKS_GROUPS_ENABLED" = "false" ]; then + BACKUP_DISKS_GROUPS_ENABLED="" +else + BACKUP_DISKS_GROUPS_ENABLED="/opt/isard/groups" +fi +if [ "$BACKUP_DISKS_MEDIA_ENABLED" = "false" ]; then + BACKUP_DISKS_MEDIA_ENABLED="" +else + BACKUP_DISKS_MEDIA_ENABLED="/opt/isard/media" +fi + +if [ "$1" == "check-integrity" ]; then + borg extract --dry-run --list $BACKUP_PATH::$3 +elif [ "$1" == "list" ]; then + borg list --short $BACKUP_PATH +elif [ "$1" == "info" ]; then + borg info $BACKUP_PATH +elif [ "$1" == "show-files" ]; then + borg list --short $BACKUP_PATH::$3 +elif [ "$1" == "extract" ]; then + cd /backup/extract && borg extract --list $BACKUP_PATH::$3 $4 +elif [ "$1" == "execute-now" ]; then + if [ "$2" = "db" ]; then + rm /dbdump/isard-db*.tar.gz + nice -n 0 \ + /usr/bin/rethinkdb-dump -c "isard-db:28015" -f "/dbdump/isard-db-$(date +%Y-%m-%d_%H:%M:%S).tar.gz" + + nice -n 0 \ + borg create --stats --compression lz4 \ + $BACKUP_PATH::{now:%Y-%m-%dT%H:%M:%S} \ + /dbdump + fi + + if [ "$2" = "disks" ]; then + nice -n 0 \ + borg create --stats --compression lz4 \ + $BACKUP_PATH::{now:%Y-%m-%dT%H:%M:%S} \ + $BACKUP_DISKS_TEMPLATES_ENABLED $BACKUP_DISKS_GROUPS_ENABLED $BACKUP_DISKS_MEDIA_ENABLED + fi +fi \ No newline at end of file diff --git a/docker/backupninja/run.sh b/docker/backupninja/run.sh new file mode 100755 index 0000000000..361a583874 --- /dev/null +++ b/docker/backupninja/run.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +mkdir -p /backup/db +borg init -e none /backup/db > /dev/null 2>&1 +mkdir -p /backup/disks +borg init -e none /backup/disks > /dev/null 2>&1 +mkdir -p /backup/extract + +sed -i '/^logfile =/d' /usr/local/etc/backupninja.conf +echo "logfile = /backup/backupninja.log" >> /usr/local/etc/backupninja.conf + +rm -f /usr/local/etc/backup.d/* + +# BACKUP DB SCRIPT +if [ "$BACKUP_DB_ENABLED" = "true" ] +then + echo "DATABASE ENABLED: Enabled database backup $BACKUP_DB_WHEN with $BACKUP_DB_PRUNE prune policy" + echo " Logs can be found at $BACKUP_DIR/backupninja.log" + + cat <> /usr/local/etc/backup.d/10.info.sh +when = $BACKUP_DB_WHEN + +EOT + + cat <<'EOT' >> /usr/local/etc/backup.d/10.info.sh +echo "----------- NEW DATABASE BACKUP: $(date +%Y-%m-%d_%H:%M:%S) -----------" >> /backup/backupninja.log +EOT + + cat <> /usr/local/etc/backup.d/20.dbdump.sh +when = $BACKUP_DB_WHEN + +rm -f /dbdump/isard-db*.tar.gz +EOT + cat <<'EOT' >> /usr/local/etc/backup.d/20.dbdump.sh +/usr/bin/rethinkdb-dump -c "isard-db:28015" -f "/dbdump/isard-db-$(date +%Y-%m-%d_%H:%M:%S).tar.gz" +EOT + + cat <> /usr/local/etc/backup.d/30.dbborg.borg +when = $BACKUP_DB_WHEN + +[source] +include = /dbdump + +## for more info see : borg prune -h +keep = 0 +prune = yes +prune_options = $BACKUP_DB_PRUNE + +[dest] +directory = /backup/db +host = localhost +port = 22 +user = root +archive = {now:%Y-%m-%dT%H:%M:%S} +compression = lz4 +encryption = none +passphrase = +EOT +fi + +## BACKUP DISKS SCRIPT +if [ "$BACKUP_DISKS_TEMPLATES_ENABLED" = "false" ]; then + BACKUP_DISKS_TEMPLATES_ENABLED="" +else + BACKUP_DISKS_TEMPLATES_ENABLED="include = /opt/isard/templates" +fi +if [ "$BACKUP_DISKS_GROUPS_ENABLED" = "false" ]; then + BACKUP_DISKS_GROUPS_ENABLED="" +else + BACKUP_DISKS_GROUPS_ENABLED="include = /opt/isard/groups" +fi +if [ "$BACKUP_DISKS_MEDIA_ENABLED" = "false" ]; then + BACKUP_DISKS_MEDIA_ENABLED="" +else + BACKUP_DISKS_MEDIA_ENABLED="include = /opt/isard/media" +fi + +if [ "$BACKUP_DISKS_ENABLED" = "true" ] +then + echo "DISKS ENABLED: Enabled disks backup $BACKUP_DISKS_WHEN with $BACKUP_DISKS_PRUNE prune policy" + echo " Disks backup included folders:" + echo " - TEMPLATES: $BACKUP_DISKS_TEMPLATES_ENABLED" + echo " - GROUPS: $BACKUP_DISKS_GROUPS_ENABLED" + echo " - MEDIA: $BACKUP_DISKS_MEDIA_ENABLED" + echo " Logs can be found at $BACKUP_DIR/backupninja.log" + + cat <> /usr/local/etc/backup.d/40.info.sh +when = $BACKUP_DISKS_WHEN + +EOT + cat <<'EOT' >> /usr/local/etc/backup.d/40.info.sh +echo "----------- NEW DISKS BACKUP: $(date +%Y-%m-%d_%H:%M:%S) -----------" >> /backup/backupninja.log +EOT + + cat <> /usr/local/etc/backup.d/50.disksborg.borg +when = $BACKUP_DISKS_WHEN + +[source] +$BACKUP_DISKS_TEMPLATES_ENABLED +$BACKUP_DISKS_GROUPS_ENABLED +$BACKUP_DISKS_MEDIA_ENABLED + +## for more info see : borg prune -h +keep = 0 +prune = yes +prune_options = $BACKUP_DISKS_PRUNE + +[dest] +directory = /backup/disks +host = localhost +port = 22 +user = root +archive = {now:%Y-%m-%dT%H:%M:%S} +compression = lz4 +encryption = none +passphrase = +EOT +fi + +chmod 600 /usr/local/etc/backup.d/* > /dev/null 2>&1 +crond +touch /backup/backupninja.log +tail -f /backup/backupninja.log diff --git a/isardvdi.cfg.example b/isardvdi.cfg.example index 1c3d205563..6ba2d078e1 100644 --- a/isardvdi.cfg.example +++ b/isardvdi.cfg.example @@ -108,8 +108,21 @@ API_HYPERVISORS_SECRET=B5/bUEUzIC+AjNQRmFh3vxR3VeIKirwdeL/xuHPVO+E= #AUTHENTICATION_AUTHENTICATION_GOOGLE_CLIENT_ID=id #AUTHENTICATION_AUTHENTICATION_GOOGLE_CLIENT_SECRET=secret +# ------ Backups ------------------------------------------------------------- +## Automated backups (https://0xacab.org/liberate/backupninja) +#BACKUP_DIR=/opt/isard-local/backup +#BACKUP_DB_ENABLED=false +#BACKUP_DB_WHEN="everyday at 01" +#BACKUP_DB_PRUNE="--keep-weekly=8 --keep-monthly=12 --keep-within=14d --save-space" + +#BACKUP_DISKS_ENABLED=false +#BACKUP_DISKS_WHEN="everyday at 01" +#BACKUP_DISKS_PRUNE="--keep-weekly=4 --keep-monthly=3 --keep-within=7d --save-space" +#BACKUP_DISKS_TEMPLATES_ENABLED=false +#BACKUP_DISKS_GROUPS_ENABLED=false +#BACKUP_DISKS_MEDIA_ENABLED=false ################################################################## ################################################################## -- GitLab