infra/roles/backups/tasks/main.yml

254 lines
7.3 KiB
YAML

---
- name: Set backup schedule defaults
set_fact:
infra_backup_oncalendar: "{{ INFRA_BACKUP_ONCALENDAR | default(lookup('env', 'INFRA_BACKUP_ONCALENDAR') | default('daily', true), true) }}"
restic_keep_daily: "{{ RESTIC_KEEP_DAILY | default(lookup('env', 'RESTIC_KEEP_DAILY') | default(7, true), true) }}"
restic_keep_weekly: "{{ RESTIC_KEEP_WEEKLY | default(lookup('env', 'RESTIC_KEEP_WEEKLY') | default(4, true), true) }}"
restic_keep_monthly: "{{ RESTIC_KEEP_MONTHLY | default(lookup('env', 'RESTIC_KEEP_MONTHLY') | default(6, true), true) }}"
- name: Read restic repository
set_fact:
restic_repository: "{{ RESTIC_REPOSITORY | default(lookup('env', 'RESTIC_REPOSITORY') | default('', true), true) }}"
- name: Read restic password
set_fact:
restic_password: "{{ RESTIC_PASSWORD | default(lookup('env', 'RESTIC_PASSWORD') | default('', true), true) }}"
no_log: true
- name: Read restic AWS access key ID
set_fact:
restic_aws_access_key_id: >-
{{
(RESTIC_AWS_ACCESS_KEY_ID
| default(
lookup('env', 'RESTIC_AWS_ACCESS_KEY_ID')
| default(
(RESTIC_S3_ACCESS_KEY
| default(
lookup('env', 'RESTIC_S3_ACCESS_KEY')
| default(
(S3_ACCESS_KEY_ID
| default(lookup('env', 'S3_ACCESS_KEY_ID') | default('', true), true)
),
true
),
true
)
),
true
),
true
)
)
}}
no_log: true
- name: Read restic AWS secret access key
set_fact:
restic_aws_secret_access_key: >-
{{
(RESTIC_AWS_SECRET_ACCESS_KEY
| default(
lookup('env', 'RESTIC_AWS_SECRET_ACCESS_KEY')
| default(
(RESTIC_S3_SECRET_ACCESS_KEY
| default(
lookup('env', 'RESTIC_S3_SECRET_ACCESS_KEY')
| default(
(S3_SECRET_ACCESS_KEY
| default(lookup('env', 'S3_SECRET_ACCESS_KEY') | default('', true), true)
),
true
),
true
)
),
true
),
true
)
)
}}
no_log: true
- name: Read restic AWS default region
set_fact:
restic_aws_default_region: >-
{{
(RESTIC_AWS_DEFAULT_REGION
| default(
lookup('env', 'RESTIC_AWS_DEFAULT_REGION')
| default(
(S3_REGION
| default(lookup('env', 'S3_REGION') | default('us-east-1', true), true)
),
true
),
true
)
)
}}
- name: Fail if restic repository format is invalid
assert:
that:
- (restic_repository | length == 0) or (restic_repository is match('^/')) or (restic_repository is match('^[a-zA-Z][a-zA-Z0-9+.-]*:'))
fail_msg: >-
RESTIC_REPOSITORY must be an absolute path (e.g. /var/backups/restic) or a backend URL with a scheme
(e.g. s3:https://..., sftp:user@host:/path, rest:https://...).
Got: {{ restic_repository }}
- name: Fail if S3 credentials are missing for S3 restic repository
assert:
that:
- (restic_aws_access_key_id | default('') | length) > 0
- (restic_aws_secret_access_key | default('') | length) > 0
fail_msg: >-
RESTIC_REPOSITORY uses the S3 backend but credentials are missing.
Set RESTIC_AWS_ACCESS_KEY_ID and RESTIC_AWS_SECRET_ACCESS_KEY (recommended),
or RESTIC_S3_ACCESS_KEY and RESTIC_S3_SECRET_ACCESS_KEY,
or S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY.
when:
- (restic_repository | default('')) is match('^s3:')
- name: Fail if restic repository is missing
fail:
msg: "RESTIC_REPOSITORY is required"
when: restic_repository | length == 0
- name: Fail if restic password is missing
fail:
msg: "RESTIC_PASSWORD is required"
when: restic_password | length == 0
- name: Install restic
apt:
name: restic
state: present
update_cache: true
- name: Create backup directories
file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0750"
loop:
- /var/lib/infra-backups
- /var/lib/infra-backups/forgejo
- /var/lib/infra-backups/.cache
- name: Write backup environment file
template:
src: backup.env.j2
dest: /etc/infra-backup.env
owner: root
group: root
mode: "0600"
- name: Install backup script
template:
src: infra-backup.sh.j2
dest: /usr/local/sbin/infra-backup
owner: root
group: root
mode: "0750"
- name: Install systemd service
template:
src: infra-backup.service.j2
dest: /etc/systemd/system/infra-backup.service
owner: root
group: root
mode: "0644"
- name: Install systemd timer
template:
src: infra-backup.timer.j2
dest: /etc/systemd/system/infra-backup.timer
owner: root
group: root
mode: "0644"
- name: Reload systemd
systemd:
daemon_reload: true
- name: Check if restic repo is initialized
command: restic snapshots
environment:
RESTIC_REPOSITORY: "{{ restic_repository }}"
RESTIC_PASSWORD: "{{ restic_password }}"
AWS_ACCESS_KEY_ID: "{{ restic_aws_access_key_id }}"
AWS_SECRET_ACCESS_KEY: "{{ restic_aws_secret_access_key }}"
AWS_DEFAULT_REGION: "{{ restic_aws_default_region }}"
AWS_EC2_METADATA_DISABLED: "true"
register: restic_snapshots
changed_when: false
failed_when: false
- name: Debug restic snapshots output
debug:
msg: "restic snapshots rc={{ restic_snapshots.rc }}, stderr={{ restic_snapshots.stderr | default('') }}"
when: restic_snapshots.rc != 0
- name: Initialize restic repository if missing
command: restic init
environment:
RESTIC_REPOSITORY: "{{ restic_repository }}"
RESTIC_PASSWORD: "{{ restic_password }}"
AWS_ACCESS_KEY_ID: "{{ restic_aws_access_key_id }}"
AWS_SECRET_ACCESS_KEY: "{{ restic_aws_secret_access_key }}"
AWS_DEFAULT_REGION: "{{ restic_aws_default_region }}"
AWS_EC2_METADATA_DISABLED: "true"
when: restic_snapshots.rc != 0
register: restic_init
changed_when: true
failed_when: false
- name: Debug restic init output
debug:
msg: "restic init rc={{ restic_init.rc }}, stderr={{ restic_init.stderr | default('') }}"
when: restic_snapshots.rc != 0
- name: Enable and start infra-backup timer
systemd:
name: infra-backup.timer
enabled: true
state: started
- name: Install backup restore verification script
template:
src: backup-verify.sh.j2
dest: /usr/local/sbin/backup-verify
owner: root
group: root
mode: "0750"
- name: Install systemd service for backup verification
template:
src: backup-verify.service.j2
dest: /etc/systemd/system/backup-verify.service
owner: root
group: root
mode: "0644"
- name: Install systemd timer for monthly backup verification
template:
src: backup-verify.timer.j2
dest: /etc/systemd/system/backup-verify.timer
owner: root
group: root
mode: "0644"
- name: Reload systemd for backup verification
systemd:
daemon_reload: true
- name: Enable and start monthly backup verification timer
systemd:
name: backup-verify.timer
enabled: true
state: started