Add TLS proxy for novncproxy

This improvement enables TLS encryption for the novncproxy service, even
when enable_haproxy is False. This change is beneficial for operators
who run deployments using an external Load Balancer with enable_haproxy
set to False, but still require TLS encryption for the novncproxy
service.

Signed-off-by: Robert Breker
Signed-off-by: Oliver Schmitz

Change-Id: Ibb33aaf200f460c83ddeb9d0656ccaa6a655f142
This commit is contained in:
Robert Breker 2024-03-04 11:15:36 -06:00 committed by Kevin Tindall
parent cff75f6eac
commit f81095f367
No known key found for this signature in database
8 changed files with 203 additions and 3 deletions

View File

@ -510,6 +510,7 @@ nova_metadata_internal_fqdn: "{{ kolla_internal_fqdn }}"
nova_metadata_external_fqdn: "{{ kolla_external_fqdn }}"
nova_metadata_port: "8775"
nova_metadata_listen_port: "{{ nova_metadata_port }}"
nova_novncproxy_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
nova_novncproxy_fqdn: "{{ kolla_external_fqdn }}"
nova_novncproxy_port: "6080"
nova_novncproxy_listen_port: "{{ nova_novncproxy_port }}"

View File

@ -76,6 +76,7 @@ fluentd_image_full: "{{ fluentd_image }}:{{ fluentd_tag }}"
syslog_swift_facility: "local0"
syslog_haproxy_facility: "local1"
syslog_glance_tls_proxy_facility: "local2"
syslog_nova_novncproxy_tls_proxy_facility: "local3"
syslog_neutron_tls_proxy_facility: "local4"
syslog_facilities:
@ -96,6 +97,11 @@ syslog_facilities:
facility: "{{ syslog_glance_tls_proxy_facility }}"
logdir: "glance-tls-proxy"
logfile: "glance-tls-proxy"
- name: "nova_novncproxy_tls_proxy"
enabled: "{{ nova_novncproxy_enable_tls_backend | bool and inventory_hostname in groups['neutron-server'] }}"
facility: "{{ syslog_nova_novncproxy_tls_proxy_facility }}"
logdir: "nova-novncproxy-tls-proxy"
logfile: "nova-novncproxy-tls-proxy"
- name: "neutron_tls_proxy"
enabled: "{{ neutron_enable_tls_backend | bool and inventory_hostname in groups['neutron-server'] }}"
facility: "{{ syslog_neutron_tls_proxy_facility }}"

View File

@ -27,6 +27,30 @@ nova_cell_services:
volumes: "{{ nova_novncproxy_default_volumes + nova_novncproxy_extra_volumes }}"
dimensions: "{{ nova_novncproxy_dimensions }}"
healthcheck: "{{ nova_novncproxy_healthcheck }}"
nova-novncproxy-tls-proxy:
container_name: "nova_novncproxy_tls_proxy"
group: "{{ nova_cell_novncproxy_group }}"
host_in_groups: "{{ inventory_hostname in groups[nova_cell_novncproxy_group] }}"
enabled: "{{ nova_console == 'novnc' }}"
image: "{{ nova_novncproxy_tls_proxy_image_full }}"
volumes: "{{ nova_novncproxy_tls_proxy_default_volumes + nova_novncproxy_tls_proxy_extra_volumes }}"
dimensions: "{{ nova_novncproxy_tls_proxy_dimensions }}"
healthcheck: "{{ nova_novncproxy_tls_proxy_healthcheck }}"
haproxy:
nova_novncproxy_tls_proxy:
enabled: "{{ nova_console == 'novnc' and nova_novncproxy_enable_tls_backend | bool }}"
mode: "http"
external: false
port: "{{ nova_novncproxy_port }}"
custom_member_list: "{{ haproxy_tls_members.split(';') }}"
tls_backend: "yes"
nova_novncproxy_tls_proxy_external:
enabled: "{{ nova_console == 'novnc' and nova_novncproxy_enable_tls_backend | bool }}"
mode: "http"
external: true
port: "{{ nova_novncproxy_port }}"
custom_member_list: "{{ haproxy_tls_members.split(';') }}"
tls_backend: "yes"
nova-spicehtml5proxy:
container_name: "nova_spicehtml5proxy"
group: "{{ nova_cell_spicehtml5proxy_group }}"
@ -78,6 +102,11 @@ nova_cell_config_validation:
- generator: "/nova/etc/nova/nova-config-generator.conf"
config: "/etc/nova/nova.conf"
####################
# HAProxy
####################
haproxy_tls_members: "{% for host in groups[nova_cell_rpc_group_name] %}server {{ hostvars[host].ansible_facts.hostname }} {{ 'api' | kolla_address(host) }}:{{ nova_novncproxy_listen_port }} check inter 2000 rise 2 fall 5 ssl verify required ca-file {{ haproxy_backend_cacert }};{% endfor %}"
####################
# Ceph options
####################
@ -221,6 +250,7 @@ nova_cell_notify_rabbitmq_users:
####################
# Docker
####################
haproxy_tag: "{{ openstack_tag }}"
nova_tag: "{{ openstack_tag }}"
nova_libvirt_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/nova-libvirt"
@ -236,6 +266,10 @@ nova_novncproxy_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{
nova_novncproxy_tag: "{{ nova_tag }}"
nova_novncproxy_image_full: "{{ nova_novncproxy_image }}:{{ nova_novncproxy_tag }}"
nova_novncproxy_tls_proxy_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/haproxy"
nova_novncproxy_tls_proxy_tag: "{{ haproxy_tag }}"
nova_novncproxy_tls_proxy_image_full: "{{ nova_novncproxy_tls_proxy_image }}:{{ nova_novncproxy_tls_proxy_tag }}"
nova_spicehtml5proxy_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/nova-spicehtml5proxy"
nova_spicehtml5proxy_tag: "{{ nova_tag }}"
nova_spicehtml5proxy_image_full: "{{ nova_spicehtml5proxy_image }}:{{ nova_spicehtml5proxy_tag }}"
@ -269,6 +303,7 @@ nova_libvirt_default_dimensions:
nova_libvirt_dimensions: "{{ default_container_dimensions | combine(nova_libvirt_default_dimensions, recursive=True) }}"
nova_ssh_dimensions: "{{ default_container_dimensions }}"
nova_novncproxy_dimensions: "{{ default_container_dimensions }}"
nova_novncproxy_tls_proxy_dimensions: "{{ default_container_dimensions }}"
nova_spicehtml5proxy_dimensions: "{{ default_container_dimensions }}"
nova_serialproxy_dimensions: "{{ default_container_dimensions }}"
nova_conductor_dimensions: "{{ default_container_dimensions }}"
@ -305,7 +340,7 @@ nova_novncproxy_enable_healthchecks: "{{ enable_container_healthchecks }}"
nova_novncproxy_healthcheck_interval: "{{ default_container_healthcheck_interval }}"
nova_novncproxy_healthcheck_retries: "{{ default_container_healthcheck_retries }}"
nova_novncproxy_healthcheck_start_period: "{{ default_container_healthcheck_start_period }}"
nova_novncproxy_healthcheck_test: ["CMD-SHELL", "healthcheck_curl http://{{ api_interface_address | put_address_in_context('url') }}:{{ nova_novncproxy_listen_port }}/vnc_lite.html"]
nova_novncproxy_healthcheck_test: ["CMD-SHELL", "healthcheck_curl http://{% if nova_novncproxy_enable_tls_backend | bool %}localhost{% else %}{{ api_interface_address | put_address_in_context('url') }}{% endif %}:{{ nova_novncproxy_listen_port }}/vnc_lite.html"]
nova_novncproxy_healthcheck_timeout: "{{ default_container_healthcheck_timeout }}"
nova_novncproxy_healthcheck:
interval: "{{ nova_novncproxy_healthcheck_interval }}"
@ -314,6 +349,19 @@ nova_novncproxy_healthcheck:
test: "{% if nova_novncproxy_enable_healthchecks | bool %}{{ nova_novncproxy_healthcheck_test }}{% else %}NONE{% endif %}"
timeout: "{{ nova_novncproxy_healthcheck_timeout }}"
nova_novncproxy_tls_proxy_enable_healthchecks: "{{ enable_container_healthchecks }}"
nova_novncproxy_tls_proxy_healthcheck_interval: "{{ default_container_healthcheck_interval }}"
nova_novncproxy_tls_proxy_healthcheck_retries: "{{ default_container_healthcheck_retries }}"
nova_novncproxy_tls_proxy_healthcheck_start_period: "{{ default_container_healthcheck_start_period }}"
nova_novncproxy_tls_proxy_healthcheck_test: ["CMD-SHELL", "healthcheck_curl -u {{ haproxy_user }}:{{ haproxy_password }} {{ api_interface_address | put_address_in_context('url') }}:{{ haproxy_stats_port }}"]
nova_novncproxy_tls_proxy_healthcheck_timeout: "{{ default_container_healthcheck_timeout }}"
nova_novncproxy_tls_proxy_healthcheck:
interval: "{{ nova_novncproxy_tls_proxy_healthcheck_interval }}"
retries: "{{ nova_novncproxy_tls_proxy_healthcheck_retries }}"
start_period: "{{ nova_novncproxy_tls_proxy_healthcheck_start_period }}"
test: "{% if nova_novncproxy_tls_proxy_enable_healthchecks | bool %}{{ nova_novncproxy_tls_proxy_healthcheck_test }}{% else %}NONE{% endif %}"
timeout: "{{ nova_novncproxy_tls_proxy_healthcheck_timeout }}"
nova_spicehtml5proxy_enable_healthchecks: "{{ enable_container_healthchecks }}"
nova_spicehtml5proxy_healthcheck_interval: "{{ default_container_healthcheck_interval }}"
nova_spicehtml5proxy_healthcheck_retries: "{{ default_container_healthcheck_retries }}"
@ -396,6 +444,11 @@ nova_novncproxy_default_volumes:
- "{{ '/etc/timezone:/etc/timezone:ro' if ansible_facts.os_family == 'Debian' else '' }}"
- "kolla_logs:/var/log/kolla/"
- "{{ kolla_dev_repos_directory ~ '/nova/nova:/var/lib/kolla/venv/lib/python' ~ distro_python_version ~ '/site-packages/nova' if nova_dev_mode | bool else '' }}"
nova_novncproxy_tls_proxy_default_volumes:
- "{{ node_config_directory }}/nova-novncproxy-tls-proxy/:{{ container_config_directory }}/:ro"
- "/etc/localtime:/etc/localtime:ro"
- "{{ '/etc/timezone:/etc/timezone:ro' if ansible_facts.os_family == 'Debian' else '' }}"
- "kolla_logs:/var/log/kolla/"
nova_spicehtml5proxy_default_volumes:
- "{{ node_config_directory }}/nova-spicehtml5proxy/:{{ container_config_directory }}/:ro"
- "/etc/localtime:/etc/localtime:ro"
@ -446,6 +499,7 @@ nova_extra_volumes: "{{ default_extra_volumes }}"
nova_libvirt_extra_volumes: "{{ nova_extra_volumes }}"
nova_ssh_extra_volumes: "{{ nova_extra_volumes }}"
nova_novncproxy_extra_volumes: "{{ nova_extra_volumes }}"
nova_novncproxy_tls_proxy_extra_volumes: "{{ nova_extra_volumes }}"
nova_spicehtml5proxy_extra_volumes: "{{ nova_extra_volumes }}"
nova_serialproxy_extra_volumes: "{{ nova_extra_volumes }}"
nova_conductor_extra_volumes: "{{ nova_extra_volumes }}"
@ -483,6 +537,7 @@ nova_cell_services_require_nova_conf:
- nova-compute
- nova-compute-ironic
- nova-novncproxy
- nova-novncproxy-tls-proxy
- nova-serialproxy
- nova-spicehtml5proxy
@ -589,6 +644,28 @@ enable_shared_var_lib_nova_mnt: "{{ enable_cinder_backend_nfs | bool or enable_c
nova_pci_passthrough_whitelist: "{{ enable_neutron_sriov | bool | ternary(neutron_sriov_physnet_mappings | dict2items(key_name='physical_network', value_name='devname'), []) }}"
####################
# NoVNCProxy Backend TLS proxy
####################
syslog_server: "{{ api_interface_address }}"
syslog_nova_novncproxy_tls_proxy_facility: "local3"
nova_novncproxy_tls_proxy_max_connections: 40000
nova_novncproxy_tls_proxy_processes: 1
nova_novncproxy_tls_proxy_process_cpu_map: "no"
nova_novncproxy_tls_proxy_defaults_max_connections: 10000
nova_novncproxy_tls_proxy_http_request_timeout: "10s"
nova_novncproxy_tls_proxy_http_keep_alive_timeout: "10s"
nova_novncproxy_tls_proxy_queue_timeout: "1m"
nova_novncproxy_tls_proxy_connect_timeout: "10s"
nova_novncproxy_tls_proxy_client_timeout: "1m"
nova_novncproxy_tls_proxy_server_timeout: "1m"
nova_novncproxy_tls_proxy_check_timeout: "10s"
# Check http://www.haproxy.org/download/1.5/doc/configuration.txt for available options
nova_novncproxy_tls_proxy_defaults_balance: "roundrobin"
##################
# Libvirt cleanup
##################

View File

@ -228,3 +228,19 @@
- Restart nova-compute container
- Restart nova-compute-ironic container
- Restart nova-compute-fake containers
- name: Restart nova-novncproxy-tls-proxy container
vars:
service_name: "nova-novncproxy-tls-proxy"
service: "{{ nova_cell_services[service_name] }}"
become: true
kolla_container:
action: "recreate_or_restart_container"
common_options: "{{ docker_common_options }}"
name: "{{ service.container_name }}"
image: "{{ service.image }}"
volumes: "{{ service.volumes|reject('equalto', '')|list }}"
dimensions: "{{ service.dimensions }}"
healthcheck: "{{ service.healthcheck | default(omit) }}"
when:
- kolla_action != "config"

View File

@ -14,7 +14,7 @@
- include_tasks: copy-certs.yml
when:
- kolla_copy_ca_into_containers | bool
- kolla_copy_ca_into_containers | bool or nova_novncproxy_enable_tls_backend | bool
- include_tasks: external_ceph.yml
when:
@ -53,6 +53,20 @@
when:
- vendordata_file.stat.exists
- name: Creating TLS backend PEM File
vars:
nova_novncproxy_tls_proxy: "{{ nova_cell_services['nova-novncproxy-tls-proxy'] }}"
assemble:
src: "{{ node_config_directory }}/nova-novncproxy-tls-proxy/"
dest: "{{ node_config_directory }}/nova-novncproxy-tls-proxy/nova-cert-and-key.pem"
mode: "0660"
regexp: "^nova-(cert|key)\\.pem$"
remote_src: true
become: true
when:
- nova_novncproxy_tls_proxy.enabled | bool
- nova_novncproxy_tls_proxy.host_in_groups | bool
- name: Copying over config.json files for services
become: true
template:
@ -247,3 +261,21 @@
with_items:
- nova-compute
- nova-compute-ironic
- name: Copying over nova-novncproxy-haproxy-tls.cfg
vars:
nova_novncproxy_tls_proxy: "{{ nova_cell_services['nova-novncproxy-tls-proxy'] }}"
template:
src: "{{ item }}"
dest: "{{ node_config_directory }}/nova-novncproxy-tls-proxy/nova-novncproxy-tls-proxy.cfg"
mode: "0660"
become: true
with_first_found:
- "{{ node_custom_config }}/nova/{{ inventory_hostname }}/nova-novncproxy-tls-proxy.cfg"
- "{{ node_custom_config }}/nova/nova-novncproxy-tls-proxy.cfg"
- "nova-novncproxy-tls-proxy.cfg.j2"
when:
- nova_novncproxy_tls_proxy.enabled | bool
- nova_novncproxy_tls_proxy.host_in_groups | bool
notify:
- Restart nova-novncproxy-tls-proxy container

View File

@ -0,0 +1,47 @@
#jinja2: lstrip_blocks: True
global
chroot /var/lib/haproxy
user nova
group nova
daemon
log {{ syslog_server }}:{{ syslog_udp_port }} {{ syslog_nova_novncproxy_tls_proxy_facility }}
maxconn {{ nova_novncproxy_tls_proxy_max_connections }}
nbproc {{ nova_novncproxy_tls_proxy_processes }}
{% if (nova_novncproxy_tls_proxy_processes | int > 1) and (nova_novncproxy_tls_proxy_process_cpu_map | bool) %}
{% for cpu_idx in range(0, nova_novncproxy_tls_proxy_processes) %}
cpu-map {{ cpu_idx + 1 }} {{ cpu_idx }}
{% endfor %}
{% endif %}
ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
tune.ssl.default-dh-param 4096
defaults
log global
option redispatch
retries 3
timeout http-request {{ nova_novncproxy_tls_proxy_http_request_timeout }}
timeout http-keep-alive {{ nova_novncproxy_tls_proxy_http_keep_alive_timeout }}
timeout queue {{ nova_novncproxy_tls_proxy_queue_timeout }}
timeout connect {{ nova_novncproxy_tls_proxy_connect_timeout }}
timeout client {{ nova_novncproxy_tls_proxy_client_timeout }}
timeout server {{ nova_novncproxy_tls_proxy_server_timeout }}
timeout check {{ nova_novncproxy_tls_proxy_check_timeout }}
balance {{ nova_novncproxy_tls_proxy_defaults_balance }}
maxconn {{ nova_novncproxy_tls_proxy_defaults_max_connections }}
listen stats
bind {{ api_interface_address }}:{{ haproxy_stats_port }}
mode http
stats enable
stats uri /
stats refresh 15s
stats realm Haproxy\ Stats
stats auth {{ haproxy_user }}:{{ haproxy_password }}
frontend nova_novncproxy_backend_tls
bind {{ api_interface_address }}:{{ nova_novncproxy_listen_port }} ssl crt /etc/nova/certs/nova-cert-and-key.pem
default_backend nova_novncproxy_api
backend nova_novncproxy_api
server nova_novncproxy-api 127.0.0.1:{{ nova_novncproxy_listen_port }} check

View File

@ -0,0 +1,17 @@
{
"command": "/usr/sbin/haproxy -W -db -p /run/haproxy.pid -f /etc/nova/nova-novncproxy-tls-proxy.cfg",
"config_files": [
{
"source": "{{ container_config_directory }}/nova-novncproxy-tls-proxy.cfg",
"dest": "/etc/nova/nova-novncproxy-tls-proxy.cfg",
"owner": "nova",
"perm": "0600"
},
{
"source": "{{ container_config_directory }}/nova-cert-and-key.pem",
"dest": "/etc/nova/certs/nova-cert-and-key.pem",
"owner": "nova",
"perm": "0600"
}
]
}

View File

@ -49,9 +49,13 @@ workers = {{ nova_cell_conductor_workers }}
{% if service_name == "nova-compute-ironic" %}
enabled = false
{% else %}
{% if neutron_enable_tls_backend | bool %}
novncproxy_host = 127.0.0.1
{% else %}
novncproxy_host = {{ api_interface_address }}
novncproxy_port = {{ nova_novncproxy_listen_port }}
{% endif %}
server_listen = {{ api_interface_address }}
novncproxy_port = {{ nova_novncproxy_listen_port }}
server_proxyclient_address = {{ api_interface_address }}
{% if inventory_hostname in groups[nova_cell_compute_group] %}
novncproxy_base_url = {{ nova_novncproxy_fqdn | kolla_url(public_protocol, nova_novncproxy_public_port, '/vnc_lite.html') }}