initial commit
This commit is contained in:
commit
e4097afe2b
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Borg backup role
|
||||||
|
This role installs Borg backup on backupservers and clients. The role contains a wrapper-script 'borg-backup' to ease the usage on the client. Supported options include borg-backup info | init | list | backup | mount. Automysqlbackup will run as pre-backup command if it's installed.
|
||||||
|
The role supports both self hosted and rsync.net as Borg server.
|
||||||
|
|
||||||
|
|
||||||
|
## Required variables
|
||||||
|
Define a group backupservers in your inventory with one or multiple hosts. The default location where the backups will be saved is /var/backup/repos/.
|
||||||
|
```
|
||||||
|
infra:
|
||||||
|
[backupservers]
|
||||||
|
backup1.fiaas.co
|
||||||
|
```
|
||||||
|
|
||||||
|
group\_vars/all.yml:
|
||||||
|
```
|
||||||
|
backupservers:
|
||||||
|
- fqdn: backup1.fiaas.co
|
||||||
|
user: borgbackup
|
||||||
|
type: normal
|
||||||
|
home: /backup/
|
||||||
|
pool: repos
|
||||||
|
options: ""
|
||||||
|
- fqdn: yourhost.rsync.net
|
||||||
|
user: userid
|
||||||
|
type: rsync.net
|
||||||
|
home: ""
|
||||||
|
pool: repos
|
||||||
|
options: "--remote-path=borg1"
|
||||||
|
```
|
||||||
|
Contains the list of server you want to use on a certain client.
|
||||||
|
Allows to override backup servers on group or host level.
|
||||||
|
*WARNING: the trailing / in item.home is required.*
|
||||||
|
|
||||||
|
Define a borg\_passphrase for every host.
|
||||||
|
host\_vars\client1:
|
||||||
|
```
|
||||||
|
borg\_passphrase: Ahl9EiNohr5koosh1Wohs3Shoo3ooZ6p
|
||||||
|
```
|
||||||
|
|
||||||
|
*Make sure to check the configured defaults for this role, which contains the list of default locations being backed up in backup_include.* Override this in your inventory where required.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Configure Borg on the server and on a client:
|
||||||
|
```
|
||||||
|
ansible-playbook -i inventory/test playbooks/backup.yml -l backup1.fiaas.co
|
||||||
|
ansible-playbook -i inventory/test playbooks/backup.yml -l client1.fiaas.co
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further reading
|
||||||
|
https://borgbackup.readthedocs.io/en/stable/
|
25
defaults/main.yml
Normal file
25
defaults/main.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
backup_required: True
|
||||||
|
restore: False
|
||||||
|
|
||||||
|
borg_version: "1.0.10"
|
||||||
|
borg_checksum: "sha256:99f889e57630e64a67d0c1a54c056f8d82aac1ccc2cc56c9fb5f5f2710f29950"
|
||||||
|
borg_download_url: "https://github.com/borgbackup/borg/releases/download/{{ borg_version }}/borg-linux64"
|
||||||
|
|
||||||
|
backup_pre_commands:
|
||||||
|
- '[[ ! -f "/usr/sbin/automysqlbackup" ]] || /usr/sbin/automysqlbackup'
|
||||||
|
- "dpkg --get-selections > /root/.installed-software.log"
|
||||||
|
|
||||||
|
backup_include:
|
||||||
|
- "/etc"
|
||||||
|
- "/home"
|
||||||
|
- "/root"
|
||||||
|
- "/var/www"
|
||||||
|
- "/var/log"
|
||||||
|
|
||||||
|
retention:
|
||||||
|
hourly: 12
|
||||||
|
daily: 7
|
||||||
|
weekly: 4
|
||||||
|
monthly: 6
|
||||||
|
yearly: 1
|
106
tasks/borg-client.yml
Normal file
106
tasks/borg-client.yml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
- name: client | generate ssh key for this machine
|
||||||
|
shell: if [ -f ~/.ssh/id_rsa ]; then rm -f ~/.ssh/id_rsa; fi && ssh-keygen -q -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" creates=~/.ssh/id_rsa.pub
|
||||||
|
|
||||||
|
- name: client | fetch ssh-key
|
||||||
|
shell: cat /root/.ssh/id_rsa.pub
|
||||||
|
register: sshkey
|
||||||
|
changed_when: False
|
||||||
|
|
||||||
|
- name: client | write passphrase
|
||||||
|
lineinfile:
|
||||||
|
dest: "/root/.borg.passphrase"
|
||||||
|
state: "present"
|
||||||
|
line: 'export BORG_PASSPHRASE="{{ borg_passphrase }}"'
|
||||||
|
create: "yes"
|
||||||
|
|
||||||
|
- name: client | template sshconfig for backup-hosts (no strict key checking)
|
||||||
|
template:
|
||||||
|
src: "ssh.config.j2"
|
||||||
|
dest: "/root/.ssh/config"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
|
||||||
|
- name: client | put sshpubkey on the normal backupserver
|
||||||
|
authorized_key:
|
||||||
|
user: "{{ item.user }}"
|
||||||
|
key: "{{ sshkey.stdout }}"
|
||||||
|
key_options: 'command="cd {{ item.home }}{{ item.pool }}/{{ inventory_hostname }};borg serve --restrict-to-path {{ item.home }}/{{ item.pool }}/{{ inventory_hostname }}",no-port-forwarding,no-X11-forwarding,no-pty,no-agent-forwarding,no-user-rc'
|
||||||
|
delegate_to: "{{ item.fqdn }}"
|
||||||
|
when: item.type == 'normal'
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
|
||||||
|
# rsync.net has no python, so we can only use raw to manage ssh keys - workaround with local tmp file
|
||||||
|
- name: client | get rsync.net authorized_keys file
|
||||||
|
raw: scp {{ item.user }}@{{ item.fqdn }}:.ssh/authorized_keys /tmp/rsync.net-{{ item.fqdn }}-authkeys
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
when: item.type == 'rsync.net'
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: client | modify local rsync.net authorized_keys
|
||||||
|
authorized_key:
|
||||||
|
user: "{{ ansible_user_id }}"
|
||||||
|
key: "{{ sshkey.stdout }}"
|
||||||
|
key_options: 'command="cd {{ item.home }}{{ item.pool }}/{{ inventory_hostname }};borg serve --restrict-to-path {{ item.home }}/{{ item.pool }}/{{ inventory_hostname }}",no-port-forwarding,no-X11-forwarding,no-pty,no-agent-forwarding,no-user-rc'
|
||||||
|
path: "/tmp/rsync.net-{{ item.fqdn }}-authkeys"
|
||||||
|
manage_dir: no
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
when: item.type == 'rsync.net'
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
register: authkeys
|
||||||
|
|
||||||
|
- name: client | upload local authorized_keys to rsync.net
|
||||||
|
raw: scp /tmp/rsync.net-{{ item.fqdn }}-authkeys {{ item.user }}@{{ item.fqdn }}:.ssh/authorized_keys
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
when: item.type == 'rsync.net' and authkeys.changed
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
|
||||||
|
- name: client | remove tmp authorized_keys files
|
||||||
|
file:
|
||||||
|
path: /tmp/rsync.net-{{ item.fqdn }}-authkeys
|
||||||
|
state: absent
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
when: authkeys.changed
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: client | check for mysql
|
||||||
|
stat: path=/var/lib/automysqlbackup
|
||||||
|
register: automysql
|
||||||
|
|
||||||
|
- name: client | put wrapper script
|
||||||
|
template:
|
||||||
|
src: "borg-backup.sh.j2"
|
||||||
|
dest: "/usr/local/bin/borg-backup"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0744"
|
||||||
|
|
||||||
|
- name: client | create backup-directory on backup server
|
||||||
|
shell: /usr/local/bin/borg-backup init
|
||||||
|
register: backup_init
|
||||||
|
changed_when: "'Remember your passphrase' in backup_init.stderr"
|
||||||
|
|
||||||
|
- name: client | create backup cronjob
|
||||||
|
cron:
|
||||||
|
cron_file: "borg-backup"
|
||||||
|
user: "root"
|
||||||
|
name: "borg-backup"
|
||||||
|
minute: "{{ 59|random }}"
|
||||||
|
hour: "{{ 5|random }}"
|
||||||
|
job: "/usr/local/bin/borg-backup backup"
|
||||||
|
|
||||||
|
- name: client | disable automysqlbackup cronjob, it's in our pre-backup-tasks
|
||||||
|
lineinfile:
|
||||||
|
dest: "/etc/cron.daily/automysqlbackup"
|
||||||
|
regexp: "^/usr/sbin/automysqlbackup$"
|
||||||
|
line: "#/usr/sbin/automysqlbackup"
|
||||||
|
state: "present"
|
||||||
|
backrefs: "yes"
|
||||||
|
create: "no"
|
||||||
|
when: automysql.stat.isdir is defined and automysql.stat.isdir == True
|
30
tasks/borg-server.yml
Normal file
30
tasks/borg-server.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
- name: server | install borg backup
|
||||||
|
get_url:
|
||||||
|
dest: "/usr/local/bin/borg"
|
||||||
|
checksum: "{{ borg_checksum }}"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0755"
|
||||||
|
url: "{{ borg_download_url }}"
|
||||||
|
delegate_to: "{{ item.fqdn }}"
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
when: item.type == 'normal'
|
||||||
|
|
||||||
|
- name: server | create user
|
||||||
|
user:
|
||||||
|
name: "{{ item.user }}"
|
||||||
|
shell: "/bin/bash"
|
||||||
|
home: "{{ item.home }}"
|
||||||
|
createhome: "yes"
|
||||||
|
delegate_to: "{{ item.fqdn }}"
|
||||||
|
with_items: "{{ backupservers }}"
|
||||||
|
when: item.type == 'normal'
|
||||||
|
|
||||||
|
- name: server | create directories
|
||||||
|
file:
|
||||||
|
path: "{{ item.pool }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ item.user }}"
|
||||||
|
group: "{{ item.user }}"
|
||||||
|
mode: "0770"
|
10
tasks/install.yml
Normal file
10
tasks/install.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
- name: install borg backup
|
||||||
|
get_url:
|
||||||
|
dest: "/usr/local/bin/borg"
|
||||||
|
checksum: "{{ borg_checksum }}"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0755"
|
||||||
|
url: "{{ borg_download_url }}"
|
||||||
|
tags: borginstall
|
13
tasks/main.yml
Normal file
13
tasks/main.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- include: install.yml
|
||||||
|
when: backup_required == True or inventory_hostname in groups.backupservers or restore == True
|
||||||
|
|
||||||
|
- include: borg-server.yml
|
||||||
|
when: inventory_hostname in groups.backupservers
|
||||||
|
|
||||||
|
- include: borg-client.yml
|
||||||
|
when: backup_required == True and inventory_hostname not in groups.backupservers
|
||||||
|
|
||||||
|
- include: restore.yml
|
||||||
|
when: restore == True and inventory_hostname not in groups.backupservers
|
42
tasks/restore.yml
Normal file
42
tasks/restore.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
- name: client | generate ssh key for this machine
|
||||||
|
shell: if [ -f ~/.ssh/id_rsa ]; then rm -f ~/.ssh/id_rsa; fi && ssh-keygen -q -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" creates=~/.ssh/id_rsa.pub
|
||||||
|
|
||||||
|
- name: client | fetch ssh-key
|
||||||
|
shell: cat /root/.ssh/id_rsa.pub
|
||||||
|
register: sshkey
|
||||||
|
changed_when: False
|
||||||
|
|
||||||
|
- name: client | write passphrase
|
||||||
|
lineinfile:
|
||||||
|
dest: "/root/.borg.passphrase"
|
||||||
|
state: "present"
|
||||||
|
line: 'export BORG_PASSPHRASE="{{ borg_passphrase }}"'
|
||||||
|
create: "yes"
|
||||||
|
|
||||||
|
- name: client | template sshconfig for backup-hosts (no strict key checking)
|
||||||
|
template:
|
||||||
|
src: "ssh.config.j2"
|
||||||
|
dest: "/root/.ssh/config"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
|
||||||
|
- name: client | place sshpubkey on the backupserver
|
||||||
|
authorized_key:
|
||||||
|
user: "{{ borg_user }}"
|
||||||
|
key: "{{ sshkey.stdout }}"
|
||||||
|
key_options: 'command="cd {{ borg_pool }}/{{ restore_from_vm }};borg serve --restrict-to-path {{ borg_pool }}/{{ restore_from_vm }}",no-port-forwarding,no-X11-forwarding,no-pty,no-agent-forwarding,no-user-rc'
|
||||||
|
delegate_to: "{{ item }}"
|
||||||
|
with_items: "{{ groups.backupservers }}"
|
||||||
|
|
||||||
|
- name: client | check for mysql
|
||||||
|
stat: path=/var/lib/automysqlbackup
|
||||||
|
register: automysql
|
||||||
|
|
||||||
|
- name: client | put wrapper script
|
||||||
|
template:
|
||||||
|
src: "borg-restore-from.sh.j2"
|
||||||
|
dest: "/usr/local/bin/borg-restore-from"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0744"
|
78
templates/borg-backup.sh.j2
Normal file
78
templates/borg-backup.sh.j2
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ ! -z "$2" ]
|
||||||
|
then
|
||||||
|
printf "Possible: info | init | list | backup | mount \n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sourcing the backup-passphrase
|
||||||
|
|
||||||
|
. /root/.borg.passphrase
|
||||||
|
|
||||||
|
# Small helper commands, like listing backups, will help us in the future :)
|
||||||
|
|
||||||
|
if [ "$1" = "info" ]
|
||||||
|
then
|
||||||
|
if [ -z "$2" ]; then printf "run $0 with list and use the backup-tag to request information\n"; exit 1; fi
|
||||||
|
{% for b in backupservers %}
|
||||||
|
REPOSITORY={{ b.user }}@{{ b.fqdn }}:{{ b.home }}{{ b.pool }}/{{ inventory_hostname }}
|
||||||
|
/usr/local/bin/borg info $REPOSITORY::$2 {{ b.options }}
|
||||||
|
{% endfor %}
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "mount" ]
|
||||||
|
then
|
||||||
|
if [ -z "$2" ]; then printf "Select the backup-server\n"; exit 1; fi
|
||||||
|
if [ -z "$3" ]; then printf "Select the backup to mount\n"; exit 1; fi
|
||||||
|
if [ -z "$4" ]; then printf "Select the path to mount the backup on\n"; exit 1; fi
|
||||||
|
{% for b in backupservers %}
|
||||||
|
REPOSITORY={{ b.user }}@{{ b.fqdn }}:{{ b.home }}{{ b.pool }}/{{ inventory_hostname }}
|
||||||
|
/usr/local/bin/borg mount $REPOSITORY::$3 $4 {{ b.options }}
|
||||||
|
if [ "$?" = "0" ]; then printf "Backup mounted on $4, do not forget to unmount!\n"; fi
|
||||||
|
exit 0
|
||||||
|
{% endfor %}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "list" ]
|
||||||
|
then
|
||||||
|
{% for b in backupservers %}
|
||||||
|
REPOSITORY={{ b.user }}@{{ b.fqdn }}:{{ b.home }}{{ b.pool }}/{{ inventory_hostname }}
|
||||||
|
printf "Archives on {{ b.fqdn }} :\n"
|
||||||
|
/usr/local/bin/borg list -v $REPOSITORY {{ b.options }}
|
||||||
|
{% endfor %}
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "init" ]
|
||||||
|
then
|
||||||
|
{% for b in backupservers %}
|
||||||
|
REPOSITORY={{ b.user }}@{{ b.fqdn }}:{{ b.home }}{{ b.pool }}/{{ inventory_hostname }}
|
||||||
|
borg init $REPOSITORY {{ b.options }}
|
||||||
|
{% endfor %}
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "backup" ]
|
||||||
|
then
|
||||||
|
date=`date +%Y%m%d-%H%M`
|
||||||
|
|
||||||
|
# Running some commands pre-backup
|
||||||
|
{% for precommand in backup_pre_commands %}
|
||||||
|
{{ precommand }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for b in backupservers %}
|
||||||
|
printf "Backing up to {{ b.fqdn }} :\n"
|
||||||
|
REPOSITORY={{ b.user }}@{{ b.fqdn }}:{{ b.home }}{{ b.pool }}/{{ inventory_hostname }}
|
||||||
|
|
||||||
|
/usr/local/bin/borg create --compression zlib,6 --stats $REPOSITORY::$date {{ b.options }} {% for dir in backup_include %}{{ dir }} {% endfor %}{% if automysql.stat.isdir is defined and automysql.stat.isdir == True %}/var/lib/automysqlbackup{% endif %}
|
||||||
|
|
||||||
|
if [ "$?" -eq "0" ]; then printf "Backup succeeded on $date\n" >> /var/log/borg-backup.log; fi
|
||||||
|
|
||||||
|
# Use the `prune` subcommand to maintain 7 daily, 4 weekly
|
||||||
|
# and 6 monthly archives.
|
||||||
|
/usr/local/bin/borg prune -v $REPOSITORY {{ b.options }} -H {{ retention.hourly }} -d {{ retention.daily }} -w {{ retention.weekly }} -m {{ retention.monthly }} -y {{ retention.yearly }}
|
||||||
|
{% endfor %}
|
||||||
|
fi
|
||||||
|
|
45
templates/borg-restore-from.sh.j2
Normal file
45
templates/borg-restore-from.sh.j2
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ ! -z "$2" ]
|
||||||
|
then
|
||||||
|
printf "Possible: info | list | mount \n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sourcing the backup-passphrase
|
||||||
|
|
||||||
|
. /root/.borg.passphrase
|
||||||
|
|
||||||
|
# Small helper commands, like listing backups, will help us in the future :)
|
||||||
|
|
||||||
|
if [ "$1" = "info" ]
|
||||||
|
then
|
||||||
|
if [ -z "$2" ]; then printf "run $0 with list and use the backup-tag to request information\n"; exit 1; fi
|
||||||
|
{% for host in groups.backupservers %}
|
||||||
|
REPOSITORY={{ borg_user }}@{{ restore_backup_server }}:{{ borg_pool }}/{{ restore_from_vm }}
|
||||||
|
/usr/local/bin/borg info $REPOSITORY::$2
|
||||||
|
{% endfor %}
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "mount" ]
|
||||||
|
then
|
||||||
|
if [ -z "$2" ]; then printf "Select the backup-server\n"; exit 1; fi
|
||||||
|
if [ -z "$3" ]; then printf "Select the backup to mount\n"; exit 1; fi
|
||||||
|
if [ -z "$4" ]; then printf "Select the path to mount the backup on\n"; exit 1; fi
|
||||||
|
{% for host in groups.backupservers %}
|
||||||
|
REPOSITORY={{ borg_user }}@{{ restore_backup_server }}:{{ borg_pool }}/{{ restore_from_vm }}
|
||||||
|
/usr/local/bin/borg mount $REPOSITORY::$3 $4
|
||||||
|
if [ "$?" = "0" ]; then printf "Backup mounted on $4, do not forget to unmount!\n"; fi
|
||||||
|
exit 0
|
||||||
|
{% endfor %}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "list" ]
|
||||||
|
then
|
||||||
|
{% for host in groups.backupservers %}
|
||||||
|
REPOSITORY={{ borg_user }}@{{ restore_backup_server }}:{{ borg_pool }}/{{ restore_from_vm }}
|
||||||
|
printf "{{ restore_from_vm }} archives to restore on {{ host }} ( {{ restore_backup_server }} ):\n"
|
||||||
|
/usr/local/bin/borg list -v $REPOSITORY
|
||||||
|
{% endfor %}
|
||||||
|
exit 0
|
||||||
|
fi
|
8
templates/ssh.config.j2
Normal file
8
templates/ssh.config.j2
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# backup hosts
|
||||||
|
{% for b in backupservers %}
|
||||||
|
Host {{ b.fqdn }}
|
||||||
|
StrictHostKeyChecking no
|
||||||
|
{% if b.port is defined %}
|
||||||
|
Port {{ b.port }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
Loading…
Reference in New Issue
Block a user