--- - name: Ensure borg_client_user exists ansible.builtin.getent: database: passwd key: "{{ borg_client_user }}" become: true - name: Compute borg_client_user_home if not set ansible.builtin.set_fact: borg_client_user_home: "{{ getent_passwd[borg_client_user][4] }}" when: borg_client_user_home is not defined - name: Validate borg_client_user home exists ansible.builtin.stat: path: "{{ borg_client_user_home }}" register: user_home_stat become: true - name: Fail if borg_client_user home missing ansible.builtin.fail: msg: | Home directory {{ borg_client_user_home }} for user {{ borg_client_user }} does not exist. Please ensure the user has a valid home directory before running this role. when: not user_home_stat.stat.exists - name: Check readability of included paths ansible.builtin.stat: path: "{{ item }}" loop: "{{ borg_included_dirs }}" register: included_paths_stat become: true become_user: "{{ borg_client_user }}" when: borg_included_dirs | length > 0 - name: Create SSH Directory ansible.builtin.file: path: "{{ borg_client_user_home }}/.ssh" owner: "{{ borg_client_user }}" group: "{{ borg_client_user }}" mode: "0700" state: directory become: true - name: Add borg server to known_hosts ansible.builtin.known_hosts: name: "{{ borg_server_host_url }}" key: "{{ borg_server_host_url }} {{ borg_server_host_ssh_key }}" path: "{{ borg_client_user_home }}/.ssh/known_hosts" state: present become: true become_user: "{{ borg_client_user }}" - name: Generate SSH keys community.crypto.openssh_keypair: path: "{{ borg_client_user_home }}/.ssh/id_{{ borg_ssh_key_type }}" type: "{{ borg_ssh_key_type }}" owner: "{{ borg_client_user }}" group: "{{ borg_client_user }}" mode: "0600" comment: "{{ borg_client_user }}@{{ inventory_hostname }}" become: true register: ssh_key - name: Ensure authorized_keys file exists on borg server ansible.builtin.file: path: "{{ borg_server_user_home }}/.ssh/authorized_keys" state: touch owner: borg group: borg mode: "0600" access_time: preserve modification_time: preserve become: true delegate_to: "{{ borg_server_host }}" - name: Read existing authorized_keys content ansible.builtin.slurp: src: "{{ borg_server_user_home }}/.ssh/authorized_keys" become: true delegate_to: "{{ borg_server_host }}" register: auth_keys_content - name: Find existing authorized_keys line for this host ansible.builtin.set_fact: existing_line: >- {{ (auth_keys_content.content | b64decode).splitlines() | select("search", ssh_key.public_key | trim | regex_escape) | first | default("") }} - name: Detect --append-only setting from existing entry ansible.builtin.set_fact: existing_append_only: "{{ existing_line is search('--append-only') }}" when: existing_line | length > 0 - name: Fail if --append-only setting differs from existing entry ansible.builtin.fail: msg: | Inconsistent --append-only setting for host {{ inventory_hostname }}. Existing entry has --append-only={{ existing_append_only }}, but current invocation uses --append-only={{ borg_mode_append_only }}. All repositories for a host must have the same --append-only setting. when: - existing_line | length > 0 - existing_append_only != borg_mode_append_only - name: Compute all repos for this host ansible.builtin.set_fact: all_repos: >- {{ ( (existing_line | regex_findall('--restrict-to-repository ([^\s"]+)')) + [borg_server_user_home + '/' + borg_repo_name] ) | unique | sort }} - name: Update authorized_keys entry for this host ansible.builtin.lineinfile: path: "{{ borg_server_user_home }}/.ssh/authorized_keys" search_string: "{{ ssh_key.public_key | trim }}" line: >- restrict,command="borg serve{{ " --append-only" if borg_mode_append_only }} {{ all_repos | map('regex_replace', '^', '--restrict-to-repository ') | join(' ') }}" {{ ssh_key.public_key | trim }} {{ borg_client_user }}@{{ inventory_hostname }} state: present become: true delegate_to: "{{ borg_server_host }}" - name: Initialise Borg repository ansible.builtin.command: > borg init --encryption=repokey borg@{{ borg_server_host_url }}:{{ borg_server_user_home }}/{{ borg_repo_name }} environment: BORG_PASSPHRASE: "{{ borg_passphrase }}" become: true become_user: "{{ borg_client_user }}" register: init_borg_output changed_when: init_borg_output.rc != 2 failed_when: - init_borg_output.rc != 2 - init_borg_output.rc != 0 - name: Make sure key file exists ansible.builtin.file: path: "{{ borg_decryption_keys_yaml_path }}" state: touch mode: "0600" access_time: preserve modification_time: preserve delegate_to: localhost become: false - name: Read Vars file ansible.builtin.include_vars: file: "{{ borg_decryption_keys_yaml_path }}" register: local - name: Add repository encryption keys to ansible repo when: not (inventory_hostname ~ '_' ~ borg_repo_name) in local.ansible_facts throttle: 1 block: - name: If host new read encryption keys ansible.builtin.command: > borg key export --paper borg@{{ borg_server_host_url }}:{{ borg_server_user_home }}/{{ borg_repo_name }} become: true become_user: "{{ borg_client_user }}" register: borg_keys changed_when: borg_keys.rc != 0 - name: If host new add encryption keys to vars ansible.builtin.set_fact: decryption_keys: "{{ local.ansible_facts | combine({(inventory_hostname ~ '_' ~ borg_repo_name): borg_keys.stdout}) }}" - name: Update encryption vars ansible.builtin.copy: content: "{{ decryption_keys | to_nice_yaml(indent=2, width=2048) }}" dest: "{{ borg_decryption_keys_yaml_path }}" mode: "0600" when: decryption_keys is defined delegate_to: localhost become: false - name: Create backup scripts ansible.builtin.include_tasks: client_create_scripts_each.yml loop: - "{{ borg_backup_script_location }}" - "{{ borg_backup_script_location }}{{ '@' if borg_backup_argument != '' else '' }}{{ borg_backup_argument }}" loop_control: loop_var: script_location - name: Configure systemd borg_backup service ansible.builtin.template: src: borg_backup.service.j2 dest: /etc/systemd/system/{{ borg_backup_timer_name }}{{ "@" if borg_backup_argument != "" }}{{ borg_backup_argument }}.service mode: "0644" owner: root group: root notify: Reload systemd become: true - name: Configure systemd borg_backup timer ansible.builtin.template: src: borg_backup.timer.j2 dest: /etc/systemd/system/{{ borg_backup_timer_name }}{{ "@" if borg_backup_argument != "" }}{{ borg_backup_argument }}.timer mode: "0644" owner: root group: root notify: Reload systemd become: true - name: Reload systemd now before enabling services ansible.builtin.meta: flush_handlers - name: Enable borg_backup systemd timer ansible.builtin.systemd: name: "{{ borg_backup_timer_name }}{{ '@' if borg_backup_argument != '' else '' }}{{ borg_backup_argument }}.timer" state: started enabled: true become: true