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