diff --git a/README.md b/README.md index 88fca06..0d573e5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # borg_ynh An experimental borg implementation for yunohost + +## Usage + diff --git a/conf/backup-with-borg.j2 b/conf/backup-with-borg.j2 new file mode 100644 index 0000000..6ee0eac --- /dev/null +++ b/conf/backup-with-borg.j2 @@ -0,0 +1,27 @@ +#!/bin/bash + +filter_hooks() { + ls /usr/share/yunohost/hooks/backup/ /etc/yunohost/hooks.d/backup/ | grep "\-$1_" | cut -d"-" -f2 | uniq +} + +# Backup system part conf +conf=$(yunohost app setting {{ app }} conf) +if [ "$conf" = "true" ] +then + yunohost backup create --ignore-apps -n auto_conf --method {{ app }} --system $(filter_hooks conf) +fi + +# Backup system data +data=$(yunohost app setting {{ app }} data) +if [ "$data" = "true" ] +then + yunohost backup create --ignore-apps -n auto_data --method {{ app }} --system $(filter_hooks data) +fi + +# Backup all apps independently +apps=$(yunohost app setting {{ app }} apps) +for app in $(yunohost app list --installed -b | grep id: | cut -d: -f2); do + if [ "$apps" ?? "$app," ] || [ "$apps" = "all" ]; then + yunohost backup create --ignore-system -n auto_$app --method {{ app }} --apps $app + fi +done diff --git a/conf/backup_method.j2 b/conf/backup_method.j2 new file mode 100644 index 0000000..c660432 --- /dev/null +++ b/conf/backup_method.j2 @@ -0,0 +1,69 @@ +#!/bin/bash + +set -e + +BORG_PASSPHRASE="{{ passphrase }}" +BORG_RSH="ssh -i /root/.ssh/id_{{ app }}_ed25519 -oStrictHostKeyChecking=accept-new " +repo=ssh://{{ ssh_user }}@{{ server }}/~/backup #$4 + +do_need_mount() { + true +} + +do_backup() { + + export BORG_PASSPHRASE + work_dir=$1 + name=$2 + repo=$3 + size=$4 + description=$5 + LOGFILE=/var/log/backup_borg.log + ERRFILE=/var/log/backup_borg.err + current_date=$(date +"%d_%m_%y_%H:%M") + pushd $work_dir + set +e + borg init -e repokey $repo >> $LOGFILE 2>> $ERRFILE + set -e + + borg create $repo::${name}_${current_date} ./ >> $LOGFILE 2>> $ERRFILE + popd + + borg prune $repo -P ${name} --keep-daily=7 --keep-weekly=8 --keep-monthly=12 >> $LOGFILE 2>> $ERRFILE +} + +do_mount() { + export BORG_PASSPHRASE + work_dir=$1 + name=$2 + repo=$3 + size=$4 + description=$5 + LOGFILE=/var/log/backup_borg.log + ERRFILE=/var/log/backup_borg.err + borg mount $repo::$name $work_dir >> $LOGFILE 2>> $ERRFILE +} + +work_dir=$2 +name=$3 + +size=$5 +description=$6 + +case "$1" in + need_mount) + do_need_mount $work_dir $name $repo $size $description + ;; + backup) + do_backup $work_dir $name $repo $size $description + ;; + mount) + do_mount + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/conf/systemd.service b/conf/systemd.service new file mode 100644 index 0000000..212201b --- /dev/null +++ b/conf/systemd.service @@ -0,0 +1,12 @@ +[Unit] +Description=Run backup __APP__ +After=network.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/backup⁻with-__APP__ +User=root +Group=root + +[Install] +WantedBy=multi-user.target diff --git a/conf/systemd.timer.j2 b/conf/systemd.timer.j2 new file mode 100644 index 0000000..f21a951 --- /dev/null +++ b/conf/systemd.timer.j2 @@ -0,0 +1,8 @@ +[Unit] +Description=Run backup {{ app }} regularly + +[Timer] +OnCalendar={{ on_calendar }} + +[Install] +WantedBy=timers.target diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..0648647 --- /dev/null +++ b/manifest.json @@ -0,0 +1,86 @@ +{ + "name": "Borg", + "id": "borg", + "packaging_format": 1, + "description": { + "en": "Backup your server with borg.", + "fr": "Sauvegarder votre serveur avec borg." + }, + "version": "1.0", + "url": "https://borgbackup.readthedocs.io", + "license": "BSD-3-Clause", + "maintainer": { + "name": "ljf", + "email": "ljf+borg_ynh@reflexlibre.net", + "url": "https://reflexlibre.net" + }, + "requirements": { + "yunohost": ">= 2.7.2" + }, + "multi_instance": true, + "services": [], + "arguments": { + "install" : [ + { + "name": "server", + "ask": { + "en": "Indicate the server where you want put your backups", + "fr": "Indiquez le serveur où vous voulez faire vos sauvegardes" + }, + "example": "example.com:22" + }, + { + "name": "ssh_user", + "ask": { + "en": "Indicate the ssh user to use to connect on this server", + "fr": "Indiquez l'utilisateur ssh à utiliser pour se connecter au serveur" + }, + "example": "john" + }, + { + "name": "passphrase", + "type": "password", + "ask": { + "en": "Indicate a strong passphrase, that you will keep preciously if you want to be able to use your backups", + "fr": "Indiquez une phrase de passe forte que vous garderez précieusement si vous voulez être en mesure d'utiliser vos sauvegardes" + } + }, + { + "name": "conf", + "type": "boolean", + "ask": { + "en": "Would you like to backup your YunoHost configuration ?", + "fr": "Souhaitez-vous effectuer des sauvegardes des configurations du système YunoHost ?" + }, + "default": true + }, + { + "name": "data", + "type": "boolean", + "ask": { + "en": "Would you like to backup mails and user home directory ?", + "fr": "Souhaitez-vous effectuer des sauvegardes des mails et des répertoire des utilisateurs ?" + }, + "default": true + }, + { + "name": "apps", + "type": "boolean", + "ask": { + "en": "Which apps would you backup (list separated by comma or 'all') ?", + "fr": "Souhaitez-vous effectuer des sauvegardes de vos applications ?" + }, + "default": "all" + }, + { + "name": "on_calendar", + "ask": { + "en": "Indicate the backup frequency (see systemd OnCalendar format)", + "fr": "Indiquez la fréquence de la sauvegarde (voir le format OnCalendar de systemd)" + }, + "example": "Daily", + "default": "Daily" + } + ] + } +} diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..5059d87 --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +#================================================= +# COMMON VARIABLES +#================================================= +# App package root directory should be the parent folder +PKG_DIR=$(cd ../; pwd) + +pkg_dependencies="python3-pip python3-dev libacl1-dev libssl-dev liblz4-dev" + +#================================================= +# COMMON HELPERS +#================================================= +ynh_export () { + local ynh_arg="" + for var in $@; + do + ynh_arg=$(echo $var | awk '{print toupper($0)}') + if [ "$var" == "path_url" ]; then + ynh_arg="PATH" + fi + ynh_arg="YNH_APP_ARG_$ynh_arg" + export $var=${!ynh_arg} + done +} +# Save listed var in YunoHost app settings +# usage: ynh_save_args VARNAME1 [VARNAME2 [...]] +ynh_save_args () { + for var in $@; + do + local setting_var="$var" + if [ "$var" == "path_url" ]; then + setting_var="path" + fi + ynh_app_setting_set $app $setting_var ${!var} + done +} + +# Render templates with Jinja2 +# +# Attention : Variables should be exported before calling this helper to be +# accessible inside templates. +# +# usage: ynh_render_template some_template output_path +# | arg: some_template - Template file to be rendered +# | arg: output_path - The path where the output will be redirected to +ynh_render_template() { + local template_path=$1 + local output_path=$2 + # Taken from https://stackoverflow.com/a/35009576 + python2.7 -c 'import os, sys, jinja2; sys.stdout.write( + jinja2.Template(sys.stdin.read() + ).render(os.environ));' < $template_path > $output_path +} + +ynh_configure () { + ynh_backup_if_checksum_is_different $2 + ynh_configure "${PKG_DIR}/conf/$1.j2" $2 + ynh_store_file_checksum $2 +} diff --git a/scripts/backup b/scripts/backup new file mode 100755 index 0000000..72fc637 --- /dev/null +++ b/scripts/backup @@ -0,0 +1,30 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# LOAD SETTINGS +#================================================= + +app=$YNH_APP_INSTANCE_NAME + + +ynh_backup "/usr/local/bin/backup-with-$app" +ynh_backup "/etc/systemd/system/$app.service" +ynh_backup "/etc/systemd/system/$app.timer" +ynh_backup "/etc/yunohost/hooks.d/backup_method/05-$app" +ynh_backup "/root/.ssh/id_${app}_ed25519" +ynh_backup "/root/.ssh/id_${app}_ed25519.pub" diff --git a/scripts/install b/scripts/install new file mode 100755 index 0000000..c787ad4 --- /dev/null +++ b/scripts/install @@ -0,0 +1,80 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= +export app=$YNH_APP_INSTANCE_NAME + +# Retrieve arguments +ynh_export server ssh_user passphrase on_calendar conf data apps + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_save_args server ssh_user passphrase on_calendar conf data apps + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_install_app_dependencies $pkg_dependencies +pip3 install setuptools --upgrade +pip3 install borgbackup + +#================================================= +# ACTIVATE BACKUP METHODS +#================================================= +mkdir -p /etc/yunohost/hooks.d/backup_method +mkdir -p /usr/share/yunohost/backup_method + +#================================================= +# SETUP THE BACKUP METHOD +#================================================= +ynh_configure backup_method "/etc/yunohost/hooks.d/backup_method/05-$app" + +#================================================= +# CONFIGURE CRON +#================================================= +ynh_configure backup-with-borg "/usr/local/bin/backup-with-$app" +chmod u+x "/usr/local/bin/backup-with-$app" +ynh_add_systemd_config +ynh_configure systemd.timer "/etc/systemd/system/$app.timer" + +#================================================= +# GENERATE SSH KEY +#================================================= +private_key="/root/.ssh/id_${app}_ed25519" +test -f $private_key || ssh-keygen -q -t ed25519 -N "" -f $private_key + +#================================================= +# SEND A README FOR THE ADMIN +#================================================= +ynh_print_OFF +message="You should now install the \"Borg Server\" app on $server and fill questions like this: +User: ${ssh_suser} +Public key: $(cat ${private_key}.pub) + +Or if you want to use cli: + +yunohost app install https://github.com/YunoHost-Apps/borg_server_ynh -a \"ssh_user=$(ssh_user)&public_key=$(cat ${private_key}.pub)\" + +If you facing an issue or want to improve this app, please open a new issue in this project: https://github.com/YunoHost-Apps/borg_ynh" + +ynh_send_readme_to_admin "$message" "root" +ynh_print_ON + diff --git a/scripts/remove b/scripts/remove new file mode 100755 index 0000000..b8cc3e6 --- /dev/null +++ b/scripts/remove @@ -0,0 +1,29 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# REMOVE DEPENDENCIES +#================================================= +ynh_remove_app_dependencies + +#================================================= +# REMOVE FILES +#================================================= +ynh_remove_systemd_config +ynh_secure_remove "/etc/systemd/system/$app.timer" +ynh_secure_remove "/usr/local/bin/backup-with-$app" +ynh_secure_remove "/etc/yunohost/hooks.d/backup_method/05-$app" diff --git a/scripts/restore b/scripts/restore new file mode 100755 index 0000000..49d1150 --- /dev/null +++ b/scripts/restore @@ -0,0 +1,60 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +if [ ! -e _common.sh ]; then + # Get the _common.sh file if it's not in the current directory + cp ../settings/scripts/_common.sh ./_common.sh + chmod a+rx _common.sh +fi +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# LOAD SETTINGS +#================================================= + +app=$YNH_APP_INSTANCE_NAME + +server=$(ynh_app_setting_get $app server) +ssh_user=$(ynh_app_setting_get $app ssh_user) + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_install_app_dependencies $pkg_dependencies +pip3 install setuptools --upgrade +pip3 install borgbackup + +#================================================= +# ACTIVATE BACKUP METHODS +#================================================= +mkdir -p /etc/yunohost/hooks.d/backup_method +mkdir -p /usr/share/yunohost/backup_method + +#================================================= +# RESTORE FILES +#================================================= +ynh_restore + +#================================================= +# ADVERTISE SERVICE IN ADMIN PANEL +#================================================= +yunohost service add $app + +#================================================= +# RESTORE SYSTEMD +#================================================= +systemctl enable $app.service + diff --git a/scripts/upgrade b/scripts/upgrade new file mode 100755 index 0000000..35d72d2 --- /dev/null +++ b/scripts/upgrade @@ -0,0 +1,30 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= + +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors +