From 2b8d5a0b88b8109ddb2b047bb88ee75c37698645 Mon Sep 17 00:00:00 2001 From: James Gibson Date: Mon, 8 Nov 2021 15:40:27 +0000 Subject: [PATCH] Enable TLS for VNC from novncproxy to compute hosts To secure communications from the proxy server to the compute nodes using VeNCrypt authentication scheme. In a previous patch a TLS server certificate was deployed to compute nodes, this patch makes use of this same server cert for securing VNC sessions on compute nodes. It is recommended that this certificate be issued by a dedicated certificate authority solely for the VNC service, as libvirt does not currently have a mechanism to restrict what certificates can be presented by the proxy server. This has not been implemented to reduce complexity. In addition the noVNC proxy needs to present a client certificate so only approved VNC proxy servers can connect to the Compute nodes. The PKI role has been used to create a client certificate for the nova console nodes. Related Nova docs: https://docs.openstack.org/nova/latest/admin/remote-console-access.html To help with the transition from from unencrypted VNC to VeNCrypt, initially compute nodes auth scheme allows for both encrypted and unencrypted sessions using the variable `nova_vencrypt_auth_scheme`, this will be removed in future releases. Change-Id: Iafb788f80fd401c6ce6e4576bafd06c92431bd65 --- defaults/main.yml | 54 +++++++++++++++++-- handlers/main.yml | 9 +++- .../VNC-proxy-security-520e6dac6bad0018.yaml | 16 ++++++ tasks/main.yml | 23 ++++++-- templates/nova.conf.j2 | 7 +++ templates/qemu.conf.j2 | 10 ++++ 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 releasenotes/notes/VNC-proxy-security-520e6dac6bad0018.yaml diff --git a/defaults/main.yml b/defaults/main.yml index 4ed393fe..4b0ba19c 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -276,6 +276,18 @@ nova_console_ssl_dir: "/etc/nova/ssl" nova_console_ssl_cert: "{{ nova_console_ssl_dir }}/nova-console.pem" nova_console_ssl_key: "{{ nova_console_ssl_dir }}/nova-console.key" +# Enable TLS on VNC connection from novnc to compute hosts +nova_qemu_vnc_tls: 1 +nova_vencrypt_client_key: "/etc/pki/nova-novncproxy/client-key.pem" +nova_vencrypt_client_cert: "/etc/pki/nova-novncproxy/client-cert.pem" +nova_vencrypt_ca_certs: "/etc/pki/nova-novncproxy/ca-cert.pem" +# The auth_schemes values should be listed in order of preference. +# If enabling VeNCrypt on an existing deployment which already has instances running, +# the noVNC proxy server must initially be allowed to use vencrypt and none. +# Once it is confirmed that all Compute nodes have VeNCrypt enabled for VNC, +# it is possible to remove the none option from the list +nova_vencrypt_auth_scheme: "vencrypt,none" + # Set to true when terminating SSL/TLS at a load balancer nova_external_ssl: "{{ openstack_external_ssl | default(False) }}" @@ -548,8 +560,8 @@ nova_pki_intermediate_cert_name: "{{ openstack_pki_service_intermediate_cert_nam nova_pki_intermediate_chain_path: "{{ nova_pki_dir ~ '/roots/' ~ nova_pki_intermediate_cert_name ~ '/certs/' ~ nova_pki_intermediate_cert_name ~ '-chain.crt' }}" nova_pki_regen_cert: '' # Create client and server cert for compute hosts -# This certiticate is used during TLS live migrations -nova_pki_certificates: +# This certiticate is used to secure TLS live migrations and VNC sessions +nova_pki_compute_certificates: - name: "nova_{{ ansible_facts['hostname'] }}" provider: ownca cn: "{{ ansible_facts['nodename'] }}" @@ -568,9 +580,8 @@ nova_libvirt_ssl_dir: /etc/pki/libvirt # QEMU default destination files for SSL certificates nova_qemu_ssl_dir: /etc/pki/qemu -# Installation details for SSL certificates for TLS live migration -nova_pki_install_certificates: - # Server certificate used by libvirt for live migrations +# Installation details for SSL certificates for compute hosts TLS live migration +nova_pki_compute_install_certificates: - src: "{{ nova_user_ssl_cert | default(nova_pki_certs_path ~ 'nova_' ~ ansible_facts['hostname'] ~ '-chain.crt') }}" dest: "{{ nova_libvirt_ssl_dir }}/servercert.pem" owner: "root" @@ -640,3 +651,36 @@ nova_pki_install_certificates: #nova_user_ssl_cert: #nova_user_ssl_key: #nova_user_ssl_ca_cert: + +# TLS certficates for console hosts +nova_pki_console_certificates: + # Client certificate used by novnv proxy to authenticate with compute hosts using vencrypt + - name: "nova_{{ ansible_facts['hostname'] }}-client" + provider: ownca + cn: "{{ ansible_facts['nodename'] }}" + san: "{{ 'DNS:' ~ ansible_facts['hostname'] ~ ',DNS:' ~ ansible_facts['nodename'] ~ ',IP:' ~ (nova_management_address == 'localhost') | ternary('127.0.0.1', nova_management_address) }}" + signed_by: "{{ nova_pki_intermediate_cert_name }}" + key_usage: + - digitalSignature + - keyAgreement + - keyEncipherment + extended_key_usage: + - clientAuth + +# Installation details for SSL certificates for console hosts +nova_pki_console_install_certificates: + - src: "{{ nova_user_ssl_cert | default(nova_pki_certs_path ~ 'nova_' ~ ansible_facts['hostname'] ~ '-client-chain.crt') }}" + dest: "{{ nova_vencrypt_client_cert }}" + owner: "root" + group: "{{ nova_system_group_name }}" + mode: "0640" + - src: "{{ nova_user_ssl_key | default(nova_pki_keys_path ~ 'nova_' ~ ansible_facts['hostname'] ~ '-client.key.pem') }}" + dest: "{{ nova_vencrypt_client_key }}" + owner: "root" + group: "{{ nova_system_group_name }}" + mode: "0640" + - src: "{{ nova_user_ssl_ca_cert | default(nova_pki_intermediate_chain_path) }}" + dest: "{{ nova_vencrypt_ca_certs }}" + owner: "root" + group: "{{ nova_system_group_name }}" + mode: "0640" diff --git a/handlers/main.yml b/handlers/main.yml index 01b9aa61..f4106935 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -21,6 +21,9 @@ listen: - Restart libvirt-bin - "cert installed" + when: + - "'nova_compute' in group_names" + - nova_virt_type != 'ironic' - name: Enable sockets when needed service: @@ -37,7 +40,6 @@ condition: "{{ nova_libvirtd_listen_tcp | bool }}" listen: - Restart libvirt-bin - - "cert installed" - name: Start libvirt-bin service: @@ -47,6 +49,9 @@ listen: - Restart libvirt-bin - "cert installed" + when: + - "'nova_compute' in group_names" + - nova_virt_type != 'ironic' - name: Stop services service: @@ -62,6 +67,7 @@ listen: - "Restart nova services" - "venv changed" + - "cert installed" # NOTE (noonedeadpunk): Remove this task after Xena release - name: Remove obsoleted policy.json @@ -86,6 +92,7 @@ listen: - "Restart nova services" - "venv changed" + - "cert installed" - meta: noop listen: Manage LB diff --git a/releasenotes/notes/VNC-proxy-security-520e6dac6bad0018.yaml b/releasenotes/notes/VNC-proxy-security-520e6dac6bad0018.yaml new file mode 100644 index 00000000..1f842545 --- /dev/null +++ b/releasenotes/notes/VNC-proxy-security-520e6dac6bad0018.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + Enable VeNCrypt authentication scheme from noVNC proxy to compute nodes. + When using HTTPS, the TLS encryption only applies to data between the + tenant user and proxy server. To provide protection from the noVNC proxy + to the Compute Nodes, it is necessary to enable the VeNCrypt + authentication scheme for VNC. + + A pre-existing PKI (Public Key Infrastructure) setup is + required. + + Initially to help with the transition from unencrypted VNC to + VeNCrypt, compute nodes auth scheme allows for both encrypted and + unencrypted sessions using the variable `nova_vencrypt_auth_scheme`, + this will be removed in future releases. diff --git a/tasks/main.yml b/tasks/main.yml index 49840d0a..8c538f32 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -120,7 +120,7 @@ - nova-config # Create certs after libvirt groups have been created but before handlers -- name: Create and install SSL certificates +- name: Create and install SSL certificates for compute hosts include_role: name: pki tasks_from: main_certs.yml @@ -129,12 +129,29 @@ pki_dir: "{{ nova_pki_dir }}" pki_create_certificates: "{{ nova_user_ssl_cert is not defined and nova_user_ssl_key is not defined }}" pki_regen_certificates: "{{ nova_pki_regen_cert }}" - pki_certificates: "{{ nova_pki_certificates }}" - pki_install_certificates: "{{ nova_pki_install_certificates }}" + pki_certificates: "{{ nova_pki_compute_certificates }}" + pki_install_certificates: "{{ nova_pki_compute_install_certificates }}" when: - nova_libvirtd_listen_tls == 1 - "'nova_compute' in group_names" +# Create certs after nova groups have been created but before handlers +- name: Create and install SSL certificates for console hosts + include_role: + name: pki + tasks_from: main_certs.yml + vars: + pki_setup_host: "{{ nova_pki_setup_host }}" + pki_dir: "{{ nova_pki_dir }}" + pki_create_certificates: "{{ nova_user_ssl_cert is not defined and nova_user_ssl_key is not defined }}" + pki_regen_certificates: "{{ nova_pki_regen_cert }}" + pki_certificates: "{{ nova_pki_console_certificates }}" + pki_install_certificates: "{{ nova_pki_console_install_certificates }}" + when: + - nova_qemu_vnc_tls == 1 + - nova_console_type == 'novnc' + - "'nova_console' in group_names" + - import_tasks: nova_post_install.yml tags: - nova-config diff --git a/templates/nova.conf.j2 b/templates/nova.conf.j2 index bc6fd83b..fa645801 100644 --- a/templates/nova.conf.j2 +++ b/templates/nova.conf.j2 @@ -98,6 +98,13 @@ novncproxy_port = {{ nova_novncproxy_port }} server_listen = {{ nova_novncproxy_vncserver_listen }} server_proxyclient_address = {{ nova_novncproxy_vncserver_proxyclient_address }} +{% if nova_qemu_vnc_tls == 1 %} +auth_schemes={{ nova_vencrypt_auth_scheme }} +vencrypt_client_key={{ nova_vencrypt_client_key }} +vencrypt_client_cert={{ nova_vencrypt_client_cert }} +vencrypt_ca_certs={{ nova_vencrypt_ca_certs }} +{% endif %} + {% elif nova_console_type == 'serialconsole' %} [serial_console] enabled = True diff --git a/templates/qemu.conf.j2 b/templates/qemu.conf.j2 index e5092a7b..ba79ec8d 100644 --- a/templates/qemu.conf.j2 +++ b/templates/qemu.conf.j2 @@ -62,6 +62,16 @@ default_tls_x509_cert_dir = "{{ nova_qemu_ssl_dir }}" default_tls_x509_verify = 1 {% endif %} +# Enable use of TLS encryption on the VNC server. This requires +# a VNC client which supports the VeNCrypt protocol extension. +# Examples include vinagre, virt-viewer, virt-manager and vencrypt +# itself. UltraVNC, RealVNC, TightVNC do not support this +# +# It is necessary to setup CA and issue a server certificate +# before enabling this. +# +vnc_tls = {{ nova_qemu_vnc_tls }} + {% for key, value in _nova_qemu_conf.items() %} {{ key }} = {{ value }} {% endfor %}