From 099f0568b8cd5eb623495665f98210933cd93791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= Date: Sat, 25 May 2024 09:34:34 +0200 Subject: [PATCH] Manifest v2 --- conf/check-restic.j2 | 6 +- conf/sudoer | 1 + conf/systemd_check.service | 2 +- conf/systemd_check_read_data.service | 2 +- doc/POST_INSTALL.md | 16 +++ manifest.json | 160 --------------------- manifest.toml | 133 +++++++++++++++++ scripts/_common.sh | 192 ++++++++++--------------- scripts/backup | 52 +++---- scripts/install | 208 +++++++-------------------- scripts/remove | 68 +++------ scripts/restore | 60 ++++---- scripts/upgrade | 59 +++----- tests.toml | 38 +++++ 14 files changed, 400 insertions(+), 597 deletions(-) create mode 100644 conf/sudoer create mode 100644 doc/POST_INSTALL.md delete mode 100644 manifest.json create mode 100644 manifest.toml create mode 100644 tests.toml diff --git a/conf/check-restic.j2 b/conf/check-restic.j2 index 0dc1551..ad67eea 100644 --- a/conf/check-restic.j2 +++ b/conf/check-restic.j2 @@ -20,13 +20,13 @@ CHECK_READ_DATA=${1:-0} # Check system part conf conf=$(sudo yunohost app setting {{ app }} conf) if [ $conf -eq 1 ];then - sudo {{final_path}}/check_method_{{ app }} auto_conf ${CHECK_READ_DATA} + sudo {{install_dir}}/check_method_{{ app }} auto_conf ${CHECK_READ_DATA} fi # Check system data data=$(sudo yunohost app setting {{ app }} data) if [ $data -eq 1 ];then - sudo {{final_path}}/check_method_{{ app }} auto_data ${CHECK_READ_DATA} + sudo {{install_dir}}/check_method_{{ app }} auto_data ${CHECK_READ_DATA} fi # Check all apps independently @@ -40,7 +40,7 @@ for app in $(sudo /usr/bin/find /etc/yunohost/apps -name backup | cut -d / -f 5) fi done if [ "$check_app" == "true" ];then - sudo {{final_path}}/check_method_{{ app }} auto_${app} ${CHECK_READ_DATA} + sudo {{install_dir}}/check_method_{{ app }} auto_${app} ${CHECK_READ_DATA} fi done rm "$LOCK_FILE" diff --git a/conf/sudoer b/conf/sudoer new file mode 100644 index 0000000..041b40d --- /dev/null +++ b/conf/sudoer @@ -0,0 +1 @@ +__APP__ ALL=(root) NOPASSWD: /usr/bin/yunohost*, /bin/journalctl*, /usr/bin/find /etc/yunohost/apps -name backup, __INSTALL_DIR__/check_method___APP__ diff --git a/conf/systemd_check.service b/conf/systemd_check.service index 0b21ab5..41652bc 100644 --- a/conf/systemd_check.service +++ b/conf/systemd_check.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=__FINALPATH__/check-__APP__ +ExecStart=__INSTALL_DIR__/check-__APP__ ExecStartPost=/opt/yunohost/__APP__/restic_check_log___APP__ 0 User=__APP__ Group=__APP__ diff --git a/conf/systemd_check_read_data.service b/conf/systemd_check_read_data.service index 7cad957..0fb4135 100644 --- a/conf/systemd_check_read_data.service +++ b/conf/systemd_check_read_data.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=oneshot -ExecStart=__FINALPATH__/check-__APP__ "1" +ExecStart=__INSTALL_DIR__/check-__APP__ "1" ExecStartPost=/opt/yunohost/__APP__/restic_check_log___APP__ 1 User=__APP__ Group=__APP__ diff --git a/doc/POST_INSTALL.md b/doc/POST_INSTALL.md new file mode 100644 index 0000000..d2225ab --- /dev/null +++ b/doc/POST_INSTALL.md @@ -0,0 +1,16 @@ +You should now allow the following public key for user __SSH_USER__ on server __SERVER__: + +__PUBLIC_KEY__ + +Do so by running those commands on __SERVER__ with user __SSH_USER__: + +mkdir ~/.ssh 2>/dev/null +touch ~/.ssh/authorized_keys +chmod u=rw,go= ~/.ssh/authorized_keys +cat << EOPKEY >> ~/.ssh/authorized_keys +__PUBLIC_KEY__ +EOPKEY + +Also make sure __BACKUP_PATH__ exists and is writable by __SSH_USER__ + +If you're facing an issue or want to improve this app, please open a new issue in this project: diff --git a/manifest.json b/manifest.json deleted file mode 100644 index 41e28c8..0000000 --- a/manifest.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "name": "Restic", - "id": "restic", - "packaging_format": 1, - "description": { - "en": "Backup your server with Restic", - "fr": "Sauvegardez votre serveur avec Restic" - }, - "version": "0.12.0~ynh9", - "url": "https://restic.net/", - "upstream": { - "license": "BSD-2-Clause", - "website": "https://restic.net", - "admindoc": "https://restic.readthedocs.io/en/latest/", - "code": "https://github.com/restic/restic" - }, - "license": "BSD-2-Clause", - "maintainer": { - "name": "Lionel Coupouchetty-Ramouchetty", - "email": "restic-ynh@coupouchetty-ramouchetty.fr", - "url": "https://gnoobix.net" - }, - "requirements": { - "yunohost": ">= 11.2" - }, - "multi_instance": true, - "services": [], - "arguments": { - "install" : [ - { - "name": "server", - "type": "string", - "ask": { - "en": "Indicate the server where you want put your backups", - "fr": "Indiquez le serveur où vous voulez faire vos sauvegardes" - }, - "help":{ - "en": "IP address or resolvable hostname of your destination server", - "fr": "Adresse IP ou nom résolvable de votre serveur de destination" - }, - "example": "example.com" - }, - { - "name": "port", - "type": "string", - "ask": { - "en": "sftp port of your server", - "fr": "Le port sftp de votre serveur" - }, - "help":{ - "en": "Listening port of your sftp or ssh server. The default value is 22", - "fr": "Le port d'écoute de votre serveur sftp ou ssh. La valeur par défaut est 22" - }, - "example": "22", - "default": "22" - }, - { - "name": "backup_path", - "type": "string", - "ask": { - "en": "The directory where you want your backup repositories to be created in", - "fr": "Le répertoire dans lequel les dépôts restic seront créés" - }, - "help":{ - "en": "A complete or relative path to an existing directory on the remote server writable by the remote backup user. Defaults to the login directory", - "fr": "Un chemin complet ou relatif vers un répertoire existant sur le serveur distant et accessible en écriture au compte utilisé pour la sauvegarde. Répertoire d'accueil par défaut" - }, - "example": "./backups", - "default": "." - }, - { - "name": "ssh_user", - "type": "string", - "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": "string", - "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": "allow_extra_space_use", - "type": "boolean", - "ask": { - "en": "Allow backup method to temporarily use more space?", - "fr": "Permettre à la sauvegarde de consommer temporairement de l'espace supplémentaire?" - }, - "help":{ - "en": "Some applications as Gitlab can't be backed up with the standard method and require extra space temporarily", - "fr": "Certaines applications comme Gitlab ne peuvent être sauvegardées avec la méthode standard et nécessitent d'utiliser plus d'espace disque temporairement" - }, - "default": true - }, - { - "name": "on_calendar", - "type": "string", - "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": "*-*-* 0:15:00" - }, - { - "name": "check_on_calendar", - "type": "string", - "ask": { - "en": "Indicate the backup check frequency (see systemd OnCalendar format)", - "fr": "Indiquez la fréquence de vérification de la sauvegarde (voir le format OnCalendar de systemd)" - }, - "example": "Tue *-*-* 00:15:00", - "default": "Sat *-*-8..31 3:15:00" - }, - { - "name": "check_read_data_on_calendar", - "type": "string", - "ask": { - "en": "Indicate the complete backup check frequency (see systemd OnCalendar format)", - "fr": "Indiquez la fréquence de vérification complète de la sauvegarde (voir le format OnCalendar de systemd)" - }, - "example": "Tue *-*-* 00:15:00", - "default": "Sat *-*-1..7 3:15:00" - } - ] - } -} diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..61758a0 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,133 @@ +#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json + +packaging_format = 2 + +id = "restic" +name = "Restic" +description.en = "Backup your server with Restic" +description.fr = "Sauvegardez votre serveur avec Restic" + +version = "0.12.0~ynh9" + +maintainers = ["Lionel Coupouchetty-Ramouchetty"] + +[upstream] +license = "BSD-2-Clause" +website = "https://restic.net" +admindoc = "https://restic.readthedocs.io/en/latest/" +code = "https://github.com/restic/restic" + +[integration] +yunohost = ">= 11.2" +architectures = "all" +multi_instance = true +ldap = "not_relevant" +sso = "not_relevant" +disk = "50M" # FIXME: replace with an **estimate** minimum disk requirement. e.g. 20M, 400M, 1G, ... +ram.build = "50M" # FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ... +ram.runtime = "50M" # FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ... + +[install] + [install.server] + ask.en = "Indicate the server where you want put your backups" + ask.fr = "Indiquez le serveur où vous voulez faire vos sauvegardes" + help.en = "IP address or resolvable hostname of your destination server" + help.fr = "Adresse IP ou nom résolvable de votre serveur de destination" + type = "string" + example = "example.com" + + [install.port] + ask.en = "sftp port of your server" + ask.fr = "Le port sftp de votre serveur" + help.en = "Listening port of your sftp or ssh server. The default value is 22" + help.fr = "Le port d'écoute de votre serveur sftp ou ssh. La valeur par défaut est 22" + type = "string" + example = "22" + default = "22" + + [install.backup_path] + ask.en = "The directory where you want your backup repositories to be created in" + ask.fr = "Le répertoire dans lequel les dépôts restic seront créés" + help.en = "A complete or relative path to an existing directory on the remote server writable by the remote backup user. Defaults to the login directory" + help.fr = "Un chemin complet ou relatif vers un répertoire existant sur le serveur distant et accessible en écriture au compte utilisé pour la sauvegarde. Répertoire d'accueil par défaut" + type = "string" + example = "./backups" + default = "." + + [install.ssh_user] + ask.en = "Indicate the ssh user to use to connect on this server" + ask.fr = "Indiquez l'utilisateur ssh à utiliser pour se connecter au serveur" + type = "string" + example = "john" + + [install.passphrase] + ask.en = "Indicate a strong passphrase, that you will keep preciously if you want to be able to use your backups" + ask.fr = "Indiquez une phrase de passe forte que vous garderez précieusement si vous voulez être en mesure d'utiliser vos sauvegardes" + type = "password" + + [install.conf] + ask.en = "Would you like to backup your YunoHost configuration ?" + ask.fr = "Souhaitez-vous effectuer des sauvegardes des configurations du système YunoHost ?" + type = "boolean" + default = true + + [install.data] + ask.en = "Would you like to backup mails and user home directory ?" + ask.fr = "Souhaitez-vous effectuer des sauvegardes des mails et des répertoire des utilisateurs ?" + type = "boolean" + default = true + + [install.apps] + ask.en = "Which apps would you backup (list separated by comma or 'all') ?" + ask.fr = "Souhaitez-vous effectuer des sauvegardes de vos applications ?" + type = "string" + default = "all" + + [install.allow_extra_space_use] + ask.en = "Allow backup method to temporarily use more space?" + ask.fr = "Permettre à la sauvegarde de consommer temporairement de l'espace supplémentaire?" + help.en = "Some applications as Gitlab can't be backed up with the standard method and require extra space temporarily" + help.fr = "Certaines applications comme Gitlab ne peuvent être sauvegardées avec la méthode standard et nécessitent d'utiliser plus d'espace disque temporairement" + type = "boolean" + default = true + + [install.on_calendar] + ask.en = "Indicate the backup frequency (see systemd OnCalendar format)" + ask.fr = "Indiquez la fréquence de la sauvegarde (voir le format OnCalendar de systemd)" + type = "string" + example = "Daily" + default = "*-*-* 0:15:00" + + [install.check_on_calendar] + ask.en = "Indicate the backup check frequency (see systemd OnCalendar format)" + ask.fr = "Indiquez la fréquence de vérification de la sauvegarde (voir le format OnCalendar de systemd)" + type = "string" + example = "Tue *-*-* 00:15:00" + default = "Sat *-*-8..31 3:15:00" + + [install.check_read_data_on_calendar] + ask.en = "Indicate the complete backup check frequency (see systemd OnCalendar format)" + ask.fr = "Indiquez la fréquence de vérification complète de la sauvegarde (voir le format OnCalendar de systemd)" + type = "string" + example = "Tue *-*-* 00:15:00" + default = "Sat *-*-1..7 3:15:00" + +[resources] + [resources.sources.main] + amd64.url = "https://github.com/restic/restic/releases/download/v0.16.2/restic_0.16.2_linux_amd64.bz2" + amd64.sha256 = "dae5e6e39107a66dc5c8ea59f6f27b16c54bd6be31f57e3281f6d87de30e05b0" + i386.url = "https://github.com/restic/restic/releases/download/v0.16.2/restic_0.16.2_linux_386.bz2" + i386.sha256 = "692e70ade358ad4fe19f0cd5fbaf21c3830d0f23c3d4e491a043f6cbc1b5cf59" + arm64.url = "https://github.com/restic/restic/releases/download/v0.16.2/restic_0.16.2_linux_arm64.bz2" + arm64.sha256 = "efdd75eb5c12af6fec4189aa57dc777035a87dd57204daa52293901199569157" + armhf.url = "https://github.com/restic/restic/releases/download/v0.16.2/restic_0.16.2_linux_arm.bz2" + armhf.sha256 = "60376b01b334a0cee3a59016f44dde8b336de2b6aa44f1e6e403d307990c47a0" + + in_subdir = false + rename = "restic" + + [resources.system_user] + + [resources.install_dir] + + [resources.permissions] diff --git a/scripts/_common.sh b/scripts/_common.sh index d3f701e..3ad5289 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -3,132 +3,88 @@ #================================================= # COMMON VARIABLES #================================================= -# App package root directory should be the parent folder -PKG_DIR=$(cd ../; pwd) + RESTIC_VERSION="0.16.2" +systemd_services_suffixes=( "" "_check" "_check_read_data" ) + # Install restic if restic is not here install_restic () { - architecture=$(uname -m) - arch='' - case $architecture in - i386|i686) - arch="386" - ;; - x86_64) - arch=amd64 - ;; - armv*) - arch=arm - ;; - aarch64) - arch=arm64 - ;; - *) - echo - ynh_die --message="Unsupported architecture \"$architecture\"" - ;; - esac - wget https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_${arch}.bz2 -O /tmp/restic.bz2 2>&1 >/dev/null - wget https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/SHA256SUMS -O /tmp/restic-sha256sums 2>&1 >/dev/null - expected_sum=$(grep restic_${RESTIC_VERSION}_linux_${arch}.bz2 /tmp/restic-sha256sums | awk '{print $1}') - sum=$(sha256sum /tmp/restic.bz2 | awk '{print $1}') - if [ "$sum" == "$expected_sum" ];then - pkill restic || true - bunzip2 /tmp/restic.bz2 -f -c > /usr/local/bin/${app} - chmod +x /usr/local/bin/${app} - else - ynh_die --message="\nDownloaded file does not match expected sha256 sum, aborting" - fi + ynh_setup_source --source_id=main --dest_dir="$install_dir" + chmod +x "$install_dir/restic" } +_gen_and_save_public_key() { + public_key="" + + if [[ -n "$server" ]]; then + private_key="/root/.ssh/id_${app}_ed25519" + if [ ! -f "$private_key" ]; then + ssh-keygen -q -t ed25519 -N "" -f "$private_key" + fi + public_key=$(cat "$private_key.pub") + fi + + ynh_app_setting_set --app="$app" --key=public_key --value="$public_key" +} + +_set_ssh_config() { + if grep -q "$app" "/root/.ssh/config" 2>/dev/null; then + return 0 + fi + + cat << EOCONF >> /root/.ssh/config +# begin $app ssh config +Host ${server} + Hostname ${server} + Port ${port} + User ${ssh_user} + IdentityFile ${private_key} + StrictHostKeyChecking no + UserKnownHostsFile /dev/null +# end $app ssh config +EOCONF + +} + + #================================================= # 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" + +_ynh_add_config_j2() { + # Declare an array to define the options of this helper. + local legacy_args=tdv + local -A args_array=([t]=template= [d]=destination=) + local template + local destination + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local template_path + + if [ -f "$YNH_APP_BASEDIR/conf/$template" ]; then + template_path="$YNH_APP_BASEDIR/conf/$template" + elif [ -f "$template" ]; then + template_path=$template + else + ynh_die --message="The provided template $template doesn't exist" fi - ynh_arg="YNH_APP_ARG_$ynh_arg" - export $var="${!ynh_arg}" - done + + ynh_backup_if_checksum_is_different --file="$destination" + + # Make sure to set the permissions before we copy the file + # This is to cover a case where an attacker could have + # created a file beforehand to have control over it + # (cp won't overwrite ownership / modes by default...) + touch $destination + chown root:root $destination + chmod 640 $destination + + cp -f "$template_path" "$destination" + + _ynh_apply_default_permissions $destination + + ynh_replace_vars --file="$destination" + + ynh_store_file_checksum --file="$destination" } -# 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 -} - -ynh_configure () { - ynh_backup_if_checksum_is_different $2 - ynh_render_template "${PKG_DIR}/conf/$1.j2" "$2" - ynh_store_file_checksum $2 -} - -# Send an email to inform the administrator -# -# usage: ynh_send_readme_to_admin app_message [recipients] -# | arg: app_message - The message to send to the administrator. -# | arg: recipients - The recipients of this email. Use spaces to separate multiples recipients. - default: root -# example: "root admin@domain" -# If you give the name of a YunoHost user, ynh_send_readme_to_admin will find its email adress for you -# example: "root admin@domain user1 user2" -ynh_send_readme_to_admin() { - local app_message="${1:-...No specific information...}" - local recipients="${2:-root}" - - # Retrieve the email of users - find_mails () { - local list_mails="$1" - local mail - local recipients=" " - # Read each mail in argument - for mail in $list_mails - do - # Keep root or a real email address as it is - if [ "$mail" = "root" ] || echo "$mail" | grep --quiet "@" - then - recipients="$recipients $mail" - else - # But replace an user name without a domain after by its email - if mail=$(ynh_user_get_info "$mail" "mail" 2> /dev/null) - then - recipients="$recipients $mail" - fi - fi - done - echo "$recipients" - } - recipients=$(find_mails "$recipients") - - local mail_subject="☁️🆈🅽🅷☁️: \`$app\` was just installed!" - - local mail_message="This is an automated message from your beloved YunoHost server. -Specific information for the application $app. -$app_message ---- -Automatic diagnosis data from YunoHost -$(yunohost tools diagnosis | grep -B 100 "services:" | sed '/services:/d')" - - # Define binary to use for mail command - if [ -e /usr/bin/bsd-mailx ] - then - local mail_bin=/usr/bin/bsd-mailx - else - local mail_bin=/usr/bin/mail.mailutils - fi - - # Send the email to the recipients - echo "$mail_message" | $mail_bin -a "Content-Type: text/plain; charset=UTF-8" -s "$mail_subject" "$recipients" -} \ No newline at end of file diff --git a/scripts/backup b/scripts/backup index 9e62a26..b15d50c 100755 --- a/scripts/backup +++ b/scripts/backup @@ -1,7 +1,5 @@ #!/bin/bash -#================================================= -# GENERIC START #================================================= # IMPORT GENERIC HELPERS #================================================= @@ -10,45 +8,33 @@ source ../settings/scripts/_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 -#================================================= -ynh_print_info --message="Loading installation settings..." - -export app=$YNH_APP_INSTANCE_NAME -export final_path="/opt/yunohost/${app}" - #================================================= # DECLARE DATA AND CONF FILES TO BACKUP #================================================= ynh_print_info --message="Declaring files to be backed up..." - #================================================= -# BACKUP VARIOUS FILES +# BACKUP THE APP MAIN DIR #================================================= -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/systemd/system/${app}_check.service" -ynh_backup "/etc/systemd/system/${app}_check.timer" -ynh_backup "/etc/systemd/system/${app}_check_read_data.service" -ynh_backup "/etc/systemd/system/${app}_check_read_data.timer" -ynh_backup "/etc/yunohost/hooks.d/backup_method/05-${app}_app" -ynh_backup "${final_path}/check_method_${app}" -ynh_backup "${final_path}/restic_log_${app}" -ssh_dir="/root/.ssh" -ynh_backup "${ssh_dir}/id_${app}_ed25519" -ynh_backup "${ssh_dir}/id_${app}_ed25519.pub" -ynh_backup "${ssh_dir}/config" +ynh_backup --src_path="$install_dir" + +#================================================= +# SYSTEM CONFIGURATION +#================================================= + +for suffix in "${systemd_services_suffixes[@]}"; do + ynh_backup --src_path="/etc/systemd/system/$app$suffix.timer" + ynh_backup --src_path="/etc/systemd/system/$app$suffix.service" +done + +ynh_backup --src_path="/etc/yunohost/hooks.d/backup_method/05-${app}_app" +ynh_backup --src_path="/etc/sudoers.d/$app" + +ynh_backup --src_path="/root/.ssh/id_${app}_ed25519" +ynh_backup --src_path="/root/.ssh/id_${app}_ed25519.pub" +# FIXME: uh do we really want to backup it all? +ynh_backup --src_path="/root/.ssh/config" #================================================= # END OF SCRIPT diff --git a/scripts/install b/scripts/install index 12e922f..e7e2f6c 100755 --- a/scripts/install +++ b/scripts/install @@ -1,7 +1,5 @@ #!/bin/bash -#================================================= -# GENERIC START #================================================= # IMPORT GENERIC HELPERS #================================================= @@ -9,188 +7,84 @@ source _common.sh source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - ynh_clean_check_starting -} -# 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 -export final_path="/opt/yunohost/${app}" - -# Retrieve arguments -ynh_export server port ssh_user backup_path passphrase on_calendar check_on_calendar check_read_data_on_calendar conf data apps allow_extra_space_use - #================================================= # STORE SETTINGS FROM MANIFEST #================================================= -ynh_script_progression --message="Storing installation settings..." -ynh_save_args server port ssh_user backup_path passphrase on_calendar check_on_calendar check_read_data_on_calendar conf data apps allow_extra_space_use +# passwords aren't saved by default +ynh_app_setting_set --app=$app --key=passphrase --value="$passphrase" #================================================= # INSTALL RESTIC #================================================= -ynh_script_progression --message="Installing restic binary" --weight=7 +ynh_script_progression --message="Installing Restic..." --weight=7 install_restic -#================================================= -# CREATE APP USER -#================================================= -ynh_script_progression --message="Creating user ${app}" +_gen_and_save_public_key -useradd -m ${app} - -ynh_script_progression --message="Configure ${app} user sudoer rights" - -cat > /tmp/${app}_sudoer << EOSUDOER -${app} ALL = (root) NOPASSWD: /usr/bin/yunohost*, /bin/journalctl*, /usr/bin/find /etc/yunohost/apps -name backup, ${final_path}/check_method_${app} -EOSUDOER -visudo -cf /tmp/${app}_sudoer && mv /tmp/${app}_sudoer /etc/sudoers.d/${app} - -#================================================= -# ACTIVATE BACKUP METHODS -#================================================= -ynh_script_progression --message="Activating backup methods" - -mkdir -p /etc/yunohost/hooks.d/backup_method -mkdir -p /usr/share/yunohost/backup_method +_set_ssh_config #================================================= # SETUP THE BACKUP METHOD #================================================= -ynh_script_progression --message="Setting up backup methods" +ynh_script_progression --message="Setting up backup method..." --weight=1 -ynh_configure backup_method "/etc/yunohost/hooks.d/backup_method/05-${app}_app" -ynh_configure check_method "${final_path}/check_method_${app}" +mkdir -p /etc/yunohost/hooks.d/backup +mkdir -p /etc/yunohost/hooks.d/backup_method +mkdir -p /usr/share/yunohost/backup_method + +## Backup method +_ynh_add_config_j2 --template="backup_method" --destination="/etc/yunohost/hooks.d/backup_method/05-${app}_app" +chmod go=--- "/etc/yunohost/hooks.d/backup_method/05-${app}_app" + +_ynh_add_config_j2 --template="backup-with-restic" --destination="$install_dir/backup-with-${app}" +chmod u+x "$install_dir/backup-with-restic" + +## Check method +_ynh_add_config_j2 --template="check_method" --destination="$install_dir/check_method_${app}" + +_ynh_add_config_j2 --template="check-restic" --destination="$install_dir/check-${app}" +chmod u+x "$install_dir/check-$app" + +## Backup log script +_ynh_add_config_j2 --template="restic_log" --destination="${install_dir}/restic_log_${app}" +chmod u+x "$install_dir/restic_log_${app}" + +# Check log script +_ynh_add_config_j2 --template="restic_check_log" --destination="${install_dir}/restic_check_log_${app}" +chmod u+x "$install_dir/restic_check_log_${app}" + +chown -R "$app:$app" "$install_dir" #================================================= -# SETUP LOG SCRIPTS +# SYSTEM CONFIGURATION #================================================= -ynh_script_progression --message="Setting up backup log script" +ynh_script_progression --message="Adding system configurations related to $app..." --weight=1 -ynh_configure restic_log "${final_path}/restic_log_${app}" -chmod +x "${final_path}/restic_log_${app}" -chown ${app}: "${final_path}/restic_log_${app}" +# Systemd services and timers +for suffix in "${systemd_services_suffixes[@]}"; do + ynh_add_systemd_config --service="$app$suffix" --template="systemd$suffix.service" + _ynh_add_config_j2 --template="systemd$suffix.timer" --destination="/etc/systemd/system/$app$suffix.timer" + systemctl disable --quiet "${app}$suffix.service" + systemctl enable --quiet "${app}$suffix.time" + systemctl start --quiet "${app}$suffix.time" -ynh_script_progression --message="Setting up check log script" -ynh_configure restic_check_log "${final_path}/restic_check_log_${app}" -chmod +x "${final_path}/restic_check_log_${app}" -chown ${app}: "${final_path}/restic_check_log_${app}" + yunohost service add "$app$suffix" --description="Restic backup program ($app$suffix)" \ + --test_status="systemctl show $app$suffix.service -p ActiveState --value | grep -v failed" +done -#================================================= -# CONFIGURE CRON -#================================================= -ynh_script_progression --message="Configuring cron" --weight=5 +ynh_add_config --template="sudoer" --destination="/etc/sudoers.d/$app" +chown root:root "/etc/sudoers.d/$app" -ynh_configure backup-with-restic "/usr/local/bin/backup-with-${app}" -ynh_configure check-restic "${final_path}/check-${app}" -chmod +x "/usr/local/bin/backup-with-${app}" -chown ${app}: "/usr/local/bin/backup-with-${app}" -chmod +x "${final_path}/check-${app}" -chmod +x "${final_path}/check_method_${app}" -ynh_add_systemd_config --service=${app} --template=systemd.service -ynh_add_systemd_config --service=${app}_check --template=systemd_check.service -ynh_add_systemd_config --service=${app}_check_read_data --template=systemd_check_read_data.service -ynh_configure systemd.timer "/etc/systemd/system/${app}.timer" -ynh_configure systemd_check.timer "/etc/systemd/system/${app}_check.timer" -ynh_configure systemd_check_read_data.timer "/etc/systemd/system/${app}_check_read_data.timer" -systemctl disable --quiet ${app}.service -systemctl disable --quiet ${app}_check.service -systemctl disable --quiet ${app}_check_read_data.service -systemctl enable --quiet ${app}.timer -systemctl enable --quiet ${app}_check.timer -systemctl enable --quiet ${app}_check_read_data.timer -systemctl start ${app}.timer -systemctl start ${app}_check.timer -systemctl start ${app}_check_read_data.timer +ynh_use_logrotate --logfile="/var/log/restic_backup_${app}.log" +ynh_use_logrotate --logfile="/var/log/restic_backup_${app}.err" +ynh_use_logrotate --logfile="/var/log/restic_check_${app}.log" +ynh_use_logrotate --logfile="/var/log/restic_check_${app}.err" -#================================================= -# SET PERMISSIONS ON FINAL PATH -#================================================= -ynh_script_progression --message="Set permissions on ${final_path}" - -chown -R ${app}: ${final_path} - -#================================================= -# SETUP LOGROTATE -#================================================= -ynh_script_progression --message="Configuring logrotate" - -ynh_use_logrotate --logfile=/var/log/restic_backup_${app}.log -ynh_use_logrotate --logfile=/var/log/restic_backup_${app}.err -ynh_use_logrotate --logfile=/var/log/restic_check_${app}.log -ynh_use_logrotate --logfile=/var/log/restic_check_${app}.err - -#================================================= -# GENERATE SSH KEY -#================================================= -ynh_script_progression --message="Generating private key" - -ssh_dir="/root/.ssh" -if [ ! -d "${ssh_dir}" ];then - mkdir -p "${ssh_dir}" -fi -private_key="${ssh_dir}/id_${app}_ed25519" -test -f $private_key || ssh-keygen -q -t ed25519 -N "" -f $private_key - -#================================================= -# GENERATE SSH CONFIG -#================================================= -ynh_script_progression --message="Generating ssh config for ${app} server ${server}" - -grep -q "${app}" ${ssh_dir}/config 2>/dev/null || cat << EOCONF >> ${ssh_dir}/config -# begin $app ssh config -Host ${server} - Hostname ${server} - Port ${port} - User ${ssh_user} - IdentityFile ${private_key} - StrictHostKeyChecking no - UserKnownHostsFile /dev/null -# end $app ssh config -EOCONF - -#================================================= -# Display key -#================================================= -ynh_script_progression --message="You should now allow the following public key for user ${ssh_user} on server ${server}: - -$(cat ${private_key}.pub)" - -#================================================= -# SEND A README FOR THE ADMIN -#================================================= -ynh_script_progression --message="Sending post-installation instructions to admin" --last - -message="You should now allow the following public key for user ${ssh_user} on server ${server}: -$(cat ${private_key}.pub) - -Do so by running those commands on ${server} with user ${ssh_user}: - -mkdir ~/.ssh 2>/dev/null -touch ~/.ssh/authorized_keys -chmod u=rw,go= ~/.ssh/authorized_keys -cat << EOPKEY >> ~/.ssh/authorized_keys -$(cat ${private_key}.pub) -EOPKEY - -$(if [ "$backup_path" != "./" ];then echo "Also make sure ${backup_path} exists and is writable by ${ssh_user}";fi) - -If you're facing an issue or want to improve this app, please open a new issue in this project: https://github.com/YunoHost-Apps/restic_ynh" - -ynh_send_readme_to_admin "$message" "root" #================================================= # END OF SCRIPT #================================================= -ynh_script_progression --message="Installation of $app completed" +ynh_script_progression --message="Installation of $app completed" --last diff --git a/scripts/remove b/scripts/remove index 9d2927e..b69166d 100755 --- a/scripts/remove +++ b/scripts/remove @@ -1,7 +1,5 @@ #!/bin/bash -#================================================= -# GENERIC START #================================================= # IMPORT GENERIC HELPERS #================================================= @@ -9,71 +7,37 @@ source _common.sh source /usr/share/yunohost/helpers -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME -export final_path="/opt/yunohost/${app}" #================================================= -# REMOVE LOGROTATE CONFIGURATION +# REMOVE SYSTEM CONFIGURATIONS #================================================= -ynh_script_progression --message="Removing logrotate configuration..." +ynh_script_progression --message="Removing system configurations related to $app..." --weight=1 + +# Systemd services and timers +for suffix in "${systemd_services_suffixes[@]}"; do + if ynh_exec_warn_less yunohost service status "$app_suffix" >/dev/null; then + yunohost service remove "$app_suffix" + fi + systemctl stop "$app$suffix.timer" + systemctl --quiet disable "$app$suffix.timer" + ynh_remove_systemd_config --service="$app$suffix" + ynh_secure_remove "/etc/systemd/system/$app$suffix.timer" +done # Remove the app-specific logrotate config ynh_remove_logrotate -#================================================= -# REMOVE DEPENDENCIES -#================================================= -ynh_script_progression --message="Removing dependencies..." --weight=4 +# Remove sudoers +rm "/etc/sudoers.d/$app" -# Remove metapackage and its dependencies -ynh_remove_app_dependencies - -#================================================= -# SPECIFIC REMOVE -#================================================= -# REMOVE VARIOUS FILES -#================================================= -ynh_script_progression --message="Removing various files..." --weight=2 - -systemctl stop ${app}.timer -systemctl --quiet disable ${app}.timer -ynh_remove_systemd_config --service=${app} -ynh_remove_systemd_config --service=${app}_check -ynh_remove_systemd_config --service=${app}_check_read_data -ynh_secure_remove "/etc/systemd/system/${app}.timer" -ynh_secure_remove "/etc/systemd/system/${app}_check.timer" -ynh_secure_remove "/etc/systemd/system/${app}_check_read_data.timer" -ynh_secure_remove "/usr/local/bin/backup-with-${app}" ynh_secure_remove "/etc/yunohost/hooks.d/backup_method/05-${app}_app" -ynh_secure_remove "${final_path}/check_method_${app}" -ynh_secure_remove "${final_path}/check-${app}" -ynh_secure_remove "${final_path}/restic_log_${app}" -ynh_secure_remove "${final_path}/restic_check_log_${app}" -ynh_secure_remove "${final_path}" #================================================= # REMOVE SSH CONFIG #================================================= ynh_script_progression --message="Removing ssh config" -ssh_dir="/root/.ssh" -sed -e "/begin ${app}/,/end ${app}/{/.*/d}" ${ssh_dir}/config -i || true - -#================================================= -# GENERIC FINALIZATION -#================================================= -# REMOVE DEDICATED USER -#================================================= -ynh_script_progression --message="Removing sudoers rights for user ${app}" -rm /etc/sudoers.d/${app} - -ynh_script_progression --message="Removing ${app} user" --last -userdel ${app} +sed -e "/begin ${app}/,/end ${app}/{/.*/d}" /root/.ssh/config -i || true #================================================= # END OF SCRIPT diff --git a/scripts/restore b/scripts/restore index fb89b7f..1ad8759 100755 --- a/scripts/restore +++ b/scripts/restore @@ -1,7 +1,5 @@ #!/bin/bash -#================================================= -# GENERIC START #================================================= # IMPORT GENERIC HELPERS #================================================= @@ -11,53 +9,47 @@ source ../settings/scripts/_common.sh source /usr/share/yunohost/helpers #================================================= -# MANAGE SCRIPT FAILURE +# RESTORE THE APP MAIN DIR #================================================= +ynh_script_progression --message="Restoring the app main directory..." --weight=1 -ynh_clean_setup () { - ynh_clean_check_starting -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors +ynh_restore_file --origin_path="$install_dir" -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." - -export app=$YNH_APP_INSTANCE_NAME - -export server=$(ynh_app_setting_get --app=$app --key=server) - -export final_path="/opt/yunohost/${app}" - -#================================================= -# INSTALL RESTIC -#================================================= install_restic +chown -R "$app:$app" "$install_dir" + #================================================= # ACTIVATE BACKUP METHODS #================================================= +ynh_script_progression --message="Setting up backup method..." --weight=1 + +mkdir -p /etc/yunohost/hooks.d/backup mkdir -p /etc/yunohost/hooks.d/backup_method mkdir -p /usr/share/yunohost/backup_method -#================================================= -# RESTORE FILES -#================================================= - -ynh_restore +ynh_restore_file --origin_path="/etc/yunohost/hooks.d/backup_method/05-${app}_app" +chmod go=--- "/etc/yunohost/hooks.d/backup_method/05-${app}_app" #================================================= -# ENABLE TIMER +# RESTORE SYSTEM CONFIGURATIONS #================================================= +ynh_script_progression --message="Restoring system configurations related to $app..." --weight=1 -systemctl enable --quiet ${app}.timer -systemctl enable --quiet ${app}_check.timer -systemctl enable --quiet ${app}_check_read_data.timer -systemctl start ${app}.timer -systemctl start ${app}_check.timer -systemctl start ${app}_check_read_data.timer +ynh_restore_file --origin_path="/etc/sudoers.d/$app" +chown root:root "/etc/sudoers.d/$app" + +ynh_restore --origin_path="/root/.ssh/id_${app}_ed25519" +ynh_restore --origin_path="/root/.ssh/id_${app}_ed25519.pub" +# FIXME: restore the .ssh/config instead? +_set_ssh_config + +for suffix in "${systemd_services_suffixes[@]}"; do + ynh_restore_file --origin_path="/etc/systemd/system/$app$suffix.timer" + ynh_restore_file --origin_path="/etc/systemd/system/$app$suffix.service" + systemctl enable "$app$suffix.timer" + systemctl start "$app$suffix.timer" +done #================================================= # END OF SCRIPT diff --git a/scripts/upgrade b/scripts/upgrade index 2709a7f..832b45e 100755 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,7 +1,5 @@ #!/bin/bash -#================================================= -# GENERIC START #================================================= # IMPORT GENERIC HELPERS #================================================= @@ -10,46 +8,31 @@ source _common.sh source /usr/share/yunohost/helpers #================================================= -# LOAD SETTINGS +# STOP SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Loading installation settings..." - -export app=$YNH_APP_INSTANCE_NAME - -export final_path="/opt/yunohost/${app}" -export server=$(ynh_app_setting_get $app server) -export port=$(ynh_app_setting_get $app port) -export ssh_user=$(ynh_app_setting_get $app ssh_user) -export backup_path=$(ynh_app_setting_get $app backup_path) -export passphrase=$(ynh_app_setting_get $app passphrase) -export on_calendar=$(ynh_app_setting_get $app on_calendar) -export check_on_calendar=$(ynh_app_setting_get $app check_on_calendar) -export check_read_data_on_calendar=$(ynh_app_setting_get $app check_read_data_on_calendar) -export conf=$(ynh_app_setting_get $app conf) -export data=$(ynh_app_setting_get $app data) -export apps=$(ynh_app_setting_get $app apps) -export allow_extra_space_use=$(ynh_app_setting_get $app allow_extra_space_use) +ynh_script_progression --message="Stopping $app's systemd service..." --weight=1 #================================================= -# CHECK VERSION +# ENSURE DOWNWARD COMPATIBILITY #================================================= +#ynh_script_progression --message="Ensuring downward compatibility..." --weight=1 + -upgrade_type=$(ynh_check_app_version_changed) #================================================= # BACKUP BEFORE UPGRADE THEN ACTIVE TRAP #================================================= -ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." +#REMOVEME? ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." # Backup the current version of the app -ynh_backup_before_upgrade -ynh_clean_setup () { +#REMOVEME? ynh_backup_before_upgrade +#REMOVEME? ynh_clean_setup () { ynh_clean_check_starting # Restore it if the upgrade fails - ynh_restore_upgradebackup +#REMOVEME? ynh_restore_upgradebackup } # Exit if an error occurs during the execution of the script -ynh_abort_if_errors +#REMOVEME? ynh_abort_if_errors if grep "${app}.timer" /etc/yunohost/services.yml > /dev/null ; then @@ -73,7 +56,7 @@ ynh_script_progression --message="Creating user ${app}" id ${app} 2>/dev/null || useradd -m ${app} ynh_script_progression --message="Configure ${app} user sudoer rights" cat > /tmp/${app}_sudoer << EOSUDOER -${app} ALL = (root) NOPASSWD: /usr/bin/yunohost*, /bin/journalctl*, /usr/bin/find /etc/yunohost/apps -name backup, ${final_path}/check_method_${app} +${app} ALL = (root) NOPASSWD: /usr/bin/yunohost*, /bin/journalctl*, /usr/bin/find /etc/yunohost/apps -name backup, ${install_dir}/check_method_${app} EOSUDOER visudo -cf /tmp/${app}_sudoer && mv /tmp/${app}_sudoer /etc/sudoers.d/${app} ynh_script_progression --message="Move ssh keys from root to ${app} user's home" @@ -116,21 +99,21 @@ mkdir -p /usr/share/yunohost/backup_method ynh_script_progression --message="Setting up backup methods" ynh_configure backup_method "/etc/yunohost/hooks.d/backup_method/05-${app}_app" -ynh_configure check_method "${final_path}/check_method_${app}" +ynh_configure check_method "${install_dir}/check_method_${app}" #================================================= # SETUP LOG SCRIPTS #================================================= ynh_script_progression --message="Setting up backup log script" -ynh_configure restic_log "${final_path}/restic_log_${app}" -chmod +x "${final_path}/restic_log_${app}" -chown ${app}: "${final_path}/restic_log_${app}" +ynh_configure restic_log "${install_dir}/restic_log_${app}" +chmod +x "${install_dir}/restic_log_${app}" +chown ${app}: "${install_dir}/restic_log_${app}" ynh_script_progression --message="Setting up check log script" -ynh_configure restic_check_log "${final_path}/restic_check_log_${app}" -chmod +x "${final_path}/restic_check_log_${app}" -chown ${app}: "${final_path}/restic_check_log_${app}" +ynh_configure restic_check_log "${install_dir}/restic_check_log_${app}" +chmod +x "${install_dir}/restic_check_log_${app}" +chown ${app}: "${install_dir}/restic_check_log_${app}" #================================================= # CONFIGURE CRON @@ -138,11 +121,11 @@ chown ${app}: "${final_path}/restic_check_log_${app}" ynh_script_progression --message="Configuring cron" --weight=5 ynh_configure backup-with-restic "/usr/local/bin/backup-with-${app}" -ynh_configure check-restic "${final_path}/check-${app}" +ynh_configure check-restic "${install_dir}/check-${app}" chmod +x "/usr/local/bin/backup-with-${app}" chown ${app}: "/usr/local/bin/backup-with-${app}" -chmod +x "${final_path}/check-${app}" -chmod +x "${final_path}/check_method_${app}" +chmod +x "${install_dir}/check-${app}" +chmod +x "${install_dir}/check_method_${app}" ynh_add_systemd_config --service=${app} --template=systemd.service ynh_add_systemd_config --service=${app}_check --template=systemd_check.service ynh_add_systemd_config --service=${app}_check_read_data --template=systemd_check_read_data.service diff --git a/tests.toml b/tests.toml new file mode 100644 index 0000000..050cb44 --- /dev/null +++ b/tests.toml @@ -0,0 +1,38 @@ +#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/tests.v1.schema.json + +test_format = 1.0 + +[default] + + args.repository = "ssh://sam@domain.tld:22/~/backup" + args.passphrase = "A_Passphrase" + args.conf = 1 + args.data = 1 + args.apps = "all" + args.on_calendar = "Daily" + args.mailalert = "errors_only" + + # ------------------------------- + # Commits to test upgrade from + # ------------------------------- + + [default.test_upgrade_from.d1cd666ee27f5cfb8e40c6f44a09370381b41b35] + name = "Older ynh 11 version" + args.server = "domain.tld:22" + args.ssh_user = "package_checker" + args.passphrase = "A_Passphrase" + args.conf = 1 + args.data = 1 + args.apps = "all" + args.on_calendar = "Daily" + args.mailalert = "never" + + +[local_directory] + args.repository = "/mnt/backup" + args.passphrase = "A_Passphrase" + args.conf = 1 + args.data = 1 + args.apps = "all" + args.on_calendar = "Daily" + args.mailalert = "errors_only"