Implement governed S1 backup recovery loop
Some checks failed
CI Smoke / source-contract (push) Failing after 9s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
This commit is contained in:
codex 2026-08-23 13:13:13 +02:00
parent 40e295e3bd
commit 295bf43d54
16 changed files with 1623 additions and 95 deletions

View file

@ -0,0 +1,20 @@
---
# Mutating deployment surface for RAIL-HO-WP-0012-T04. This playbook is not
# included by bootstrap or verification and requires an exact operator token.
- hosts: all
become: true
vars_files:
- ../inventory/group_vars/all.yaml
pre_tasks:
- name: Require exact approval for S1 backup timer deployment
ansible.builtin.assert:
that:
- railiance_backup_deploy_approval | default('') == 'DEPLOY-RAIL-HO-WP-0012-S1-BACKUP-TIMER'
- railiance_backup_source_revision | default('') is match('^[0-9a-f]{40}$')
fail_msg: >-
Refusing host mutation. Set
railiance_backup_deploy_approval=DEPLOY-RAIL-HO-WP-0012-S1-BACKUP-TIMER
and a 40-character railiance_backup_source_revision after reviewing
this play and its rendered units.
roles:
- role: s1_backup

View file

@ -0,0 +1,4 @@
---
- name: Reload systemd for S1 backup
ansible.builtin.systemd:
daemon_reload: true

View file

@ -0,0 +1,81 @@
---
- name: Install S1 backup runtime packages
ansible.builtin.package:
name:
- age
- python3
- python3-yaml
state: present
- name: Create bounded S1 backup directories
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: root
group: root
mode: "{{ item.mode }}"
loop:
- {path: /usr/local/lib/railiance-infra/s1-backup/scripts, mode: "0755"}
- {path: /usr/local/lib/railiance-infra/s1-backup/spec, mode: "0755"}
- {path: /opt/backup/railiance/infra, mode: "0700"}
- name: Install S1 backup Python implementation
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../scripts/{{ item }}"
dest: "/usr/local/lib/railiance-infra/s1-backup/scripts/{{ item }}"
owner: root
group: root
mode: "0755"
loop:
- s1_backup.py
- s1_backup_contract.py
- s1_restore.py
- name: Install S1 backup declaration
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../spec/s1-backup.yaml"
dest: /usr/local/lib/railiance-infra/s1-backup/spec/s1-backup.yaml
owner: root
group: root
mode: "0644"
- name: Install public recipient policy metadata
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../.sops.yaml"
dest: /usr/local/lib/railiance-infra/s1-backup/.sops.yaml
owner: root
group: root
mode: "0644"
- name: Install S1 backup command wrapper
ansible.builtin.template:
src: railiance-backup-s1.j2
dest: /usr/local/sbin/railiance-backup-s1
owner: root
group: root
mode: "0755"
- name: Install S1 backup systemd service
ansible.builtin.template:
src: railiance-backup-s1.service.j2
dest: /etc/systemd/system/railiance-backup-s1.service
owner: root
group: root
mode: "0644"
notify: Reload systemd for S1 backup
- name: Install S1 backup systemd timer
ansible.builtin.template:
src: railiance-backup-s1.timer.j2
dest: /etc/systemd/system/railiance-backup-s1.timer
owner: root
group: root
mode: "0644"
notify: Reload systemd for S1 backup
- name: Enable daily S1 backup timer
ansible.builtin.systemd:
name: railiance-backup-s1.timer
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
exec python3 /usr/local/lib/railiance-infra/s1-backup/scripts/s1_backup.py create \
--source-revision "{{ railiance_backup_source_revision }}" "$@"

View file

@ -0,0 +1,18 @@
[Unit]
Description=Encrypted Railiance S1 operating-system backup
Documentation=file:///usr/local/lib/railiance-infra/s1-backup/spec/s1-backup.yaml
After=local-fs.target
ConditionPathIsReadWrite=/opt/backup/railiance/infra
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/railiance-backup-s1
User=root
Group=root
UMask=0077
Nice=10
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/backup/railiance/infra

View file

@ -0,0 +1,11 @@
[Unit]
Description=Daily encrypted Railiance S1 backup (RAIL-HO-WP-0012-T04)
[Timer]
OnCalendar=*-*-* 02:15:00 UTC
RandomizedDelaySec=30min
Persistent=true
Unit=railiance-backup-s1.service
[Install]
WantedBy=timers.target