Index: main/waeup-ansible/host_vars/h9.waeup.org
===================================================================
--- main/waeup-ansible/host_vars/h9.waeup.org	(revision 15904)
+++ main/waeup-ansible/host_vars/h9.waeup.org	(revision 15904)
@@ -0,0 +1,31 @@
+# h9.waeup.org host-specific settings
+# Generate vault values like this:
+#    ansible-vault encrypt_string password123 --ask-vault-pass
+---
+# backup_user: u220626
+# backup_host: u220626.your-storagebox.de
+borgbackup_encryption_mode: "keyfile"
+borgbackup_servers:
+    - fqdn: "u220626.your-storagebox.de"
+      user: "u220626"
+      password: !vault |
+          $ANSIBLE_VAULT;1.1;AES256
+          62393538306432313235313362626134326634666665343836333734646335346337376639356636
+          6235633538656633666130336263346536616361653539360a363032323565376432653736656135
+          65656263656539643335333362656435386639393437613562346638323163313731613036316365
+          3635626333343262620a633363323539643632666530653130623535376663396232346261353533
+          61323866316437396136633530616135303731303333333761613131346435636261
+      type: "hetzner"
+      home: ""
+      pool: "borg"
+      options: ""
+      port: 23
+      quota: "80G"
+borgbackup_include:
+    - "/etc"
+    - "/root"
+    - "/moodle"
+borgbackup_exclude: []
+borgbackup_cron_day: "*"
+borgbackup_cron_hour: "{{ 5|random(seed=inventory_hostname) }}"
+borgbackup_cron_minute: "{{ 59|random(seed=inventory_hostname) }}"
Index: main/waeup-ansible/playbook-install-borg.yml
===================================================================
--- main/waeup-ansible/playbook-install-borg.yml	(revision 15904)
+++ main/waeup-ansible/playbook-install-borg.yml	(revision 15904)
@@ -0,0 +1,194 @@
+---
+# This ansible-playbook prepares hosts for use of borg backup
+#
+# What it does:
+#   - enable borg ppa
+#   - install borg client
+#   - create SSH key for passwordless login
+#   - generate passphrase for borgi (/root/backup/.borg_passphrase)
+#   - add SSH key at remote storagebox authorized_keys
+#   - create borg dirs (log, root/backup)
+#   - create scripts in /root/backup/ for borg handling
+#   - initialize backup repo on remote machine(s) running borg-init.sh
+#   - install CRON jobs for regular backups
+#   - download generated keyfiles/passwords to local machine (borg-creds/)
+#
+##
+#
+# This playbook will normally be run like this:
+#
+#  ansible-playbook -b -i h8.waeup.org, --ask-vault-pass backup-init.yml
+#
+# Cf.: https://github.com/noplanman/ansible-role-borgbackup/blob/master/tasks/borg-client.yml
+#
+##
+#
+# PLEASE NOTE: this playbook requires a gazillion parameters to be set via host vars.
+#              As some of these are hopefully encrypted, you also need the respective
+#              passwords.
+#
+##
+- hosts: h9.waeup.org
+  become: yes
+  vars:
+    backup_server_url: "u220626.your-storagebox.de"
+    backup_server_user: "u220626"
+
+  tasks:
+  - name: "Activate borg PPA"
+    apt_repository:
+        repo: "ppa:costamagnagianfranco/borgbackup"
+        codename: "{{ ansible_distribution_release }}"
+        state: present
+
+  - name: "Install packages for borg backup"
+    apt:
+        name: ['borgbackup', 'pwgen', 'sshpass']
+        state: present
+
+  - name: "Generate ssh-key for backup."
+    openssh_keypair:
+        path: "/root/.ssh/id_backup"
+        type: "ed25519"
+    register: sshkey
+
+  - name: "Create directories for config and logs."
+    file:
+        path: '{{ item.path }}'
+        state: directory
+        owner: root
+        group: root
+        mode: '{{ item.mode }}'
+    loop:
+        - { path: '/root/backup', mode: '0700' }
+        - { path: '/var/log/borg', mode: '0755' }
+
+  - name: "Generate a backup password."
+    shell:
+        cmd: echo 'export BORG_PASSPHRASE="'$(pwgen -s -1 64)'"' > "/root/backup/.borg_passphrase"
+        creates: "/root/backup/.borg_passphrase"
+
+  - name: "Fix file permissions of password."
+    file:
+        path: '/root/backup/.borg_passphrase'
+        owner: root
+        group: root
+        mode: '0600'
+
+  - name: "Get authorized_keys from backup server"
+    shell:
+        cmd: >
+            SSHPASS={{ item.password }}
+            sshpass -e scp
+            -o BatchMode=no -o StrictHostKeyChecking=no
+            {{ item.user }}@{{ item.fqdn }}:.ssh/authorized_keys
+            /tmp/authkeys-{{ item.type }}-{{ item.fqdn }}-authkeys
+    when: item.type in ['hetzner',]
+    with_items: "{{ borgbackup_servers }}"
+    changed_when: false
+    failed_when: false
+
+  - name: "update authorized_keys"
+    authorized_key:
+        user: "root"
+        key: "{{ sshkey.public_key }}"
+        path: /tmp/authkeys-{{ item.type }}-{{ item.fqdn }}-authkeys
+        manage_dir: false
+    when: item.type in ['hetzner', ]
+    with_items: "{{ borgbackup_servers }}"
+    register: authkeys
+
+  - name: "Upload changed authorized_keys"
+    shell:
+        cmd: |
+            SSHPASS={{ item.password }} sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=no {{ item.user }}@{{ item.fqdn }} << EOF
+              mkdir ./.ssh
+              chmod 0700 ./.ssh
+              cd .ssh
+              put /tmp/authkeys-{{ item.type }}-{{ item.fqdn }}-authkeys authorized_keys
+              chmod 0600 authorized_keys
+              bye
+            EOF
+    when: (item.type in ['hetzner', ]) and (authkeys.changed)
+    with_items: "{{ borgbackup_servers }}"
+
+  - name: "Remove tmp authorized_keys files"
+    file:
+        path: /tmp/authkeys-{{ item.type }}-{{ item.fqdn }}-authkeys
+        state: absent
+    with_items: "{{ borgbackup_servers }}"
+
+  - name: "Disable strict host key checks for backup server"
+    blockinfile:
+        dest: "/root/.ssh/config"
+        create: true
+        marker: "### {mark} ANSIBLE MANAGED BLOCK {{ item.fqdn }} ###"
+        content: |
+            Host {{ item.fqdn  }}
+              StrictHostKeyChecking no
+              IdentityFile /root/.ssh/id_backup
+              {% if item.port is defined %}Port {{ item.port }}{% endif %}
+    with_items: "{{ borgbackup_servers }}"
+
+  - name: "Upload borg-init.sh script"
+    template:
+        src: "borg-init.sh.j2"
+        dest: "/root/backup/borg-init.sh"
+        owner: "root"
+        group: "root"
+        mode: "0744"
+        force: no
+
+  - name: "Upload borg-backup.sh script"
+    template:
+        src: "borg-backup.sh.j2"
+        dest: "/root/backup/borg-backup.sh"
+        owner: "root"
+        group: "root"
+        mode: "0744"
+        force: no
+
+  - name: "Upload sb-ftp.sh script"
+    template:
+        src: "sb-ftp.sh.j2"
+        dest: "/root/backup/sb-ftp.sh"
+        owner: "root"
+        group: "root"
+        mode: "0744"
+        force: no
+
+  - name: "Initialize repo"
+    command: /root/backup/borg-init.sh
+    args:
+        creates: /root/.config/borg/keys/
+
+  - name: "Create backup cronjob"
+    cron:
+      cron_file: "borg-backup"
+      user: "root"
+      name: "borg-backup"
+      minute: "{{ borgbackup_cron_minute }}"
+      hour: "{{ borgbackup_cron_hour }}"
+      day: "{{ borgbackup_cron_day }}"
+      job: "/root/backup/borg-backup.sh > /dev/null 2>&1"
+
+  - name: "Fetch keyfiles to localhost"
+    fetch:
+      src: /root/.config/borg/keys/{{ item.fqdn | replace('.', '_') | replace('-', '_') }}__{{ item.home }}{{ item.pool }}_{{ inventory_hostname_short }}
+      dest: borg-creds/
+      flat: yes
+    with_items: "{{ borgbackup_servers }}"
+
+  - name: "Fetch passwordfile to localhost"
+    fetch:
+      src: /root/backup/.borg_passphrase
+      dest: borg-creds/_borg_passphrase-{{ inventory_hostname_short }}
+      flat: yes
+
+  - debug:
+      msg:
+         - "A copy of the keyfile and the borg password were saved to the"
+         - "local borg-creds/ directory."
+         - "Please keep them in a safe place, away from the backup machine"
+         - "and the backuped machine. You might want to encrypt them."
+
