tripleo-heat-templates/deployment/haproxy/haproxy-public-tls-inject.yaml
Damien Ciabrini 63001263ad HA: inject public certificates without blocking container
Do not inject public certificates in pacemaker bundles by means
of "podman cp", as this pauses the container for a short amount
of time and can make pacemaker operation fail during that time
window and impact cluster for no reason.

Keep "podman cp" for non-HA containers, as the freeze is short
and doesn't seem to impact podman monitoring anyway.

The new certificate injection only works for podman 1.9+, lower
version won't overwrite the existing certificate.

(cherry-picked from 93e53b7429)
(squashed with Ic6e4264c5ad46bd2589cc907c365af2d42fde63d)
(removed a part that should stay in puppet-tripleo before wallaby)

Closes-Bug: #1917868

Change-Id: Id7308f028f33716be5e3df6699c3f2c12e33e344
2021-03-31 19:33:18 +02:00

189 lines
7.0 KiB
YAML

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: "{{ hide_sensitive_logs | bool }}"
# 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_facts['hostname'] | lower}}
when:
- haproxy_short_bootstrap_node_name|default(false)
- 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: "{{ hide_sensitive_logs | bool }}"
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: copy certificate, chgrp, restart haproxy
shell: |
set -e
if {{ container_cli }} ps -f "id={{ item }}" --format "{{ '{{' }}.Names{{ '}}' }}" | grep -q "^haproxy-bundle"; then
tar -c {{ cert_path }} | {{container_cli}} exec -i {{ item }} tar -C / -xv
else
{{ container_cli }} cp {{ cert_path }} {{ item }}:{{ cert_path }}
fi
{{ container_cli }} exec --user root {{ item }} chgrp haproxy {{ cert_path }}
{{ container_cli }} kill --signal=HUP {{ item }}
with_items: "{{ container_id.stdout.split('\n') }}"