initial commit

This commit is contained in:
Luc Stroobant 2017-09-07 20:35:01 +02:00
commit e4097afe2b
10 changed files with 408 additions and 0 deletions

51
README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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"

View 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

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