init commit borg

This commit is contained in:
Lars Hahn 2024-06-23 15:31:52 +02:00
parent 36ed650628
commit f2387d325c
14 changed files with 98 additions and 292 deletions

View File

@ -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: []
backup_storage_key: "MyStorageKey"

62
tasks/client.yml Executable file → Normal file
View File

@ -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 }}"

View File

@ -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

1
tasks/master.yml Normal file
View File

@ -0,0 +1 @@
---

View File

@ -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"

View File

@ -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 }}

View File

@ -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

View File

@ -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 %}

View File

@ -1,5 +0,0 @@
{% if 'file' in backup_targets %}
{% for filedir in backup_targets.file %}
{{ filedir }}
{% endfor %}
{% endif %}

View File

@ -1,5 +0,0 @@
{% if 'mail' in backup_targets %}
{% for mail in backup_targets.mail %}
{{ mail }}
{% endfor %}
{% endif %}

View File

@ -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;

View File

@ -0,0 +1 @@
#!/bin/bash

View File

@ -0,0 +1,2 @@
#!/bin/bash

View File

@ -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"
backup_home: "{{ backup_root }}/home"
backup_storage: "{{ backup_root }}/storage"