From f2387d325ce2ead7cafe6b75ae279c40d0b8d4e4 Mon Sep 17 00:00:00 2001 From: lhahn Date: Sun, 23 Jun 2024 15:31:52 +0200 Subject: [PATCH] init commit borg --- defaults/main.yml | 61 +++------------- tasks/client.yml | 62 +++++++++------- tasks/main.yml | 47 +++++++----- tasks/master.yml | 1 + tasks/server.yml | 27 ------- templates/backup/backup_from_clients.sh.j2 | 68 ------------------ templates/backup/backup_on_client.sh.j2 | 72 ------------------- templates/backup/conf/db.list.j2 | 5 -- templates/backup/conf/filedir.list.j2 | 5 -- templates/backup/conf/mail.list.j2 | 5 -- templates/usr/local/bin/email-backup.sh.j2 | 21 ++++++ templates/usr/local/bin/idp-backup.sh.j2 | 1 + templates/usr/local/bin/password-backup.sh.j2 | 2 + vars/main.yml | 13 +--- 14 files changed, 98 insertions(+), 292 deletions(-) mode change 100755 => 100644 tasks/client.yml create mode 100644 tasks/master.yml delete mode 100755 tasks/server.yml delete mode 100755 templates/backup/backup_from_clients.sh.j2 delete mode 100755 templates/backup/backup_on_client.sh.j2 delete mode 100755 templates/backup/conf/db.list.j2 delete mode 100755 templates/backup/conf/filedir.list.j2 delete mode 100755 templates/backup/conf/mail.list.j2 create mode 100644 templates/usr/local/bin/email-backup.sh.j2 create mode 100644 templates/usr/local/bin/idp-backup.sh.j2 create mode 100755 templates/usr/local/bin/password-backup.sh.j2 diff --git a/defaults/main.yml b/defaults/main.yml index 9f52e6e..a65401f 100755 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -3,67 +3,22 @@ cloud_apps: /opt cloud_storage: /opt/storage cloud_stage: prod cloud_update: false -ssh_port: 1802 # clients push only daily backup # server (!= client) will then pull daily backups backup_client: true +backup_host: "example" +backup_special_time: "daily" -# Server should pull 1 hour later the backups. -backup_hour_shift: 1 - -backup_times_weekday: "*" -backup_times_hour: - - 6 - - 10 - - 14 - - 18 - - 22 -backup_times_minute: 0 - -backup_keep_days: 7 backup_owner: backup -backup_owner_ssh_key: "ssh-rsa THEREISSUCHRSAKEYYET" backup_group: "{{ backup_owner }}" -backup_permission: 0770 +backup_owner_ssh_private_key: "ssh-rsa THEREISNOSUCHRSAKEYYET" +backup_owner_ssh_public_key: "ssh-rsa THEREISNOSUCHRSAKEYYET" -backup_hosts: - - name: server1-name - domain: host1.my-domain.tld - ip: 001.112.223.123 - - name: server2-name - domain: host2.my-domain.tld +backup_location: "/borg" -backup_folder: "{{ cloud_apps }}/backup" +backup_root: "{{ cloud_storage }}/backup" +backup_run_folder: "{{ cloud_storage }}" -#backup_client_scripts: -# - /path/to/script -param -backup_client_scripts: [] -#backup_client_script_envs: -# - var: VAR_NAME -# path: /some/path -# - var: VAR_NAME -# value: some_value -backup_client_script_envs: [] - -backup_lock: "{{ backup_folder }}/backup.lock" - -#backup_targets: -# db: -# - type: mariadb -# name: db_name_mdb -# - type: pgsql -# name: db_name_pg -# mail: -# - type: file -# path: /path/to/some/file -# - type: mariadb -# name: db_name_mdb_mail -# file: -# - /path/to/some/file -# - /path/to/some/dir -backup_targets: - db: [] - mail: [] - file: [] \ No newline at end of file +backup_storage_key: "MyStorageKey" \ No newline at end of file diff --git a/tasks/client.yml b/tasks/client.yml old mode 100755 new mode 100644 index bf17a8b..20ec5a1 --- a/tasks/client.yml +++ b/tasks/client.yml @@ -1,38 +1,46 @@ --- -- name: generate backup ssh user - user: - name: "{{ backup_owner }}" +- name: Setup backup storage password + copy: + content: "{{ backup_storage_key }}" + dest: "{{ backup_home }}/.borg.key" + owner: "{{ backup_owner }}" group: "{{ backup_group }}" - comment: Backup user - shell: /bin/sh + mode: "0600" -- name: Setup User ssh_key - authorized_key: - user: "{{ backup_owner }}" - key: "{{ backup_owner_ssh_key }}" +- name: Setup ssh folder + file: + state: directory + path: "{{ backup_home }}/.ssh" + owner: "{{ backup_owner }}" + group: "{{ backup_group }}" + mode: "0700" -- name: generate config lists - template: - src: backup/conf/{{ conf }}.list.j2 - dest: "{{ backup_conf }}/{{ conf }}.list" +- name: Setup ssh keys + copy: + content: "{{ item.content }}" + dest: "{{ backup_home }}/.ssh/{{ item.file }}" + owner: "{{ backup_owner }}" + group: "{{ backup_group }}" + mode: "0600" loop: - - filedir - - db - - mail + - content: "{{ backup_owner_ssh_private_key }}" + file: "id_rsa" + - content: "{{ backup_owner_ssh_public_key }}" + file: "id_rsa.pub" loop_control: - loop_var: conf - label: "{{ conf }}.list" + label: "{{ item.file }}" - name: setup backup script template: - src: backup/backup_on_client.sh.j2 - dest: "{{ backup_folder }}/backup.sh" - mode: 0740 + src: "./usr/local/bin/{{ backup_host }}-backup.sh.j2" + dest: "/usr/local/bin/backup.sh" + owner: "{{ backup_owner }}" + group: "{{ backup_group }}" + mode: "0750" -- name: setup cronjob for client backup generation +- name: setup cron backup job cron: - name: "backup" - weekday: "{{ backup_times_weekday }}" - hour: "{{ backup_times_hour | join(',') }}" - minute: "{{ backup_times_minute }}" - job: "{{ backup_folder }}/backup.sh" + name: "{{ backup_host }} backup" + user: "{{ backup_owner }}" + job: "/usr/local/bin/backup.sh" + special_time: "{{ backup_special_time }}" \ No newline at end of file diff --git a/tasks/main.yml b/tasks/main.yml index 53d8579..2110f30 100755 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,34 +1,43 @@ --- -# Basics - name: install requirements for backup system apt: update_cache: yes state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" install_recommends: yes pkg: - - rsync + - borgbackup -- name: setup storage folders for backup +- name: setup backup location file: state: directory - path: "{{ dir.value.path }}" + path: "{{ backup_root }}" + owner: "root" + group: "root" + mode: "0755" + +- name: Setup required groups of users + group: + name: "{{ backup_group }}" + state: present + +- name: generate backup user + user: + name: "{{ backup_owner }}" + group: "{{ backup_group }}" + comment: backup user + shell: /sbin/nologin + home: "{{ backup_home }}" + +- name: setup backup storage + file: + state: directory + path: "{{ backup_storage }}" owner: "{{ backup_owner }}" group: "{{ backup_group }}" - mode: "{{ backup_permission }}" - with_dict: "{{ backup_storage }}" - loop_control: - loop_var: dir - label: "{{ dir.key }}" + mode: "0777" -- name: setup conf folder - file: - state: directory - path: "{{ backup_folder }}/conf" +- include_tasks: master.yml + when: not backup_client -- name: setup client backup structure - include_tasks: client.yml +- include_tasks: client.yml when: backup_client - -- name: setup server backup structure - include_tasks: server.yml - when: not backup_client \ No newline at end of file diff --git a/tasks/master.yml b/tasks/master.yml new file mode 100644 index 0000000..73b314f --- /dev/null +++ b/tasks/master.yml @@ -0,0 +1 @@ +--- \ No newline at end of file diff --git a/tasks/server.yml b/tasks/server.yml deleted file mode 100755 index cbd4976..0000000 --- a/tasks/server.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -- name: analyse backup hours for pulling server - set_fact: - backup_times_server_hour: "{{ backup_times_server_hour | default([]) + [item + backup_hour_shift] }}" - loop: "{{ backup_times_hour }}" - -- name: generate backup user ssh key - user: - name: "{{ backup_owner }}" - generate_ssh_key: yes - ssh_key_type: rsa - ssh_key_bits: 4096 - home: "{{ backup_folder }}" - -- name: template main server backup pull script - template: - src: backup/backup_from_clients.sh.j2 - dest: "{{ backup_folder }}/pull-backup.sh" - mode: 0740 - -- name: setup cronjob for server backup pull - cron: - name: "pull backups" - weekday: "{{ backup_times_weekday }}" - hour: "{{ backup_times_server_hour | join(',') }}" - minute: "{{ backup_times_minute }}" - job: "{{ backup_folder }}/pull-backup.sh" diff --git a/templates/backup/backup_from_clients.sh.j2 b/templates/backup/backup_from_clients.sh.j2 deleted file mode 100755 index d52d5ad..0000000 --- a/templates/backup/backup_from_clients.sh.j2 +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -set -e -o pipefail - -cd {{ backup_folder }} -if [ -f {{ backup_lock }} ]; then - echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist." - echo "Associated PID is $(cat {{ backup_lock }})" - exit 1 -fi - -echo "$$" > {{ backup_lock }} -BKPTIMESTAMP=$(date +"%y-%m-%d.%H") -BKP=$BKPTIMESTAMP"_backup" - - -# generate rolling folder -{% for target in backup_targets %} -{% for host in backup_hosts %} -{% if 'domain' in host %} -mkdir -p {{ target }}/{{ host.name + '_' + host.domain }}/$BKP -{% elif 'ip' in host %} -mkdir -p {{ target }}/{{ host.name + '_' + host.ip }}/$BKP -{% endif %} -{% endfor %} -{% endfor %} - - -# pull backups -{% for target in backup_targets %} -{% for host in backup_hosts %} -{% if 'domain' in host %} -rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.domain }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.domain }}/$BKP -{% elif 'ip' in host %} -rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.ip }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.ip }}/$BKP -{% endif %} -{% endfor %} -{% endfor %} - - -# link to latest -{% for target in backup_targets %} -{% for host in backup_hosts %} -{% if 'domain' in host %} -ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/LATEST -{% elif 'ip' in host %} -ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/LATEST -{% endif %} -{% endfor %} -{% endfor %} - - -# keep only last {{ backup_keep_days * ( backup_times_hour | length ) }} -{% for target in backup_targets %} -{% for host in backup_hosts %} -{% if 'domain' in host %} -DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.domain }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }}) -{% elif 'ip' in host %} -DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.ip }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }}) -{% endif %} -if (( $( echo $DELETE_LIST | wc -w ) > 0 )); then - rm -rf $DELETE_LIST -fi -{% endfor %} -{% endfor %} - - -rm {{ backup_lock }} diff --git a/templates/backup/backup_on_client.sh.j2 b/templates/backup/backup_on_client.sh.j2 deleted file mode 100755 index ba38eed..0000000 --- a/templates/backup/backup_on_client.sh.j2 +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -set -e -o pipefail - -if [ -f {{ backup_lock }} ]; then - echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist." - echo "Associated PID is $(cat {{ backup_lock }})" - exit 1 -fi -echo "$$" > {{ backup_lock }} - -cd {{ backup_folder }} - -# Load script envs -echo "Load script environment variables" -{% for scr_env in backup_client_script_envs %} -{% if 'path' in scr_env %} -export {{ scr_env.var }}={{ scr_env.path }}:${{ scr_env.var }} -{% else %} -export {{ scr_env.var }}={{ scr_env.value }} -{% endif %} -{% endfor %} - - -# Perform client pre backup scripts for -echo "Perform pre-backup scripts" -{% for cmd in backup_client_scripts %} -{{ cmd }}; -{% endfor %} - - -# Backup files and directories -echo "Copy files and directories" -while read DIRFILE; do - echo " - Copy $DIRFILE" - BACKUP_SUBDIR=$(dirname $DIRFILE) - mkdir -p "{{ backup_storage.file.path }}/$BACKUP_SUBDIR" - cp -r -u $DIRFILE "{{ backup_storage.file.path }}/$DIRFILE" -done < {{ backup_conf }}/filedir.list -rm -f file.tgz #remove old backup -tar czfv file.tgz file -rm -rf file/* - -# Backup databases -echo "Generate DB dumps" -while read DBS; do - DBTYPE=$(echo $DBS | cut -f 1 -d "{{ backup_conf_db_sep }}" ); - DBNAME=$(echo $DBS | cut -f 2 -d "{{ backup_conf_db_sep }}" ); - if [ $DBTYPE == "mariadb" ] || [ $DBTYPE == "mysql" ]; then - echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql" - mkdir -p {{ backup_storage.db.path }}/$DBTYPE; - mysqldump $DBNAME | gzip -c > {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql.gz; - elif [ $DBTYPE == "pgsql" ]; then - echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql" - mkdir -p {{ backup_storage.db.path }}/$DBTYPE; - sudo -u postgres bash -c "pg_dump $DBNAME | gzip -c > /tmp/$DBNAME.sql.gz"; - mv /tmp/$DBNAME.sql.gz /backup/db/$DBTYPE/$DBNAME.sql.gz; - else - echo "- ERROR! DB type '$DBTYPE' for '$DBNAME' is unknown to backup." - fi -done < {{ backup_conf }}/db.list -rm -f db.tgz #remove old backup -tar czfv db.tgz db -rm -rf db/* - -#Mail -rm -f mail.tgz #remove old backup -tar czfv mail.tgz mail -rm -rf mail/* - -rm {{ backup_lock }} -chown {{ backup_owner }}:{{ backup_group }} {{ backup_folder }}/*.tgz \ No newline at end of file diff --git a/templates/backup/conf/db.list.j2 b/templates/backup/conf/db.list.j2 deleted file mode 100755 index 7b75127..0000000 --- a/templates/backup/conf/db.list.j2 +++ /dev/null @@ -1,5 +0,0 @@ -{% if 'db' in backup_targets %} -{% for db in backup_targets.db %} -{{ db.type }}{{ backup_conf_db_sep }}{{ db.name }} -{% endfor %} -{% endif %} \ No newline at end of file diff --git a/templates/backup/conf/filedir.list.j2 b/templates/backup/conf/filedir.list.j2 deleted file mode 100755 index a045c6f..0000000 --- a/templates/backup/conf/filedir.list.j2 +++ /dev/null @@ -1,5 +0,0 @@ -{% if 'file' in backup_targets %} -{% for filedir in backup_targets.file %} -{{ filedir }} -{% endfor %} -{% endif %} \ No newline at end of file diff --git a/templates/backup/conf/mail.list.j2 b/templates/backup/conf/mail.list.j2 deleted file mode 100755 index 7224c8d..0000000 --- a/templates/backup/conf/mail.list.j2 +++ /dev/null @@ -1,5 +0,0 @@ -{% if 'mail' in backup_targets %} -{% for mail in backup_targets.mail %} -{{ mail }} -{% endfor %} -{% endif %} \ No newline at end of file diff --git a/templates/usr/local/bin/email-backup.sh.j2 b/templates/usr/local/bin/email-backup.sh.j2 new file mode 100644 index 0000000..ccbf243 --- /dev/null +++ b/templates/usr/local/bin/email-backup.sh.j2 @@ -0,0 +1,21 @@ +#!/bin/bash + +RUNFOLDER="{{ backup_run_folder }}"; +TARGETFOLDER="{{ backup_storage }}"; +REPOLOCATION="{{ backup_location }}"; +ARCHIVENAME="mailcow-$(date '+%s')"; +BORG_REPO_KEY=$(cat {{ backup_home }}/.borg.key); + +cd $RUNFOLDER; +MAILCOW_BACKUP_LOCATION="$TARGETFOLDER/" ./helper-scripts/backup_and_restore.sh backup all; +LATESTBACKUP="$(ls -t $TARGETFOLDER | head -n 1)"; + +if [[ "$LATESTBACKUP" != *"mailcow"* ]]; +then + echo "NOT MAILCOW! ABORT!"; + exit 1; +fi + + +borg create -C lzma $REPOLOCATION::$ARCHIVENAME $TARGETFOLDER/$LATESTBACKUP; +rm -rf $TARGETFOLDER/$LATESTBACKUP; diff --git a/templates/usr/local/bin/idp-backup.sh.j2 b/templates/usr/local/bin/idp-backup.sh.j2 new file mode 100644 index 0000000..cc1f786 --- /dev/null +++ b/templates/usr/local/bin/idp-backup.sh.j2 @@ -0,0 +1 @@ +#!/bin/bash \ No newline at end of file diff --git a/templates/usr/local/bin/password-backup.sh.j2 b/templates/usr/local/bin/password-backup.sh.j2 new file mode 100755 index 0000000..05a7907 --- /dev/null +++ b/templates/usr/local/bin/password-backup.sh.j2 @@ -0,0 +1,2 @@ +#!/bin/bash + diff --git a/vars/main.yml b/vars/main.yml index 27ac0f8..ffd23a9 100755 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,12 +1,3 @@ --- -backup_conf: "{{ backup_folder }}/conf" - -backup_conf_db_sep: ":" - -backup_storage: - mail: - path: "{{ backup_folder }}/mail" - db: - path: "{{ backup_folder }}/db" - file: - path: "{{ backup_folder }}/file" \ No newline at end of file +backup_home: "{{ backup_root }}/home" +backup_storage: "{{ backup_root }}/storage" \ No newline at end of file