heat_template_version: rocky description: > HAProxy deployment with TLS enabled, with an injected certificate parameters: ServiceData: default: {} description: Dictionary packing service data type: json ServiceNetMap: default: {} description: Mapping of service_name -> network name. Typically set via parameter_defaults in the resource registry. This mapping overrides those in ServiceNetMapDefaults. type: json DefaultPasswords: default: {} type: json RoleName: default: '' description: Role name on which the service is applied type: string RoleParameters: default: {} description: Parameters specific to the role type: json EndpointMap: default: {} description: Mapping of service endpoint -> protocol. Typically set via parameter_defaults in the resource registry. type: json # Can be overridden via parameter_defaults in the environment SSLCertificate: default: '' description: > The content of the SSL certificate (without Key) in PEM format. type: string SSLIntermediateCertificate: default: '' description: > The content of an SSL intermediate CA certificate in PEM format. type: string # NOTE(jaosorior): Adding this default is only while we enable TLS by default # for the overcloud. It'll be removed in a subsequent patch. SSLKey: default: '' description: > The content of the SSL Key in PEM format. type: string hidden: true DeployedSSLCertificatePath: default: '/etc/pki/tls/private/overcloud_endpoint.pem' description: > The filepath of the certificate as it will be stored in the controller. type: string outputs: role_data: description: Role data for the HAProxy public TLS injection. value: service_name: haproxy_public_tls_inject config_settings: {} certificates_specs: {} metadata_settings: null host_prep_tasks: - name: get parameters set_fact: cert_path: {get_param: DeployedSSLCertificatePath} cert_content: {get_param: SSLCertificate} chain_content: {get_param: SSLIntermediateCertificate} key_content: {get_param: SSLKey} no_log: true # We want to ensure we run all this block IFF we have # a certificate content. - name: manage certificate when: - cert_content is defined - cert_content != '' block: - name: get DeployedSSLCertificatePath attributes register: attr_cert_path stat: path: "{{cert_path}}" - name: set is_haproxy_bootstrap_node fact set_fact: is_haproxy_bootstrap_node={{haproxy_short_bootstrap_node_name | lower == ansible_hostname | lower}} - name: get haproxy status register: haproxy_state systemd: name: haproxy - name: get pacemaker status register: pacemaker_state systemd: name: pacemaker - name: get docker status register: docker_state systemd: name: docker - name: get container_id when: - docker_state.status.ActiveState == 'active' or container_cli == 'podman' - attr_cert_path.stat.exists - attr_cert_path.stat.isdir == False command: "{{ container_cli }} ps -q -f name=haproxy" register: container_id - name: get pcs resource name for haproxy container when: - bootstrap_node is defined - is_haproxy_bootstrap_node - pacemaker_state.status.ActiveState == 'active' - attr_cert_path.stat.exists - attr_cert_path.stat.isdir shell: | pcs status resources | sed -n 's/^.*container.*: \(haproxy.*\) .*/\1/p' register: pacemaker_resource # It might happen docker has started priori the file creation - it will then create a # directory. We have to drop that directory in order to push our file. - name: remove DeployedSSLCertificatePath if is dir when: attr_cert_path.stat.isdir is defined and attr_cert_path.stat.isdir file: path: "{{cert_path}}" state: absent # In containerized env, haproxy group does not exist. # We hence need to do this file creation in two distinct steps # and ignore failure on the ownership change. - name: push certificate content no_log: true copy: dest: "{{cert_path}}" mode: 0440 owner: root content: | {{cert_content}} {{chain_content}} {{key_content}} # Set certificate group IFF we're not in container context # Also, restart HAProxy service without more concern - name: BM haproxy non-pacemaker context when: haproxy_state.status.ActiveState == 'active' block: - name: set certificate ownership file: path: "{{cert_path}}" group: haproxy - name: reload haproxy if enabled service: name: haproxy state: reloaded - name: restart pacemaker resource for haproxy when: - pacemaker_resource is defined - pacemaker_resource.stdout is defined - pacemaker_resource.stdout != '' command: pcs resource restart "{{pacemaker_resource.stdout}}" # We can't use kolla for certificate updates, so we have to # set its rights and restart the service. - name: dedicated part for containers when: - container_id is defined - container_id.stdout is defined - container_id.stdout != '' block: - name: set kolla_dir fact set_fact: kolla_dir="/var/lib/kolla/config_files/src-tls" - name: assert {{ kolla_dir }}{{ cert_path }} exists stat: path: "{{ kolla_dir }}{{cert_path}}" register: kolla_cert_exists - name: set certificate group on host via container shell: | if {{ container_cli }} ps | grep {{container_id.stdout}}; then {{ container_cli }} exec {{container_id.stdout}} chgrp haproxy {{kolla_dir}}{{cert_path}} # handle situation when container_cli is podman but # the containers are still under docker elif docker ps | grep {{container_id.stdout}}; then docker exec {{container_id.stdout}} chgrp haproxy {{kolla_dir}}{{cert_path}} else echo "Error: {{container_id.stdout}} container not found" exit 1 fi when: kolla_cert_exists.stat.exists - name: copy certificate from kolla directory to final location shell: | if {{ container_cli }} ps | grep {{container_id.stdout}}; then {{ container_cli }} exec {{container_id.stdout}} cp {{kolla_dir}}{{cert_path}} {{cert_path}} # handle situation when container_cli is podman but # the containers are still under docker elif docker ps | grep {{container_id.stdout}}; then docker exec {{container_id.stdout}} cp {{kolla_dir}}{{cert_path}} {{cert_path}} else echo "Error: {{container_id.stdout}} container not found" exit 1 fi when: kolla_cert_exists.stat.exists - name: send restart order to haproxy container command: "{{ container_cli }} kill --signal=HUP {{container_id.stdout}}" when: kolla_cert_exists.stat.exists