restic: Add role+playbook for Restic backups

The `restic.yml` playbook applies the _restic_ role to hosts in the
_restic_ group.  The _restic_ role installs `restic` and creates a
systemd timer and service unit to run `restic backup` every day.

Restic doesn't really have a configuration file; all its settings are
controlled either by environment variables or command-line options. Some
options, such as the list of files to include in or exclude from
backups, take paths to files containing the values.  We can make use of
these to provide some configurability via Ansible variables.  The
`restic_env` variable is a map of environment variables and values to
set for `restic`.  The `restic_include` and `restic_exclude` variables
are lists of paths/patterns to include and exclude, respectively.
Finally, the `restic_password` variable contains the password to decrypt
the repository contents.  The password is written to a file and exposed
to the _restic-backup.service_ unit using [systemd credentials][0].

When using S3 or a compatible service for respository storage, Restic of
course needs authentication credentials.  These can be set using the
`restic_aws_credentials` variable.  If this variable is defined, it
should be a map containing the`aws_access_key_id` and
`aws_secret_access_key` keys, which will be written to an AWS shared
credentials file.  This file is then exposed to the
_restic-backup.service_ unit using [systemd credentials][0].

[0]: https://systemd.io/CREDENTIALS/
frigate-exporter
Dustin 2024-09-04 08:31:10 -05:00
parent 708bcbc87e
commit 0f4dea9007
13 changed files with 187 additions and 0 deletions

22
group_vars/restic.yml Normal file
View File

@ -0,0 +1,22 @@
restic_env:
RESTIC_REPOSITORY: s3:s3.backups.pyrocufflink.blue/restic
restic_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
62663336623030623165636439326337623638303633356564376136323338363532363837326138
3037613363383238346661336437663464363735323664340a393764343230353938383730633934
32616434303834636236663835663233356431306665613331613433353531373266323430663463
3935343335663837390a663961623764316236653234393333663533383638666230356362303432
65626335306165326262393763306533393430356134623666646339353663326137353236383464
32376366363239666636323734343864313231363765303537313464646464623666373939306131
33343265383636313938306630386164656163663463653731666364633261333230303565326231
39306636373331633764
restic_aws_credentials:
aws_access_key_id: qVjUcNjwrISvDVXbkFMZ
aws_secret_access_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
62303466353962396236626337323839303836386465636231643463363662343033353564616336
3138316166363333663531616561346134376666373830610a633535333134323733653834616332
33623230343933323532633831326633343931633438313037633634646563653730386131373831
6639313765373262650a323138346161376138396536623632636663356532616136336166303861
37643566346231333531623131633763323433313834643566373061366164663430623562623331
3462396662383536386535373263666631393835343534393561

2
hosts
View File

@ -163,6 +163,8 @@ samba-dc
[repohost]
file0.pyrocufflink.blue
[restic]
[rw-root]
[samba-dc]

5
restic.yml Normal file
View File

@ -0,0 +1,5 @@
- hosts: restic
roles:
- role: restic
tags:
- restic

View File

@ -0,0 +1,4 @@
restic_env: {}
restic_include:
- /home
restic_exclude: []

View File

@ -0,0 +1,12 @@
[Unit]
Description=Back up filesystem with restic
[Service]
Type=oneshot
LoadCredential=restic.aws.credentials
LoadCredential=restic.password
Environment=AWS_SHARED_CREDENTIALS_FILE=%d/restic.aws.credentials
Environment=RESTIC_PASSWORD_FILE=%d/restic.password
Environment=XDG_CACHE_HOME=%C
EnvironmentFile=-%E/restic/environment
ExecStart=/usr/bin/restic backup --files-from %E/restic/include --exclude-file %E/restic/exclude --exclude-if-present .nobackup

View File

@ -0,0 +1,11 @@
[Unit]
Description=Daily directory back up with restic
[Timer]
OnCalendar=daily
RandomizedDelaySec=12h
FixedRandomDelay=true
Persistent=true
[Install]
WantedBy=default.target

View File

@ -0,0 +1,8 @@
- name: reload systemd
systemd:
daemon_reload: true
- name: restart restic backup timer
systemd:
name: restic-backup.timer
state: restarted

106
roles/restic/tasks/main.yml Normal file
View File

@ -0,0 +1,106 @@
- name: ensure restic is installed
package:
name: restic
state: present
tags:
- install
- name: ensure restic configuration directory exists
file:
path: /etc/restic
owner: root
group: root
mode: u=rwx,go=rx
state: directory
tags:
- config
- name: ensure restic environment is configured
template:
src: restic.env.j2
dest: /etc/restic/environment
owner: root
group: root
mode: u=rw,go=r
tags:
- config
- restic-environment
- name: ensure restic file list is populated
template:
src: include.j2
dest: /etc/restic/include
owner: root
group: root
mode: u=rw,go=r
tags:
- config
- restic-include
- name: ensure restic exclude list is populated
template:
src: exclude.j2
dest: /etc/restic/exclude
owner: root
group: root
mode: u=rw,go=r
tags:
- config
- restic-exclude
- name: ensure restic password is set
copy:
content: >-
{{ restic_password }}
dest: /etc/credstore/restic.password
owner: root
group: root
mode: a=
diff: false
tags:
- config
- credentials
- name: ensure restic aws credentials are set
template:
src: credentials.j2
dest: /etc/credstore/restic.aws.credentials
owner: root
group: root
mode: a=
diff: false
tags:
- config
- credentials
- name: ensure restic-backup systemd service unit is installed
copy:
src: restic-backup.service
dest: /etc/systemd/system/restic-backup.service
owner: root
group: root
mode: u=rw,go=r
tags:
- systemd
notify:
- reload systemd
- restart restic backup timer
- name: ensure restic-backup systemd timer unit is installed
copy:
src: restic-backup.timer
dest: /etc/systemd/system/restic-backup.timer
owner: root
group: root
mode: u=rw,go=r
tags:
- systemd
- name: ensure restic-backup timer is enabled
systemd:
name: restic-backup.timer
enabled: true
tags:
- service
- name: ensure restic-backup timer is running
systemd:
name: restic-backup.timer
state: started
tags:
- service

View File

@ -0,0 +1,6 @@
[default]
{% if restic_aws_credentials is defined %}
{% for key, value in restic_aws_credentials.items() %}
{{ key }} = {{ value }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,3 @@
{% for path in restic_exclude %}
{{ path }}
{% endfor %}

View File

@ -0,0 +1,3 @@
{% for path in restic_include %}
{{ path }}
{% endfor %}

View File

@ -0,0 +1,3 @@
{% for key, value in _restic_env.items() %}
{{ key }}={{ value }}
{% endfor %}

View File

@ -0,0 +1,2 @@
restic_default_env: {}
_restic_env: '{{ restic_default_env|combine(restic_env) }}'