From 94aa41bb86c4f23e4292c3ee4c6770adb23335e6 Mon Sep 17 00:00:00 2001 From: Frank Adaemmer Date: Sun, 16 Oct 2022 22:37:47 +0200 Subject: [PATCH] add remote_unlock role --- roles/remote_unlock/README.md | 58 ++++++++++ roles/remote_unlock/defaults/main.yml | 2 + roles/remote_unlock/handlers/main.yml | 29 +++++ roles/remote_unlock/meta/argument_specs.yml | 31 ++++++ roles/remote_unlock/meta/main.yml | 45 ++++++++ roles/remote_unlock/tasks/main.yml | 12 ++ roles/remote_unlock/tasks/prepare.yml | 116 ++++++++++++++++++++ roles/remote_unlock/tasks/unlock.yml | 12 ++ roles/remote_unlock/tests/unlock_test.yml | 27 +++++ 9 files changed, 332 insertions(+) create mode 100644 roles/remote_unlock/README.md create mode 100644 roles/remote_unlock/defaults/main.yml create mode 100644 roles/remote_unlock/handlers/main.yml create mode 100644 roles/remote_unlock/meta/argument_specs.yml create mode 100644 roles/remote_unlock/meta/main.yml create mode 100644 roles/remote_unlock/tasks/main.yml create mode 100644 roles/remote_unlock/tasks/prepare.yml create mode 100644 roles/remote_unlock/tasks/unlock.yml create mode 100644 roles/remote_unlock/tests/unlock_test.yml diff --git a/roles/remote_unlock/README.md b/roles/remote_unlock/README.md new file mode 100644 index 0000000..99df762 --- /dev/null +++ b/roles/remote_unlock/README.md @@ -0,0 +1,58 @@ +# remote_unlock Role +Login to initial ram filesystem via SSH and unlock encrypted disks. +Proceed boot after unlock. + +With alternative entry point 'prepare' that system gets setup for this task. + +## Variables + +|Name |Type |Requiered |Default |Description | +|------|------|-----------|--------|-------------| +|unlock_port |int | no | 222 | TCP/IP port of the initial ram filesystem SSH daemon | +|luks_passphrase |str | yes | | Passphrase to unlock encrypted LUKS disks | + +### Alternative Entry Point 'prepare' + +Install all dependencies and build a initial ram filesystem that runs a SSH daemon. +SSH will that be available to unlock LUKS encrypted disks from remote. + +|Name |Type |Requiered |Default |Description | +|------|------|-----------|--------|-------------| +|unlock_port |int | no | 222 | TCP/IP port of the initial ram filesystem SSH daemon | +|ssh_keys |list[str] | yes | | List of ssh public keys that will be added to .ssh/authorized_keys | + + +## Example +```yaml +--- +- name: Remote Unlock Test + hosts: all + vars: + unlock_port: 2224 + luks_passphrase: '123456' + ssh_keys: + - "ssh-rsa 8J+OtU5ldmVyIGdvbm5hIGdpdmUgeW91IHVw8J+Otg== UmljayDwn5W6@IPCfp7sg" + - "ssh-ed25519 TmV2ZXIgZ29ubmEgbGV0IHlvdSBkb3du8J+Otg== UmljayDwn5W6@IPCfp7sg" + tasks: + - name: Prepare Unlock + ansible.builtin.include_role: + name: copyrights.on_premises.remote_unlock + tasks_from: prepare.yml + vars: + ansible_become: true + + - name: Flush handlers + ansible.builtin.meta: flush_handlers + + - name: Restart + ansible.builtin.command: + cmd: shutdown -r +1 + become: true + changed_when: result['rc'] == 0 + register: result + + - name: Unlock after reboot + ansible.builtin.include_role: + name: copyrights.on_premises.remote_unlock + +``` diff --git a/roles/remote_unlock/defaults/main.yml b/roles/remote_unlock/defaults/main.yml new file mode 100644 index 0000000..ebb78ba --- /dev/null +++ b/roles/remote_unlock/defaults/main.yml @@ -0,0 +1,2 @@ +--- +unlock_port: 222 diff --git a/roles/remote_unlock/handlers/main.yml b/roles/remote_unlock/handlers/main.yml new file mode 100644 index 0000000..37765bd --- /dev/null +++ b/roles/remote_unlock/handlers/main.yml @@ -0,0 +1,29 @@ +--- +- name: Regenerate initramfs + ansible.builtin.command: + cmd: dracut --force + changed_when: result['rc'] == 0 + register: result + +- name: Configure dracut-crypt-ssh build + ansible.builtin.command: + cmd: ./configure + chdir: /opt/src/dracut-crypt-ssh + changed_when: result['rc'] == 0 + register: result + listen: Build dracut-crypt-ssh + +- name: Build dracut-crypt-ssh + community.general.make: + chdir: /opt/src/dracut-crypt-ssh + listen: Build dracut-crypt-ssh + +- name: Install dracut-crypt-ssh + community.general.make: + chdir: /opt/src/dracut-crypt-ssh + target: install + listen: Build dracut-crypt-ssh + +- name: Regenerate GRUB + ansible.builtin.command: + cmd: grub2-mkconfig --output /etc/grub2.cfg diff --git a/roles/remote_unlock/meta/argument_specs.yml b/roles/remote_unlock/meta/argument_specs.yml new file mode 100644 index 0000000..4d3fe93 --- /dev/null +++ b/roles/remote_unlock/meta/argument_specs.yml @@ -0,0 +1,31 @@ +--- +argument_specs: + main: + short_description: Unlock LUKS encrypted disks from within initial ram filesystem + description: | + Login to initial ram filesystem via SSH and unlock encrypted disks. + Proceed boot after unlock. + options: + unlock_port: + default: 222 + description: TCP/IP port of the initial ram filesystem SSH daemon + type: "int" + luks_passphrase: + description: Passphrase to unlock encrypted LUKS disks + type: "str" + required: true + prepare: + short_description: Setup initial ram filesystem to run a SSH daemon + description: | + Install all dependencies and build a initial ram filesystem that runs a SSH daemon. + SSH will that be available to unlock LUKS encrypted disks from remote. + options: + unlock_port: + default: 222 + description: TCP/IP port of the initial ram filesystem SSH daemon + type: "int" + ssh_keys: + description: "List of ssh public keys that will be added to .ssh/authorized_keys" + type: 'list' + elements: 'str' + required: true diff --git a/roles/remote_unlock/meta/main.yml b/roles/remote_unlock/meta/main.yml new file mode 100644 index 0000000..98e2978 --- /dev/null +++ b/roles/remote_unlock/meta/main.yml @@ -0,0 +1,45 @@ +--- +galaxy_info: + author: Frank Adaemmer + description: Unlock LUKS encrypted disks from within initial ram filesystem + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: GPL-3.0-or-later + + min_ansible_version: '2.9' + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + platforms: + - name: EL + version: + - '9' + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/roles/remote_unlock/tasks/main.yml b/roles/remote_unlock/tasks/main.yml new file mode 100644 index 0000000..7ce5d4d --- /dev/null +++ b/roles/remote_unlock/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: Wait unlock port + ansible.builtin.wait_for: + host: "{{ ansible_host }}" + port: "{{ unlock_port }}" + delegate_to: localhost + +- name: Unlock + ansible.builtin.include_tasks: unlock.yml + +- name: Wait for {{ inventory_hostname }} + ansible.builtin.wait_for_connection: diff --git a/roles/remote_unlock/tasks/prepare.yml b/roles/remote_unlock/tasks/prepare.yml new file mode 100644 index 0000000..f65dfdc --- /dev/null +++ b/roles/remote_unlock/tasks/prepare.yml @@ -0,0 +1,116 @@ +--- +- name: Install EPEL + ansible.builtin.dnf: + name: + - oracle-epel-release-el9 + state: present +- name: Install Dependencies + ansible.builtin.dnf: + name: + - make + - libblkid-devel + - gcc + - git + - dropbear + state: present +- name: Clone dracut-crypt-ssh + ansible.builtin.git: + repo: https://github.com/dracut-crypt-ssh/dracut-crypt-ssh.git + dest: /opt/src/dracut-crypt-ssh + version: master + notify: Build dracut-crypt-ssh + +- name: Flush handlers + ansible.builtin.meta: flush_handlers + +- name: Create small dracut module + ansible.builtin.file: + path: /usr/lib/dracut/modules.d/99cryptsetup + state: directory + mode: 0755 + owner: root + group: root + seuser: system_u + serole: object_r + setype: bin_t + selevel: s0 + +- name: Ensure cryptsetup is installed in dracut + ansible.builtin.copy: + dest: /usr/lib/dracut/modules.d/99cryptsetup/module-setup.sh + content: | + #!/bin/bash + check() { + require_binaries cryptsetup || return 1 + return 0 + } + depends() { + return 0 + } + install() { + inst cryptsetup + dracut_need_initqueue + } + + mode: 0755 + owner: root + group: root + seuser: system_u + serole: object_r + setype: bin_t + selevel: s0 + notify: Regenerate initramfs + +- name: Configure dracut-crypt-ssh + ansible.builtin.copy: + dest: /etc/dracut.conf.d/crypt-ssh.conf + content: | + dropbear_port="{{ unlock_port }}" + # System keys are in an OpenSSH-specific format (not PEM). + # So let's stick with default GENERATE or conver all key with + # ssh-keygen -m PEM -p -f + # dropbear_rsa_key="SYSTEM" + # dropbear_ecdsa_key="SYSTEM" + # dropbear_ed25519_key="SYSTEM" + dropbear_acl="/etc/dracut.conf.d/authorized_keys" + mode: 0644 + owner: root + group: root + seuser: unconfined_u + serole: object_r + setype: etc_t + selevel: s0 + notify: Regenerate initramfs + +- name: Configure dracut authorized_keys + ansible.builtin.copy: + dest: /etc/dracut.conf.d/authorized_keys + content: | + {% for ssh_key in ssh_keys %} + {{ ssh_key }} + {% endfor %} + mode: 0600 + owner: root + group: root + seuser: unconfined_u + serole: object_r + setype: etc_t + selevel: s0 + notify: Regenerate initramfs + +- name: Get GRUB defaults + ansible.builtin.slurp: + src: /etc/default/grub + register: tmp_grub + +- name: Configure GRUB + ansible.builtin.copy: + dest: /etc/default/grub + content: "{{ + tmp_grub['content'] | b64decode | + regex_replace('rd\\.neednet=\\S+\\s', '') | + regex_replace('ip=\\S+\\s', '') | + replace('quiet', 'rd.neednet=1 ip=dhcp quiet') + }}" + mode: 0644 + notify: Regenerate GRUB diff --git a/roles/remote_unlock/tasks/unlock.yml b/roles/remote_unlock/tasks/unlock.yml new file mode 100644 index 0000000..95d477a --- /dev/null +++ b/roles/remote_unlock/tasks/unlock.yml @@ -0,0 +1,12 @@ +--- +- name: Unlock LUKS via dracut + ansible.builtin.raw: | + cat << EOF | unlock && /sbin/unlock-reap-success + {{ luks_passphrase }} + EOF + register: unlock + changed_when: unlock['rc'] == 0 + vars: + ansible_user: root + ansible_ssh_extra_args: '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + ansible_ssh_port: "{{ unlock_port }}" diff --git a/roles/remote_unlock/tests/unlock_test.yml b/roles/remote_unlock/tests/unlock_test.yml new file mode 100644 index 0000000..d85834a --- /dev/null +++ b/roles/remote_unlock/tests/unlock_test.yml @@ -0,0 +1,27 @@ +--- +- name: Remote Unlock Test + hosts: all + vars: + unlock_port: 2224 + luks_passphrase: '123456' + tasks: + - name: Prepare Unlock + ansible.builtin.include_role: + name: copyrights.on_premises.remote_unlock + tasks_from: prepare.yml + vars: + ansible_become: true + + - name: Flush handlers + ansible.builtin.meta: flush_handlers + + - name: Restart + ansible.builtin.command: + cmd: shutdown -r +1 + become: true + changed_when: result['rc'] == 0 + register: result + + - name: Unlock after reboot + ansible.builtin.include_role: + name: copyrights.on_premises.remote_unlock