--- # collect-luks-keys.yml — fetch LUKS backup keys from enrolled clients and store them # on the ansipa-luks-keys SMB share (accessible only to KeyAdmin group members). # # Flow per host: # 1. Fetch /_LUKS_BACKUP_KEY from the client to a local staging directory. # 2. Upload the staged file to //IPA_SERVER/ansipa-luks-keys/ via smbclient. # 3. Delete the local staging copy. # # The ansipa-luks-keys SMB share is write-only for 'luks-upload' and read-only # for members of the 'KeyAdmin' group. Add a Samba user to KeyAdmin on the IPA # container to grant read access: # useradd -r -G KeyAdmin && smbpasswd -a # # Usage: # ansible-playbook -i inventory collect-luks-keys.yml \ # -e luks_smb_server=ipa.corp.example.com \ # -e luks_upload_password= # # Or set defaults in group_vars / ansible-vault. The smb_server can also be # auto-detected from /etc/ipa/default.conf on the clients. - name: Collect and archive LUKS backup keys hosts: all become: yes vars: luks_key_path: /_LUKS_BACKUP_KEY # Local staging dir — files are deleted after a successful SMB upload. luks_keys_stage: "{{ playbook_dir }}/luks-keys-stage" luks_smb_server: "{{ luks_smb_server | mandatory('luks_smb_server is required — use -e luks_smb_server=') }}" luks_smb_share: ansipa-luks-keys luks_upload_user: luks-upload luks_upload_password: "{{ luks_upload_password | mandatory('luks_upload_password is required — use -e luks_upload_password=... or ansible-vault') }}" # Temp credentials file on the controller — removed at the end of the play. _smb_creds_file: "/tmp/.ansipa-luks-upload-{{ ansible_date_time.epoch }}.creds" tasks: - name: Ensure local staging directory exists file: path: "{{ luks_keys_stage }}" state: directory mode: '0700' delegate_to: localhost run_once: true become: false - name: Write temporary SMB credentials file on controller copy: dest: "{{ _smb_creds_file }}" mode: '0600' content: | username = {{ luks_upload_user }} password = {{ luks_upload_password }} domain = WORKGROUP delegate_to: localhost run_once: true become: false no_log: true - name: Check for LUKS backup key on client stat: path: "{{ luks_key_path }}" register: luks_key_stat - name: Fetch LUKS backup key to local staging area fetch: src: "{{ luks_key_path }}" dest: "{{ luks_keys_stage }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY" flat: yes when: luks_key_stat.stat.exists register: luks_key_fetch - name: Secure staged key permissions file: path: "{{ luks_keys_stage }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY" mode: '0400' delegate_to: localhost become: false when: - luks_key_stat.stat.exists - luks_key_fetch is changed - name: Upload key to ansipa-luks-keys SMB share shell: > smbclient "//{{ luks_smb_server }}/{{ luks_smb_share }}" -A "{{ _smb_creds_file }}" -c "put {{ luks_keys_stage }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY {{ inventory_hostname }}_LUKS_BACKUP_KEY" delegate_to: localhost become: false when: - luks_key_stat.stat.exists - luks_key_fetch is changed register: smb_upload no_log: true - name: Remove local staging copy after successful upload file: path: "{{ luks_keys_stage }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY" state: absent delegate_to: localhost become: false when: - luks_key_stat.stat.exists - luks_key_fetch is changed - smb_upload is succeeded - name: Report key status debug: msg: >- {{ inventory_hostname }}: {% if not luks_key_stat.stat.exists %} no /_LUKS_BACKUP_KEY present (unencrypted install or key already removed) {% elif luks_key_fetch is changed and smb_upload is succeeded %} key uploaded to //{{ luks_smb_server }}/{{ luks_smb_share }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY {% elif luks_key_fetch is not changed %} key unchanged since last collection — skipped upload {% else %} WARNING: key fetched but SMB upload failed — check smbclient output {% endif %} post_tasks: - name: Remove temporary SMB credentials file file: path: "{{ _smb_creds_file }}" state: absent delegate_to: localhost run_once: true become: false