From 36ba16474f13dfa44c7acac654a0e4cd4ae5e81d Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Tue, 22 Oct 2024 14:36:13 -0400 Subject: [PATCH] Add HSM Prep role --- docs/dictionary/en-custom.txt | 6 + roles/hsm_prep/README.md | 63 +++++++++ roles/hsm_prep/defaults/main.yml | 40 ++++++ .../files/image_add_luna_minimal_client.sh | 48 +++++++ roles/hsm_prep/meta/main.yml | 36 +++++ roles/hsm_prep/tasks/cleanup.yml | 19 +++ .../tasks/generate_luna_client_certs.yaml | 64 +++++++++ roles/hsm_prep/tasks/luna.yml | 124 ++++++++++++++++++ roles/hsm_prep/tasks/main.yml | 19 +++ roles/hsm_prep/templates/secret.yml.j2 | 10 ++ 10 files changed, 429 insertions(+) create mode 100644 roles/hsm_prep/README.md create mode 100644 roles/hsm_prep/defaults/main.yml create mode 100755 roles/hsm_prep/files/image_add_luna_minimal_client.sh create mode 100644 roles/hsm_prep/meta/main.yml create mode 100644 roles/hsm_prep/tasks/cleanup.yml create mode 100644 roles/hsm_prep/tasks/generate_luna_client_certs.yaml create mode 100644 roles/hsm_prep/tasks/luna.yml create mode 100644 roles/hsm_prep/tasks/main.yml create mode 100644 roles/hsm_prep/templates/secret.yml.j2 diff --git a/docs/dictionary/en-custom.txt b/docs/dictionary/en-custom.txt index 79315d66c4..28275109a8 100644 --- a/docs/dictionary/en-custom.txt +++ b/docs/dictionary/en-custom.txt @@ -23,6 +23,7 @@ autostart awk backend backends +barbican baremetal baremetalhost basedir @@ -69,6 +70,7 @@ ciuser cjeanner ckcg cli +clientvm clusterimageset clusterpool cmd @@ -199,6 +201,8 @@ hostnames hostvars hotfix href +hsm +hsms https ic icjbuue @@ -278,6 +282,8 @@ loopback losetup lsblk luks +lunaclient +lunasa lv lvm lvmcluster diff --git a/roles/hsm_prep/README.md b/roles/hsm_prep/README.md new file mode 100644 index 0000000000..04e0162bd1 --- /dev/null +++ b/roles/hsm_prep/README.md @@ -0,0 +1,63 @@ +# HSM Role + +In order to use HSMs, the barbican images need to be customized to include the HSM software. For now, this is something +that we expect customers to do with scripts that we will provide as part of the barbican-operator code. + +The purpose of this role is to: +* Generate new images for the barbican-api and barbican-worker containing the HSM software +* Upload those images to a private repository for use in setting up a CI job. +* Create any required config to be mounted by the barbican images to interact with the HSM + +For the Lunasa, we expect some preparatory steps to be completed prior to execution in order for the +role to complete successfully. +* We expect a VM that contains the Lunasa client software and that is registered as a lunasa client. + This VM should contain the following contents: + * The contents of the minimal linux client in a zipped tar file. + * The lunasa binaries that need to be added to the image under a specified directory. + * The lunasa HSM server cert. +* The above contents will be fetched by the role. +* The VM will be used to generate client certificates. For this, we will need the cifmw_hsm_client_ip , which + is the VM of the hypervisor hosting the openshift node. If a cert has already been generated, then + that certificate will be retrieved instead. +* The certs will be retrieved and stored in a secret (cifmw_hsm_luna_cert_secret) +* As input to this role, we will require the ssh connection details and credentials for this VM. + +A minimal (one that takes the defaults) invocation of this role is shown below, where the lunaclient +is the running client VM described above. + +- name: Set up Luna + hosts: lunaclient + ansible.builtin.include_role: hsm_prep + vars: + cifmw_hsm_admin_password: "" + cifmw_hsm_server_ip: "IP of HSM" + cifmw_hsm_client_ip: "IP of the client - this could be the hypervisor where the Openshift nodes run" + cifmw_hsm_luna_partition: "HSM partition for th client to join" + +## Parameters + +### HSM Details +* `cifmw_hsm_hsmtype`: (String) The type of HSM required. Currently, only "luna" is supported. Default value: `luna` +* `cifmw_hsm_admin_user`: (String) The user to log into the HSM. Default value: `admin` +* `cifmw_hsm_admin_password`: (String) The password to log into the HSM. +* `cifmw_hsm_server_ip`: (String) ip address or hostname of the HSM + +### Barbican Image and Details to get buildah script +* `cifmw_hsm_barbican_operator_repo`: (String) Repo for barbican-operator. Default value: "https://github.com/openstack-k8s-operators/barbican-operator.git" +* `cifmw_hsm_barbican_operator_version`: (String) Version for barbican-operator. Default value: "main" +* `cifmv_hsm_barbican_image_namespace`: (String) Namespace for barbican-operator source image. Default value: "podified-antelope-centos9" +* `cifmw_hsm_barbican_image_tag`: (String) Tag for barbican-operator source image. Default value: "current-podified" + +### Role Parameters +* `cifmw_hsm_cleanup`: (Boolean) Delete all resources created by the role at the end of the testing. Default value: `false` +* `cifmw_hsm_working_dir`: (String) Working directory to store artifacts. Default value: `/tmp/hsm-prep-working-dir` +* `cifmw_hsm_client_ip`: (String) ip address or hostname of the client VM + +### Luna Parameters +* `cifmw_hsm_luna_minclient_src`: (String) Location of linux minimal client tarball on the luna client VM. Default value: `/opt/data/Linux-Minimal-Client.tar.gz` +* `cifmw_hsm_luna_binaries_src`: (String) Location of the luna binaries on the luna client VM. Default value: `/opt/data/bin` +* `cifmw_hsm_luna_server_cert_src`: (String) Location of HSM server cert on the luna client VM. Default value: `/usr/safenet/lunaclient/cert/server` +* `cifmw_hsm_luna_client_cert_src`: (String) Location of HSM client cert on the luna client VM. Default value: `/usr/safenet/lunaclient/cert/client` +* `cifmw_hsm_luna_cert_secret`: (String) Name of the secret that stores all of the needed certs for luna. Default value: `barbican-luna-certs` +* `cifmw_hsm_luna_cert_secret_namespace`: (String) Namespace of the secret that stores all of the needed certs for luna. Default value: `openstack` +* `cifmw_hsm_luna_partition`: (String) HSM partition for the client to join. diff --git a/roles/hsm_prep/defaults/main.yml b/roles/hsm_prep/defaults/main.yml new file mode 100644 index 0000000000..9a69aed6f9 --- /dev/null +++ b/roles/hsm_prep/defaults/main.yml @@ -0,0 +1,40 @@ +--- +# Copyright Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +# defaults file for hsm role + +# HSM Details +cifmw_hsm_hsmtype: "luna" +cifmw_hsm_admin_user: "admin" + +### Barbican Image and Details to get buildah script +cifmw_hsm_barbican_operator_repo: "https://github.com/openstack-k8s-operators/barbican-operator.git" +cifmw_hsm_barbican_operator_version: "main" +cifmv_hsm_barbican_image_namespace: "podified-antelope-centos9" +cifmw_hsm_barbican_image_tag: "current-podified" + +### Role Parameters +cifmw_hsm_cleanup: false +cifmw_hsm_working_dir: "/tmp/hsm-prep-working-dir" + +### Luna Parameters +cifmw_hsm_luna_minclient_src: "/opt/data/Linux-Minimal-Client.tar.gz" +cifmw_hsm_luna_binaries_src: "/opt/data/bin" +cifmw_hsm_luna_server_cert_src: "/usr/safenet/lunaclient/cert/server" +cifmw_hsm_luna_client_cert_src: "/usr/safenet/lunaclient/cert/client" +cifmw_hsm_luna_cert_secret: "barbican-luna-certs" +cifmw_hsm_luna_cert_secret_namespace: "openstack" diff --git a/roles/hsm_prep/files/image_add_luna_minimal_client.sh b/roles/hsm_prep/files/image_add_luna_minimal_client.sh new file mode 100755 index 0000000000..d82979c51f --- /dev/null +++ b/roles/hsm_prep/files/image_add_luna_minimal_client.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# barbican_add_luna_minimal_client.sh +# +# This script adds the Linux Minimal Client for Thales Luna Network HSM +# to both the API and Worker images so that the HSM can be used as a PKCS#11 +# backend for Barbican. + +set -o errexit +set -o pipefail + +BARBICAN_IMAGE_NAMESPACE=${BARBICAN_IMAGE_NAMESPACE:-"podified-antelope-centos9"} +BARBICAN_IMAGE_TAG=${BARBICAN_IMAGE_TAG:-"current-podified"} +BARBICAN_API_IMAGE="quay.io/$BARBICAN_IMAGE_NAMESPACE/openstack-barbican-api:$BARBICAN_IMAGE_TAG" +BARBICAN_WORKER_IMAGE="quay.io/$BARBICAN_IMAGE_NAMESPACE/openstack-barbican-worker:$BARBICAN_IMAGE_TAG" + +# LUNA_LINUX_MINIMAL_CLIENT_DIR - location of the "linux-minimal" directory +# in your client media. This could be a path to a mounted ISO or a path to +# the location where a tarball was extracted +LUNA_LINUX_MINIMAL_CLIENT_DIR=${LUNA_LINUX_MINIMAL_CLIENT_DIR:-"/media/lunaiso/linux-minimal"} + +# LUNA_CLIENT_BIN - location of the binaries installed by the client +# software. +LUNA_CLIENT_BIN=${LUNA_CLIENT_BIN:-"/usr/safenet/lunaclient/bin"} + +function install_client() { + + container=$(buildah from $1) + + # set required env + buildah config --env ChrystokiConfigurationPath=/usr/local/luna $container + + # add linux-minimal client + buildah add --chown root:root $container $LUNA_LINUX_MINIMAL_CLIENT_DIR /usr/local/luna + buildah run --user root $container -- mkdir -p /usr/local/luna/config/certs + buildah run --user root $container -- mkdir -p /usr/local/luna/config/token/001 + buildah run --user root $container -- touch /usr/local/luna/config/token/001/token.db + buildah add --chown root:root $container $LUNA_CLIENT_BIN/lunacm /usr/local/bin/ + buildah add --chown root:root $container $LUNA_CLIENT_BIN/vtl /usr/local/bin/ + buildah add --chown root:root $container $LUNA_CLIENT_BIN/multitoken /usr/local/bin/ + buildah add --chown root:root $container $LUNA_CLIENT_BIN/ckdemo /usr/local/bin/ + + buildah commit $container ${1}-luna + buildah rm $container +} + +install_client $BARBICAN_API_IMAGE +install_client $BARBICAN_WORKER_IMAGE diff --git a/roles/hsm_prep/meta/main.yml b/roles/hsm_prep/meta/main.yml new file mode 100644 index 0000000000..8130a40a64 --- /dev/null +++ b/roles/hsm_prep/meta/main.yml @@ -0,0 +1,36 @@ +--- +# Copyright Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +galaxy_info: + author: CI Framework + description: CI Framework Role -- HSM Prep + company: Red Hat + license: Apache-2.0 + min_ansible_version: 2.14 + namespace: cifmw + platforms: + - name: CentOS + versions: + - 9 + + galaxy_tags: + - cifmw + - hsm + +# List your role dependencies here, one per line. Be sure to remove the '[]' above, +# if you add dependencies to this list. +dependencies: [] diff --git a/roles/hsm_prep/tasks/cleanup.yml b/roles/hsm_prep/tasks/cleanup.yml new file mode 100644 index 0000000000..2f442c4877 --- /dev/null +++ b/roles/hsm_prep/tasks/cleanup.yml @@ -0,0 +1,19 @@ +--- +# Copyright Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Log debug tasks + ansible.builtin.debug: + msg: "Running cleanup tasks here" diff --git a/roles/hsm_prep/tasks/generate_luna_client_certs.yaml b/roles/hsm_prep/tasks/generate_luna_client_certs.yaml new file mode 100644 index 0000000000..db800cee8d --- /dev/null +++ b/roles/hsm_prep/tasks/generate_luna_client_certs.yaml @@ -0,0 +1,64 @@ +- name: Set fact for client_name + ansible.builtin.set_fact: + client_name: "{{ cifmw_hsm_client_ip }}" + +- name: Generate a new client cert for NTL + ansible.builtin.command: /usr/safenet/lunaclient/bin/vtl createCert -n "{{ cifmw_hsm_client_ip }}" + become: true + +- name: Get the hsm server cert from the hsm_server + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' + scp -O -o StrictHostKeyChecking=false -c aes256-cbc + {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }}:server.pem + /usr/safenet/lunaclient/bin/{{ cifmw_hsm_server_ip }}.pem + args: + creates: /usr/safenet/lunaclient/bin/{{ cifmw_hsm_server_ip }}.pem + become: true + +- name: Check for existing clients + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' + ssh -o StrictHostKeyChecking=false -c aes256-cbc + {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }} + -C client list + register: client_list + +- name: Delete existing client when rotating certs + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' + ssh -c aes256-cbc {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }} + -C "client delete -f -c {{ client_name }}" + when: + - client_name in client_list.stdout + +- name: Register the client certificate on the hsm_server + become: true + when: client_name not in client_list.stdout + block: + - name: Copy the NTL client cert to the HSM + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' scp -O -c aes256-cbc + /usr/safenet/lunaclient/cert/client/{{ cifmw_hsm_client_ip }}.pem + {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }}:{{ cifmw_hsm_client_ip }}.pem + + - name: Register the client + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' + ssh -c aes256-cbc {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }} + -C "client register -c {{ client_name }} -hostname {{ cifmw_hsm_client_ip }}" + register: client_register + failed_when: + - client_register.rc != 0 + - "'client with the same IP address has already been registered' not in client_register.stdout" + +- name: Assign client to an HSM partition + ansible.builtin.shell: > + sshpass -p '{{ cifmw_hsm_admin_password }}' + ssh -c aes256-cbc {{ cifmw_hsm_admin_user }}@{{ cifmw_hsm_server_ip }} + -C "client assignPartition -c {{ client_name }} -p {{ cifmw_hsm_luna_partition }}" + register: assign_partition + failed_when: + - assign_partition.rc != 0 + - "'client already has access' not in assign_partition.stdout" + become: true diff --git a/roles/hsm_prep/tasks/luna.yml b/roles/hsm_prep/tasks/luna.yml new file mode 100644 index 0000000000..d617e18c25 --- /dev/null +++ b/roles/hsm_prep/tasks/luna.yml @@ -0,0 +1,124 @@ +--- +# Copyright Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Create working directories + delegate_to: localhost + ansible.builtin.file: + path: "{{ item }}" + state: directory + recurse: true + mode: '755' + loop: + - "{{ cifmw_hsm_working_dir }}" + - "{{ cifmw_hsm_working_dir }}/bin" + - "{{ cifmw_hsm_working_dir }}/certs" + - "{{ cifmw_hsm_working_dir }}/client" + - "{{ cifmw_hsm_working_dir }}/barbican_operator" + +- name: Extract luna software + block: + - name: Fetch the linux minimal client + ansible.builtin.fetch: + src: "{{ cifmw_hsm_luna_minclient_src }}" + dest: "{{ cifmw_hsm_working_dir }}/" + flat: true + + - name: Extract the minimal client + delegate_to: localhost + ansible.builtin.unarchive: + src: "{{ cifmw_hsm_working_dir }}/{{ cifmw_hsm_luna_minclient_src | basename }}" + dest: "{{ cifmw_hsm_working_dir }}/client/" + + - name: Fetch the binaries + ansible.builtin.fetch: + src: "{{ cifmw_hsm_luna_binaries_src }}/{{ item}}" + dest: "{{ cifmw_hsm_working_dir }}/bin/" + flat: true + loop: + - "vtl" + - "lunacm" + - "multitoken" + - "ckdemo" + + - name: Fetch the server cert + become: true + ansible.builtin.fetch: + src: "{{ cifmw_hsm_luna_server_cert_src }}/{{ cifmw_hsm_server_ip }}Cert.pem" + dest: "{{ cifmw_hsm_working_dir }}/certs/" + flat: true + +- name: Download build tools + delegate_to: localhost + become: true + ansible.builtin.dnf: + name: + - buildah + - podman + state: present + +- name: Clone barbican-operator to get buildah script + delegate_to: localhost + ansible.builtin.git: + repo: "{{ cifmw_hsm_barbican_operator_repo }}" + dest: "{{ cifmw_hsm_working_dir}}/barbican_operator" + version: "{{ cifmw_hsm_barbican_operator_version }}" + +# TODO(alee) Use the buildah script from the barbican-operator once it has merged +- name: Create and store new images + delegate_to: localhost + # ansible.builtin.command: "{{ cifmw_hsm_working_dir}}/barbican_operator/hack/image_add_luna_minimal_client.sh" + ansible.builtin.script: "image_add_luna_minimal_client.sh" + environment: + BARBICAN_IMAGE_NAMESPACE: "{{ cifmv_hsm_barbican_image_namespace }}" + BARBICAN_IMAGE_TAG: "{{ cifmw_hsm_barbican_image_tag }}" + LUNA_LINUX_MINIMAL_CLIENT_DIR: "{{ cifmw_hsm_working_dir }}/client" + LUNA_CLIENT_BIN: "{{ cifmw_hsm_working_dir }}/bin" + +- name: Get client certs and keys + become: true + block: + - name: Check if client cert exists + ansible.builtin.stat: + path: "{{ cifmw_hsm_luna_client_cert_src }}/{{ cifmw_hsm_client_ip }}.pem" + register: client_cert_stat + + - name: Check if client key exists + ansible.builtin.stat: + path: "{{ cifmw_hsm_luna_client_cert_src }}/{{ cifmw_hsm_client_ip }}Key.pem" + register: client_key_stat + + - name: Generate certs if they do not already exist + ansible.builtin.include_tasks: generate_luna_client_certs.yaml + when: not client_cert_stat.stat.exists or not client_key_stat.stat.exists + + - name: Fetch the client cert and key + ansible.builtin.fetch: + src: "{{ cifmw_hsm_luna_client_cert_src }}/{{ item }}" + dest: "{{ cifmw_hsm_working_dir }}/certs/" + flat: true + loop: + - "{{ cifmw_hsm_client_ip }}.pem" + - "{{ cifmw_hsm_client_ip }}Key.pem" + +- name: Write out secret template file + delegate_to: localhost + ansible.builtin.template: + src: "secret.yml.j2" + dest: "{{ cifmw_hsm_working_dir }}/secret.yml" + +- name: Create the secret + delegate_to: localhost + ansible.builtin.command: "oc apply -f {{ cifmw_hsm_working_dir }}/secret.yml" diff --git a/roles/hsm_prep/tasks/main.yml b/roles/hsm_prep/tasks/main.yml new file mode 100644 index 0000000000..c2b69d1a03 --- /dev/null +++ b/roles/hsm_prep/tasks/main.yml @@ -0,0 +1,19 @@ +--- +# Copyright Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +- name: Run luna tasks + ansible.builtin.include_tasks: luna.yml + when: cifmw_hsm_hsmtype == "luna" diff --git a/roles/hsm_prep/templates/secret.yml.j2 b/roles/hsm_prep/templates/secret.yml.j2 new file mode 100644 index 0000000000..c1444f2a61 --- /dev/null +++ b/roles/hsm_prep/templates/secret.yml.j2 @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: "{{ cifmw_hsm_luna_cert_secret }}" + namespace: "{{ cifmw_hsm_luna_cert_secret_namespace }}" +data: + "{{ cifmw_hsm_client_ip }}.pem": "{{ lookup('ansible.builtin.file', cifmw_hsm_working_dir + '/certs/' + cifmw_hsm_client_ip + '.pem') | string | b64encode }}" + "{{ cifmw_hsm_client_ip }}Key.pem": "{{ lookup('ansible.builtin.file', cifmw_hsm_working_dir + '/certs/' + cifmw_hsm_client_ip + 'Key.pem') | string | b64encode }}" + "{{ cifmw_hsm_server_ip }}.pem": "{{ lookup('ansible.builtin.file', cifmw_hsm_working_dir + '/certs/' + cifmw_hsm_server_ip + 'Cert.pem') | string | b64encode }}"