diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 57d195a68a..a36764eb84 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -402,6 +402,28 @@ grafana_server_listen_port: "{{ grafana_server_port }}"
haproxy_stats_port: "1984"
haproxy_monitor_port: "61313"
haproxy_ssh_port: "2985"
+# configure SSL/TLS settings for haproxy config, one of [modern, intermediate, legacy]:
+kolla_haproxy_ssl_settings: "modern"
+
+haproxy_ssl_settings: "{{ ssl_legacy_settings if kolla_haproxy_ssl_settings == 'legacy' else ssl_intermediate_settings if kolla_haproxy_ssl_settings == 'intermediate' else ssl_modern_settings | default(ssl_modern_settings) }}"
+
+ssl_legacy_settings: |
+ ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
+ ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
+
+ssl_intermediate_settings: |
+ ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
+ ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
+ ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
+ ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
+ ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
+ ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
+
+ssl_modern_settings: |
+ ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
+ ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
+ ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
+ ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
heat_internal_fqdn: "{{ kolla_internal_fqdn }}"
heat_external_fqdn: "{{ kolla_external_fqdn }}"
diff --git a/ansible/roles/glance/templates/glance-tls-proxy.cfg.j2 b/ansible/roles/glance/templates/glance-tls-proxy.cfg.j2
index a6b3aa0613..f424757488 100644
--- a/ansible/roles/glance/templates/glance-tls-proxy.cfg.j2
+++ b/ansible/roles/glance/templates/glance-tls-proxy.cfg.j2
@@ -10,9 +10,11 @@ global
{% if (glance_tls_proxy_threads | int > 1) and (glance_tls_proxy_thread_cpu_map | bool) %}
cpu-map auto:1/all 0-63
{% endif %}
- ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
- ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
+ {% if kolla_enable_tls_external | bool or kolla_enable_tls_internal | bool %}
+ {{ haproxy_ssl_settings }}
tune.ssl.default-dh-param 4096
+ ca-base {{ haproxy_backend_cacert_dir }}
+ {% endif %}
defaults
log global
diff --git a/ansible/roles/loadbalancer/templates/haproxy/haproxy_main.cfg.j2 b/ansible/roles/loadbalancer/templates/haproxy/haproxy_main.cfg.j2
index ef4638d81e..66427f19ec 100644
--- a/ansible/roles/loadbalancer/templates/haproxy/haproxy_main.cfg.j2
+++ b/ansible/roles/loadbalancer/templates/haproxy/haproxy_main.cfg.j2
@@ -13,8 +13,7 @@ global
stats socket /var/lib/kolla/haproxy/haproxy.sock group kolla mode 660{% if haproxy_socket_level_admin | bool %} level admin{% endif %}
{% if kolla_enable_tls_external | bool or kolla_enable_tls_internal | bool %}
- ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
- ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
+ {{ haproxy_ssl_settings }}
tune.ssl.default-dh-param 4096
ca-base {{ haproxy_backend_cacert_dir }}
{% endif %}
diff --git a/ansible/roles/neutron/templates/neutron-tls-proxy.cfg.j2 b/ansible/roles/neutron/templates/neutron-tls-proxy.cfg.j2
index 5e11d43465..caa05759a6 100644
--- a/ansible/roles/neutron/templates/neutron-tls-proxy.cfg.j2
+++ b/ansible/roles/neutron/templates/neutron-tls-proxy.cfg.j2
@@ -10,9 +10,11 @@ global
{% if (neutron_tls_proxy_threads | int > 1) and (neutron_tls_proxy_thread_cpu_map | bool) %}
cpu-map auto:1/all 0-63
{% endif %}
- ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
- ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
+ {% if kolla_enable_tls_external | bool or kolla_enable_tls_internal | bool %}
+ {{ haproxy_ssl_settings }}
tune.ssl.default-dh-param 4096
+ ca-base {{ haproxy_backend_cacert_dir }}
+ {% endif %}
defaults
log global
diff --git a/doc/source/admin/tls.rst b/doc/source/admin/tls.rst
index 2810ecdc83..d662d6c2e7 100644
--- a/doc/source/admin/tls.rst
+++ b/doc/source/admin/tls.rst
@@ -363,3 +363,29 @@ options for TLS as is.
If using this option, make sure that all certificates are present on the
appropriate hosts in the appropriate location.
+
+.. _haproxy-tls-settings:
+
+HAProxy TLS related settings
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can select between different SSL/TLS ciphers by setting the following
+in ``/etc/kolla/globals.yml``:
+
+.. code-block:: yaml
+
+ kolla_haproxy_ssl_settings: "modern" # or "intermediate" or "legacy"
+
+The default value is "modern". These settings are adapted from the
+`Mozilla SSL Configuration Generator `__.
+
+The setting "modern" is recommended for most deployments. The setting
+"intermediate" is recommended for deployments that need to support older
+clients. The setting "legacy" is not recommended, but is left as a
+compatibility option for older deployments.
+
+See the `Mozilla SSL Configuration Generator `__
+for more information on exact supported client versions.
+
+The ``kolla_haproxy_ssl_settings`` setting also affects the glance and
+neutron haproxy TLS settings, if these proxy services are enabled.
diff --git a/doc/source/reference/high-availability/haproxy-guide.rst b/doc/source/reference/high-availability/haproxy-guide.rst
index e5327e70b0..0d1219789c 100644
--- a/doc/source/reference/high-availability/haproxy-guide.rst
+++ b/doc/source/reference/high-availability/haproxy-guide.rst
@@ -92,3 +92,8 @@ disabled by setting the following in ``/etc/kolla/globals.yml``:
.. code-block:: yaml
haproxy_enable_http2: "no"
+
+SSL/TLS Settings
+----------------
+
+For SSL/TLS related settings refer to the :ref:`haproxy-tls-settings` section.
diff --git a/releasenotes/notes/harden_haproxy_tls_config-6a70503d8a124b2a.yaml b/releasenotes/notes/harden_haproxy_tls_config-6a70503d8a124b2a.yaml
new file mode 100644
index 0000000000..d5a90ccb3a
--- /dev/null
+++ b/releasenotes/notes/harden_haproxy_tls_config-6a70503d8a124b2a.yaml
@@ -0,0 +1,28 @@
+---
+features:
+ - |
+ Harden the HAProxy TLS default configuration according to the mozilla
+ ``modern`` recommendation:
+
+ ``__
+
+ If you want to revert back to the old behaviour, e.g. because
+ you have old clients, you can do so by setting the following
+ variable in your globals.yml:
+
+ ``kolla_haproxy_ssl_settings: legacy`` or if you want to have
+ at least some improved security settings:
+ ``kolla_haproxy_ssl_settings: intermediate``
+
+ See `LP#2060787 `__
+upgrade:
+ - |
+ If you have old clients that do not support the new TLS settings,
+ you can revert back to the old behaviour by setting the following
+ variable in your globals.yml:
+
+ ``kolla_haproxy_ssl_settings: legacy`` or if you want to have
+ at least some improved security settings:
+ ``kolla_haproxy_ssl_settings: intermediate``
+
+ See `LP#2060787 `__