From ddafc94e30cea8895fbc399739467650fae94e7d Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Tue, 25 Aug 2020 14:51:44 +0200 Subject: [PATCH] TLS support for API services Change-Id: I084da313eda17435c095ade7cb1b92981f5341dc --- bifrost/cli.py | 4 + doc/source/install/index.rst | 29 +++++ doc/source/install/keystone.rst | 19 +++ playbooks/ci/run.yaml | 1 + .../roles/bifrost-cloud-config/README.md | 4 + .../bifrost-cloud-config/defaults/main.yml | 3 +- .../roles/bifrost-cloud-config/tasks/main.yml | 8 ++ .../tasks/update_facts_from_ironic.yaml | 1 + .../tasks/main.yml | 2 + .../bifrost-ironic-install/defaults/main.yml | 9 +- .../tasks/bootstrap.yml | 9 ++ .../tasks/inspector_bootstrap.yml | 9 ++ .../tasks/keystone_setup.yml | 12 ++ .../tasks/keystone_setup_inspector.yml | 8 ++ .../bifrost-ironic-install/tasks/validate.yml | 10 ++ .../templates/inspector-default-boot-ipxe.j2 | 2 +- .../templates/ironic-inspector.conf.j2 | 15 ++- .../templates/ironic.conf.j2 | 35 ++++- .../defaults/main.yml | 4 +- .../tasks/main.yml | 5 + .../templates/clouds.yaml.j2 | 15 +++ .../templates/openrc.j2 | 4 + .../defaults/main.yml | 8 +- .../tasks/bootstrap.yml | 9 ++ .../bifrost-keystone-install/tasks/main.yml | 33 +---- .../tasks/upgrade.yml | 121 ++++++++++++++++++ .../nginx_conf.d_bifrost-keystone.conf.j2 | 12 ++ playbooks/roles/bifrost-tls/README.md | 86 +++++++++++++ playbooks/roles/bifrost-tls/defaults/main.yml | 34 +++++ playbooks/roles/bifrost-tls/tasks/main.yml | 54 ++++++++ .../tasks/main.yml | 1 + .../ironic-delete-dynamic/tasks/main.yml | 1 + .../ironic-enroll-dynamic/tasks/main.yml | 1 + .../roles/ironic-inspect-node/tasks/main.yml | 1 + releasenotes/notes/tls-988e725820bb8aca.yaml | 14 ++ scripts/test-bifrost.sh | 3 + zuul.d/bifrost-jobs.yaml | 2 + 37 files changed, 549 insertions(+), 39 deletions(-) create mode 100644 playbooks/roles/bifrost-keystone-install/tasks/upgrade.yml create mode 100644 playbooks/roles/bifrost-tls/README.md create mode 100644 playbooks/roles/bifrost-tls/defaults/main.yml create mode 100644 playbooks/roles/bifrost-tls/tasks/main.yml create mode 100644 releasenotes/notes/tls-988e725820bb8aca.yaml diff --git a/bifrost/cli.py b/bifrost/cli.py index 8ba685285..d7b2a7c09 100644 --- a/bifrost/cli.py +++ b/bifrost/cli.py @@ -154,6 +154,8 @@ def cmd_install(args): install_dib='true', network_interface=args.network_interface, enable_keystone=args.enable_keystone, + enable_tls=args.enable_tls, + generate_tls=args.enable_tls, noauth_mode='false', enabled_hardware_types=args.hardware_types, cleaning_disk_erase=args.cleaning_disk_erase, @@ -220,6 +222,8 @@ def parse_args(): help='the network interface to use') install.add_argument('--enable-keystone', action='store_true', help='enable keystone and use authentication') + install.add_argument('--enable-tls', action='store_true', + help='enable self-signed TLS on API endpoints') install.add_argument('--hardware-types', # only generic types are enabled in the simple CI default='ipmi,redfish,manual-management', diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst index 8a6c57474..479499f0d 100644 --- a/doc/source/install/index.rst +++ b/doc/source/install/index.rst @@ -215,6 +215,11 @@ Additionally, the following parameters can be useful: A comma separated list of hardware types to enable. ``--enable-keystone`` Whether to enable authentication with Keystone_. +``--enable-tls`` + Enable self-signed TLS on API endpoints. + + .. warning:: + If using Keystone_, see :ref:`keystone-tls` for important notes. See the built-in documentation for more details: @@ -307,6 +312,30 @@ If you are running the installation behind a proxy, export the environment variables ``http_proxy``, ``https_proxy`` and ``no_proxy`` so that ansible will use these proxy settings. +TLS support +----------- + +Bifrost supports TLS for API services with two options: + +* A self-signed certificate can be generated automatically. Set + ``enable_tls=true`` and ``generate_tls=true``. + + .. note:: This is equivalent to the ``--enable-tls`` flag of ``bifrost-cli``. + +* Certificate paths can be provided via: + + ``tls_certificate_path`` + Path to the TLS certificate (must be world-readable). + ``tls_private_key_path`` + Path to the private key (must not be password protected). + ``tls_csr_path`` + Path to the certificate signing request file. + + Set ``enable_tls=true`` and do not set ``generate_tls`` to use this option. + +.. warning:: + If using Keystone, see :ref:`keystone-tls` for important notes. + Dependencies ============ diff --git a/doc/source/install/keystone.rst b/doc/source/install/keystone.rst index 1f9981bbe..dddffd12f 100644 --- a/doc/source/install/keystone.rst +++ b/doc/source/install/keystone.rst @@ -30,6 +30,25 @@ See the following files for more settings that can be overridden: * ``playbooks/roles/bifrost-ironic-install/defaults/main.yml`` * ``playbooks/roles/bifrost-keystone-install/defaults/main.yml`` +.. _keystone-tls: + +TLS notes +--------- + +There are two important limitations to keep in mind when using Keystone with +TLS: + +* It's not possible to enable TLS on upgrade from Bifrost < 9.0 (Ussuri + and early Victoria). First do an upgrade to Bifrost >= 9.0, then enable TLS + in a separate step. + +* Automatically updating from a TLS environment to a non-TLS one may not be + possible if using custom TLS certificates in a non-standard location + (``/etc/bifrost/bifrost.crt``). You need to manually change identity + endpoints in the catalog from ``https`` to ``http`` directly before + an update. The ``public`` endpoint **must** be updated **last** or you may + lock yourself out of keystone. + Using an existing Keystone -------------------------- diff --git a/playbooks/ci/run.yaml b/playbooks/ci/run.yaml index 565603093..6daa32d47 100644 --- a/playbooks/ci/run.yaml +++ b/playbooks/ci/run.yaml @@ -15,3 +15,4 @@ BOOT_MODE: "{{ boot_mode | default('') }}" TEST_VM_NODE_DRIVER: "{{ test_driver | default('ipmi') }}" NOAUTH_MODE: "{{ noauth_mode | default(false) | bool | lower }}" + ENABLE_TLS: "{{ enable_tls | default(false) | bool | lower }}" diff --git a/playbooks/roles/bifrost-cloud-config/README.md b/playbooks/roles/bifrost-cloud-config/README.md index 500cb907a..aec37e6f5 100644 --- a/playbooks/roles/bifrost-cloud-config/README.md +++ b/playbooks/roles/bifrost-cloud-config/README.md @@ -45,6 +45,10 @@ overridden in no-auth mode. Ironic endpoint to use. If the fact is already defined, it is not overridden. +`tls_certificate_path` + +Path to the TLS certificate. Only set if TLS is used. + Notes ----- diff --git a/playbooks/roles/bifrost-cloud-config/defaults/main.yml b/playbooks/roles/bifrost-cloud-config/defaults/main.yml index 499968d50..35a0ede0a 100644 --- a/playbooks/roles/bifrost-cloud-config/defaults/main.yml +++ b/playbooks/roles/bifrost-cloud-config/defaults/main.yml @@ -5,5 +5,6 @@ network_interface: "virbr0" ans_network_interface: "{{ network_interface | replace('-', '_') }}" internal_ip: "{{ hostvars[inventory_hostname]['ansible_' + ans_network_interface]['ipv4']['address'] }}" -api_protocol: http +enable_tls: false +api_protocol: "{{ 'https' if enable_tls | bool else 'http' }}" ironic_api_url: "{{ api_protocol }}://{{ internal_ip }}:6385" diff --git a/playbooks/roles/bifrost-cloud-config/tasks/main.yml b/playbooks/roles/bifrost-cloud-config/tasks/main.yml index d3ccc4e95..302a1dc5f 100644 --- a/playbooks/roles/bifrost-cloud-config/tasks/main.yml +++ b/playbooks/roles/bifrost-cloud-config/tasks/main.yml @@ -38,6 +38,14 @@ - openstack_cloud is defined no_log: yes +- name: "Set the TLS certificate if present" + set_fact: + tls_certificate_path: "{{ openstack_cloud.cacert }}" + when: + - tls_certificate_path is undefined + - openstack_cloud is defined + - openstack_cloud.cacert is defined + - name: "If in noauth mode and no clouds.yaml, unset authentication parameters." set_fact: auth_type: None diff --git a/playbooks/roles/bifrost-configdrives-dynamic/tasks/update_facts_from_ironic.yaml b/playbooks/roles/bifrost-configdrives-dynamic/tasks/update_facts_from_ironic.yaml index 652eb0ba4..59f93e487 100644 --- a/playbooks/roles/bifrost-configdrives-dynamic/tasks/update_facts_from_ironic.yaml +++ b/playbooks/roles/bifrost-configdrives-dynamic/tasks/update_facts_from_ironic.yaml @@ -23,6 +23,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" uuid: "{{ uuid | default() }}" name: "{{ name | default() }}" diff --git a/playbooks/roles/bifrost-deploy-nodes-dynamic/tasks/main.yml b/playbooks/roles/bifrost-deploy-nodes-dynamic/tasks/main.yml index fc3fcaf66..c0a09da4a 100644 --- a/playbooks/roles/bifrost-deploy-nodes-dynamic/tasks/main.yml +++ b/playbooks/roles/bifrost-deploy-nodes-dynamic/tasks/main.yml @@ -56,6 +56,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" uuid: "{{ uuid }}" state: present @@ -85,6 +86,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" uuid: "{{ uuid }}" state: present diff --git a/playbooks/roles/bifrost-ironic-install/defaults/main.yml b/playbooks/roles/bifrost-ironic-install/defaults/main.yml index 82c9422df..035c4173c 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/main.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/main.yml @@ -260,7 +260,7 @@ noauth_mode: true enable_keystone: false # Service URLs used for communication with them. -api_protocol: http +api_protocol: "{{ 'https' if enable_tls | bool else 'http' }}" ironic_api_url: "{{ api_protocol }}://{{ internal_ip }}:6385" ironic_inspector_api_url: "{{ api_protocol }}://{{ internal_ip }}:5050" keystone_api_url: "{{ api_protocol }}://{{ internal_ip }}:5000/v3" @@ -339,3 +339,10 @@ pip_opts: "{{ lookup('env', 'PIP_OPTS') | default('') }}" # Timeout for gathering facts. fact_gather_timeout: "{{ lookup('config', 'DEFAULT_GATHER_TIMEOUT', on_missing='skip') | default(omit, true) }}" + +# Enable TLS support. +enable_tls: false +tls_root: /etc/bifrost +tls_certificate_path: "{{ tls_root }}/bifrost.crt" +ironic_private_key_path: /etc/ironic/ironic.pem +ironic_inspector_private_key_path: /etc/ironic-inspector/inspector.pem diff --git a/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml b/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml index 217f562a1..96724a584 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml @@ -159,6 +159,15 @@ - not noauth_mode | bool - not enable_keystone | bool +- name: "Generate TLS parameters" + include_role: + name: bifrost-tls + vars: + dest_private_key_path: "{{ ironic_private_key_path }}" + dest_private_key_owner: ironic + dest_private_key_group: ironic + when: enable_tls | bool + - name: "Populate keystone for Bifrost" include: keystone_setup.yml when: diff --git a/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml b/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml index 509504818..d35c36a7f 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml @@ -92,6 +92,15 @@ - not noauth_mode | bool - not enable_keystone | bool +- name: "Generate TLS parameters" + include_role: + name: bifrost-tls + vars: + dest_private_key_path: "{{ ironic_inspector_private_key_path }}" + dest_private_key_owner: ironic + dest_private_key_group: ironic + when: enable_tls | bool + - name: "Populate keystone for ironic-inspector " include: keystone_setup_inspector.yml when: enable_keystone | bool diff --git a/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup.yml b/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup.yml index aad086c22..b1a2918dc 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup.yml @@ -53,6 +53,7 @@ domain_id: "default" enabled: yes auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -66,6 +67,7 @@ auth: "{{ keystone_auth }}" update_password: always wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -76,6 +78,7 @@ project: "{{ ironic.service_catalog.project_name }}" auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -87,6 +90,7 @@ description: OpenStack Baremetal Service auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" register: baremetal_catalog_service no_log: true @@ -99,6 +103,7 @@ url: "{{ ironic.keystone.admin_url | default(ironic_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -115,6 +120,7 @@ url: "{{ ironic.keystone.public_url | default(ironic_public_url) | default(ironic_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -131,6 +137,7 @@ url: "{{ ironic.keystone.internal_url | default(ironic_private_url) | default(ironic_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -139,6 +146,7 @@ name: "baremetal_admin" state: present auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -147,6 +155,7 @@ name: "baremetal_observer" state: present auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -158,6 +167,7 @@ domain_id: "default" enabled: yes auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -169,6 +179,7 @@ domain: "default" auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -179,5 +190,6 @@ project: "baremetal" auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true diff --git a/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup_inspector.yml b/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup_inspector.yml index 9efdff4e2..acd635ca3 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup_inspector.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/keystone_setup_inspector.yml @@ -53,6 +53,7 @@ default_project: "{{ ironic_inspector.service_catalog.project_name | default('service') }}" auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -63,6 +64,7 @@ project: "{{ ironic_inspector.service_catalog.project_name | default('service') }}" auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -74,6 +76,7 @@ description: OpenStack Baremetal Introspection Service auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" register: introspection_catalog_service no_log: true @@ -86,6 +89,7 @@ url: "{{ ironic_inspector.keystone.admin_url | default(ironic_inspector_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -102,6 +106,7 @@ url: "{{ ironic_inspector.keystone.public_url | default(ironic_inspector_public_url) | default(ironic_inspector_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -118,6 +123,7 @@ url: "{{ ironic_inspector.keystone.internal_url | default(ironic_inspector_private_url) | default(ironic_inspector_api_url) }}" region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" auth: "{{ keystone_auth }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" no_log: true environment: "{{ bifrost_venv_env }}" @@ -130,6 +136,7 @@ auth: "{{ keystone_auth }}" update_password: always wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true @@ -140,5 +147,6 @@ project: baremetal auth: "{{ keystone_auth }}" wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" environment: "{{ bifrost_venv_env }}" no_log: true diff --git a/playbooks/roles/bifrost-ironic-install/tasks/validate.yml b/playbooks/roles/bifrost-ironic-install/tasks/validate.yml index 341a8a9ad..27b0c9fc6 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/validate.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/validate.yml @@ -44,6 +44,11 @@ - not enable_keystone | bool no_log: yes +- name: "Set OS_CACERT if required" + set_fact: + testing_env: "{{ testing_env | combine({'OS_CACERT': tls_certificate_path}) }}" + when: enable_tls | bool + - name: "Validate API access and at least one conductor" command: baremetal conductor list -f value -c Hostname environment: "{{ testing_env | combine(bifrost_venv_env) }}" @@ -77,6 +82,11 @@ - enable_inspector | bool no_log: yes +- name: "Set OS_CACERT if required" + set_fact: + testing_env: "{{ testing_env | combine({'OS_CACERT': tls_certificate_path}) }}" + when: enable_tls | bool + - name: "Validate introspection API access" command: baremetal introspection list environment: "{{ testing_env | combine(bifrost_venv_env) }}" diff --git a/playbooks/roles/bifrost-ironic-install/templates/inspector-default-boot-ipxe.j2 b/playbooks/roles/bifrost-ironic-install/templates/inspector-default-boot-ipxe.j2 index c7c548332..ef02a082c 100644 --- a/playbooks/roles/bifrost-ironic-install/templates/inspector-default-boot-ipxe.j2 +++ b/playbooks/roles/bifrost-ironic-install/templates/inspector-default-boot-ipxe.j2 @@ -5,6 +5,6 @@ dhcp || reboot goto introspect :introspect -kernel {{ ipa_kernel_url }} ipa-inspection-callback-url=http://{{ internal_ip }}:5050/v1/continue {% if fast_track | bool %}ipa-api-url=http://{{ internal_ip }}:6385{% endif %} systemd.journald.forward_to_console=yes BOOTIF=${mac} nofb nomodeset vga=normal console=ttyS0 {{ inspector_extra_kernel_options | default('') }} initrd={{ ipa_ramdisk_url | basename }} +kernel {{ ipa_kernel_url }} ipa-inspection-callback-url={{ api_protocol }}://{{ internal_ip }}:5050/v1/continue {% if fast_track | bool %}ipa-api-url={{ api_protocol }}://{{ internal_ip }}:6385{% endif %} systemd.journald.forward_to_console=yes BOOTIF=${mac} nofb nomodeset vga=normal console=ttyS0 {{ inspector_extra_kernel_options | default('') }} ipa-insecure=1 initrd={{ ipa_ramdisk_url | basename }} initrd {{ ipa_ramdisk_url }} boot diff --git a/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 b/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 index 26996f8f2..1f0dbd953 100644 --- a/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 +++ b/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 @@ -21,6 +21,14 @@ transport_url = rabbit://ironic:{{ironic_db_password }}@{{ message_queue_host | transport_url = fake:// {% endif %} +{% if enable_tls | bool %} +use_ssl = True + +[ssl] +cert_file = {{ tls_certificate_path }} +key_file = {{ ironic_inspector_private_key_path }} +{% endif %} + [database] connection=mysql+pymysql://{{ ironic_inspector.database.username }}:{{ ironic_inspector.database.password }}@{{ ironic_inspector.database.host }}/{{ ironic_inspector.database.name }}?charset=utf8 @@ -50,6 +58,9 @@ endpoint_override = {{ ironic_api_url }} username = {{ admin_username }} password = {{ admin_password }} {% endif %} +{% if enable_tls | bool %} +cafile = {{ tls_certificate_path }} +{% endif %} {% if enable_keystone is defined and enable_keystone | bool == true %} [keystone_authtoken] @@ -60,7 +71,9 @@ password = {{ ironic_inspector.service_catalog.password }} user_domain_id = default project_name = service project_domain_id = default - +{% if enable_tls | bool %} +cafile = {{ tls_certificate_path }} +{% endif %} {% endif %} [processing] diff --git a/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 b/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 index 59f582abb..513b6885f 100644 --- a/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 +++ b/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 @@ -40,6 +40,15 @@ http_basic_auth_user_file = /etc/ironic/htpasswd log_dir = {{ ironic_log_dir }} {% endif %} +{% if enable_tls | bool %} +[api] +enable_ssl_api = True + +[ssl] +cert_file = {{ tls_certificate_path }} +key_file = {{ ironic_private_key_path }} +{% endif %} + [agent] {% if ironic_store_ramdisk_logs | bool %} deploy_logs_collect = always @@ -50,9 +59,9 @@ deploy_logs_local_path = {{ ironic_agent_deploy_logs_local_path }} [pxe] {% if testing | bool %} -pxe_append_params = console=ttyS0 +pxe_append_params = console=ttyS0 ipa-insecure=1 {% else %} -pxe_append_params = systemd.journald.forward_to_console=yes {{ extra_kernel_options | default('') }} +pxe_append_params = systemd.journald.forward_to_console=yes ipa-insecure=1 {{ extra_kernel_options | default('') }} {% endif %} pxe_config_template = $pybasedir/drivers/modules/ipxe_config.template tftp_server = {{ internal_ip }} @@ -108,7 +117,7 @@ use_web_server_for_images = true {% if enable_inspector | bool == true %} [inspector] power_off = {{ power_off_after_inspection }} -extra_kernel_params = {{ inspector_extra_kernel_options | default('') }} +extra_kernel_params = ipa-insecure=1 {{ inspector_extra_kernel_options | default('') }} {% if enable_keystone | bool %} auth_type = password auth_url = {{ ironic.service_catalog.auth_url }} @@ -118,16 +127,20 @@ user_domain_id = default project_name = {{ ironic.service_catalog.project_name }} project_domain_id = default region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}} -callback_endpoint_override = http://{{ internal_ip }}:5050 +# NOTE(dtantsur): this has to be on internal IP even if public IPs are used +callback_endpoint_override = {{ api_protocol }}://{{ internal_ip }}:5050 {% elif noauth_mode | bool %} auth_type=none -endpoint_override = http://{{ internal_ip }}:5050 +endpoint_override = {{ ironic_inspector_api_url }} {% else %} auth_type = http_basic -endpoint_override = http://{{ internal_ip }}:5050 +endpoint_override = {{ ironic_inspector_api_url }} username = {{ admin_username }} password = {{ admin_password }} {% endif %} +{% if enable_tls | bool %} +cafile = {{ tls_certificate_path }} +{% endif %} {% endif %} {% if enable_keystone is defined and enable_keystone | bool == true %} @@ -139,6 +152,9 @@ password = {{ ironic.service_catalog.password }} user_domain_id = default project_name = {{ ironic.service_catalog.project_name }} project_domain_id = default +{% if enable_tls | bool %} +cafile = {{ tls_certificate_path }} +{% endif %} {% endif %} [service_catalog] @@ -158,9 +174,14 @@ auth_type = http_basic username = {{ admin_username }} password = {{ admin_password }} {% endif %} -endpoint_override = http://{{ internal_ip }}:6385 +# NOTE(dtantsur): this has to be on internal IP even if public IPs are used +endpoint_override = {{ api_protocol }}://{{ internal_ip }}:6385 [json_rpc] +{% if enable_tls | bool %} +use_ssl = True +cafile = {{ tls_certificate_path }} +{% endif %} {% if enable_keystone | bool %} auth_strategy = keystone auth_url = {{ ironic.service_catalog.auth_url }} diff --git a/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml b/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml index a364cfefd..68be28aa6 100644 --- a/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml +++ b/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml @@ -4,8 +4,10 @@ network_interface: "virbr0" ans_network_interface: "{{ network_interface | replace('-', '_') }}" internal_ip: "{{ hostvars[inventory_hostname]['ansible_' + ans_network_interface]['ipv4']['address'] }}" +enable_tls: false + # Service URLs used for communication with them. -api_protocol: http +api_protocol: "{{ 'https' if enable_tls | bool else 'http' }}" ironic_api_url: "{{ api_protocol }}://{{ internal_ip }}:6385" ironic_inspector_api_url: "{{ api_protocol }}://{{ internal_ip }}:5050" diff --git a/playbooks/roles/bifrost-keystone-client-config/tasks/main.yml b/playbooks/roles/bifrost-keystone-client-config/tasks/main.yml index 6a55e0172..7f6bdc67f 100644 --- a/playbooks/roles/bifrost-keystone-client-config/tasks/main.yml +++ b/playbooks/roles/bifrost-keystone-client-config/tasks/main.yml @@ -31,6 +31,11 @@ - "{{ config_region_name is defined }}" - "{{ config_auth_url is defined }}" +- name: "Generate TLS parameters" + include_role: + name: bifrost-tls + when: enable_tls | bool + - name: "Ensure the ~/.config exists" file: name: "~{{ user | default('root') }}/.config" diff --git a/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 b/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 index 4be188458..d4ac31d05 100644 --- a/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 +++ b/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 @@ -13,16 +13,25 @@ clouds: project_domain_id: "{{ cloud.1.config_project_domain_id | default('default') }}" user_domain_id: "{{ cloud.1.config_user_domain_id | default('default') }}" identity_api_version: "3" +{% if enable_tls | bool %} + cacert: "{{ tls_certificate_path }}" +{% endif %} {% endfor %} {% elif noauth_mode | default(true) | bool %} bifrost: auth_type: "none" baremetal_endpoint_override: {{ ironic_api_url }} baremetal_introspection_endpoint_override: {{ ironic_inspector_api_url }} +{% if enable_tls | bool %} + cacert: "{{ tls_certificate_path }}" +{% endif %} # Deprecated bifrost-inspector: auth_type: "none" endpoint: {{ ironic_inspector_api_url }} +{% if enable_tls | bool %} + cacert: "{{ tls_certificate_path }}" +{% endif %} {% else %} bifrost: auth_type: "http_basic" @@ -31,6 +40,9 @@ clouds: password: "{{ default_password }}" endpoint: {{ ironic_api_url }} baremetal_introspection_endpoint_override: {{ ironic_inspector_api_url }} +{% if enable_tls | bool %} + cacert: "{{ tls_certificate_path }}" +{% endif %} bifrost-admin: auth_type: "http_basic" auth: @@ -38,4 +50,7 @@ clouds: password: "{{ admin_password }}" endpoint: {{ ironic_api_url }} baremetal_introspection_endpoint_override: {{ ironic_inspector_api_url }} +{% if enable_tls | bool %} + cacert: "{{ tls_certificate_path }}" +{% endif %} {% endif %} diff --git a/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 b/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 index e1275f107..3cc351287 100644 --- a/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 +++ b/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 @@ -36,3 +36,7 @@ case "${1:-bifrost}" in *) echo -e "\nERROR unsupported or unspecified profile: $1\nMust be one of bifrost, bifrost-admin";; esac {% endif %} + +{% if enable_tls | bool %} +export OS_CACERT="{{ tls_certificate_path }}" +{% endif %} diff --git a/playbooks/roles/bifrost-keystone-install/defaults/main.yml b/playbooks/roles/bifrost-keystone-install/defaults/main.yml index e1925360d..730b3993b 100644 --- a/playbooks/roles/bifrost-keystone-install/defaults/main.yml +++ b/playbooks/roles/bifrost-keystone-install/defaults/main.yml @@ -35,7 +35,7 @@ network_interface: "virbr0" ans_network_interface: "{{ network_interface | replace('-', '_') }}" internal_ip: "{{ hostvars[inventory_hostname]['ansible_' + ans_network_interface]['ipv4']['address'] }}" -api_protocol: http +api_protocol: "{{ 'https' if enable_tls | bool else 'http' }}" keystone_api_url: "{{ api_protocol }}://{{ internal_ip }}:5000/v3" # Defaults required by this role that are normally inherited via @@ -85,3 +85,9 @@ keystone: host: localhost pip_opts: "{{ lookup('env', 'PIP_OPTS') | default('') }}" + +# Enable TLS support. +enable_tls: false +tls_root: /etc/bifrost +tls_certificate_path: "{{ tls_root }}/bifrost.crt" +nginx_private_key_path: /etc/nginx/keystone.pem diff --git a/playbooks/roles/bifrost-keystone-install/tasks/bootstrap.yml b/playbooks/roles/bifrost-keystone-install/tasks/bootstrap.yml index 4ff41a53e..b3c80a4c8 100644 --- a/playbooks/roles/bifrost-keystone-install/tasks/bootstrap.yml +++ b/playbooks/roles/bifrost-keystone-install/tasks/bootstrap.yml @@ -77,6 +77,15 @@ login_password: "{{ mysql_password | default(None) }}" when: keystone.database.host == 'localhost' +- name: "Generate TLS parameters" + include_role: + name: bifrost-tls + vars: + dest_private_key_path: "{{ nginx_private_key_path }}" + dest_private_key_owner: "{{ nginx_user }}" + dest_private_key_group: "{{ nginx_user }}" + when: enable_tls | bool + - name: "Create an keystone service group" group: name: "keystone" diff --git a/playbooks/roles/bifrost-keystone-install/tasks/main.yml b/playbooks/roles/bifrost-keystone-install/tasks/main.yml index c23a45e75..4f901dadf 100644 --- a/playbooks/roles/bifrost-keystone-install/tasks/main.yml +++ b/playbooks/roles/bifrost-keystone-install/tasks/main.yml @@ -45,34 +45,15 @@ - enable_keystone | bool - not skip_bootstrap | bool +- name: "Upgrade existing installation" + include: upgrade.yml + when: + - enable_keystone | bool + - not skip_bootstrap | bool + - not skip_start | bool + - name: "Start Keystone services" include: start.yml when: - enable_keystone | bool - not skip_start | bool - -- name: "Change the bootstrap password from the static value on upgrade" - os_user: - name: "{{ keystone.bootstrap.username }}" - password: "{{ keystone.bootstrap.password }}" - update_password: always - state: present - domain: "default" - default_project: "{{ keystone.bootstrap.project_name }}" - auth: - auth_url: "{{ ironic.service_catalog.auth_url | default('http://127.0.0.1:5000/') }}" - username: "{{ keystone.bootstrap.username }}" - password: "ChangeThisPa55w0rd" - project_name: "{{ keystone.bootstrap.project_name | default('admin') }}" - project_domain_id: "default" - user_domain_id: "default" - wait: yes - environment: "{{ bifrost_venv_env }}" - no_log: true - ignore_errors: true - when: - - enable_keystone | bool - - not skip_bootstrap | bool - - test_created_keystone_db is undefined or not test_created_keystone_db.changed | bool - - keystone.bootstrap.enabled | bool - - keystone.database.host == 'localhost' diff --git a/playbooks/roles/bifrost-keystone-install/tasks/upgrade.yml b/playbooks/roles/bifrost-keystone-install/tasks/upgrade.yml new file mode 100644 index 000000000..74ae85c61 --- /dev/null +++ b/playbooks/roles/bifrost-keystone-install/tasks/upgrade.yml @@ -0,0 +1,121 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +# TODO(dtantsur): can be removed in W +- name: "Change the bootstrap password from the static value on upgrade" + openstack.cloud.identity_user: + name: "{{ keystone.bootstrap.username }}" + password: "{{ keystone.bootstrap.password }}" + update_password: always + state: present + domain: "default" + default_project: "{{ keystone.bootstrap.project_name }}" + auth: + auth_url: "{{ ironic.service_catalog.auth_url | default('http://127.0.0.1:5000/') }}" + username: "{{ keystone.bootstrap.username }}" + password: "ChangeThisPa55w0rd" + project_name: "{{ keystone.bootstrap.project_name | default('admin') }}" + project_domain_id: "default" + user_domain_id: "default" + wait: yes + ca_cert: "{{ tls_certificate_path | default(omit) }}" + environment: "{{ bifrost_venv_env }}" + no_log: true + ignore_errors: true + when: + - test_created_keystone_db is undefined or not test_created_keystone_db.changed | bool + - keystone.bootstrap.enabled | bool + - keystone.database.host == 'localhost' + +# NOTE(dtantsur): these tasks are required for update from HTTP to HTTPS + +- name: "Configure keystone auth with http" + set_fact: + keystone_auth: + auth_url: "{{ ironic.service_catalog.auth_url | default(keystone_api_url) | replace('https:', 'http:') }}" + username: "{{ keystone.bootstrap.username }}" + password: "{{ keystone.bootstrap.password }}" + project_name: "{{ keystone.bootstrap.project_name | default('admin') }}" + project_domain_id: "default" + user_domain_id: "default" + no_log: true + when: api_protocol == 'https' + +- name: "Configure keystone auth with https" + set_fact: + keystone_auth: + auth_url: "{{ ironic.service_catalog.auth_url | default(keystone_api_url) | replace('http:', 'https:') }}" + username: "{{ keystone.bootstrap.username }}" + password: "{{ keystone.bootstrap.password }}" + project_name: "{{ keystone.bootstrap.project_name | default('admin') }}" + project_domain_id: "default" + user_domain_id: "default" + # NOTE(dtantsur): we cannot use tls_certificate_path as it won't be present + # on an upgrade to non-TLS. + keystone_ca_cert: /etc/bifrost/bifrost.crt + no_log: true + when: api_protocol == 'http' + +- name: "Ensure keystone service record for keystone" + openstack.cloud.catalog_service: + state: present + name: "keystone" + service_type: "identity" + auth: "{{ keystone_auth }}" + wait: yes + ca_cert: "{{ keystone_ca_cert | default(omit) }}" + environment: "{{ bifrost_venv_env }}" + register: identity_catalog_service + ignore_errors: true + no_log: true + +- name: "Update identity internal endpoint" + openstack.cloud.endpoint: + state: present + service: "{{ identity_catalog_service.id }}" + endpoint_interface: internal + url: "{{ keystone.bootstrap.internal_url | default(keystone_private_url) | default(keystone_api_url) }}" + region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" + auth: "{{ keystone_auth }}" + ca_cert: "{{ keystone_ca_cert | default(omit) }}" + ignore_errors: true + no_log: true + when: identity_catalog_service.id is defined + +- name: "Update identity admin endpoint" + openstack.cloud.endpoint: + state: present + service: "{{ identity_catalog_service.id }}" + endpoint_interface: admin + url: "{{ keystone.bootstrap.admin_url | default(keystone_api_url) }}" + region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" + auth: "{{ keystone_auth }}" + ca_cert: "{{ keystone_ca_cert | default(omit) }}" + ignore_errors: true + no_log: true + when: identity_catalog_service.id is defined + +# NOTE(dtantsur): the public endpoint MUST go last, otherwise the other +# endpoints will fail to update. +- name: "Update identity public endpoint" + openstack.cloud.endpoint: + state: present + service: "{{ identity_catalog_service.id }}" + endpoint_interface: public + url: "{{ keystone.bootstrap.public_url | default(keystone_public_url) | default(keystone_api_url) }}" + region: "{{ keystone.bootstrap.region_name | default('RegionOne') }}" + auth: "{{ keystone_auth }}" + ca_cert: "{{ keystone_ca_cert | default(omit) }}" + ignore_errors: true + no_log: true + when: identity_catalog_service.id is defined diff --git a/playbooks/roles/bifrost-keystone-install/templates/nginx_conf.d_bifrost-keystone.conf.j2 b/playbooks/roles/bifrost-keystone-install/templates/nginx_conf.d_bifrost-keystone.conf.j2 index 4259cd74e..d736131f6 100644 --- a/playbooks/roles/bifrost-keystone-install/templates/nginx_conf.d_bifrost-keystone.conf.j2 +++ b/playbooks/roles/bifrost-keystone-install/templates/nginx_conf.d_bifrost-keystone.conf.j2 @@ -1,6 +1,12 @@ # {{ ansible_managed }} server { +{% if enable_tls | bool %} + listen 5000 ssl; + ssl_certificate {{ tls_certificate_path }}; + ssl_certificate_key {{ nginx_private_key_path }}; +{% else %} listen 5000; +{% endif %} access_log /var/log/nginx/keystone/access.log; error_log /var/log/nginx/keystone/error.log; location / { @@ -10,7 +16,13 @@ server { } } server { +{% if enable_tls | bool %} + listen 35357 ssl; + ssl_certificate {{ tls_certificate_path }}; + ssl_certificate_key {{ nginx_private_key_path }}; +{% else %} listen 35357; +{% endif %} access_log /var/log/nginx/keystone/access.log; error_log /var/log/nginx/keystone/error.log; location / { diff --git a/playbooks/roles/bifrost-tls/README.md b/playbooks/roles/bifrost-tls/README.md new file mode 100644 index 000000000..c255c7d0b --- /dev/null +++ b/playbooks/roles/bifrost-tls/README.md @@ -0,0 +1,86 @@ +bifrost-tls +=========== + +This role generates TLS certificates for Bifrost and copies the private key to +a predefined location. + +Requirements +------------ + +This role requires: + +- Ansible 2.9 + +Role Variables +-------------- + +generate_tls: Whether the generate new certificates or use existing ones. + If the latter, this role only handles copying the private key, + all files have to exist. Defaults to `false` to avoid overwriting + operator's files. + +network_interface: Network interface services are listening on. + +tls_common_name: The common name of the certificate. Defaults to the host's + full domain name (FQDN). + +tls_hosts: A list of valid IP addresses for the generated certificate. Defaults + to `public_ip` (if set), `private_ip` (if set), `internal_ip` and + 127.0.0.1. The host `localhost` is always added. + +tls_host_names: A list of valid host names for the generated certificate. + Defaults to the host's FQDN + `localhost`. + +tls_certificate_path: Path to the TLS certificate. Can be generated. + +tls_private_key_path: Path to the private key. Can be generated. + +tls_csr_path: Path to the signing request. Can be generated. + +tls_force_regenerate: Boolean, whether to regenerate existing certificates. + Defaults to `false`. + +dest_private_key_path: Destination to copy the private key to. Defaults to + undefined (not copying). + +dest_private_key_owner: Owner of the destination private key. Defaults to root. + +dest_private_key_group: Group of the destination private key. Defaults to root. + +Dependencies +------------ + +None at this time. + +Example Playbook +---------------- + +- hosts: localhost + connection: local + name: "Generate TLS parameters" + become: yes + gather_facts: yes + roles: + - role: bifrost-tls + generate_tls: true + tls_common_name: example.com + +License +------- + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author Information +------------------ + +Ironic Developers diff --git a/playbooks/roles/bifrost-tls/defaults/main.yml b/playbooks/roles/bifrost-tls/defaults/main.yml new file mode 100644 index 000000000..83beb9173 --- /dev/null +++ b/playbooks/roles/bifrost-tls/defaults/main.yml @@ -0,0 +1,34 @@ +--- +generate_tls: false + +# NOTE(dtantsur): we don't want to make our generated certificates accepted +# system-wide, hence storing them here. +tls_root: /etc/bifrost +tls_certificate_path: "{{ tls_root }}/bifrost.crt" +tls_private_key_path: "{{ tls_root }}/bifrost.pem" +tls_csr_path: "{{ tls_root }}/bifrost.csr" + +# Force re-generating of certificates. +tls_force_regenerate: false + +# Copy the resulting key to: +#dest_private_key_path: +dest_private_key_owner: root +dest_private_key_group: root +# Don't change this unless you really know what you're doing. +dest_private_key_mode: 0600 + +network_interface: "virbr0" +ans_network_interface: "{{ network_interface | replace('-', '_') }}" +internal_interface: "{{ hostvars[inventory_hostname]['ansible_' + ans_network_interface]['ipv4'] }}" +internal_ip: "{{ internal_interface['address'] }}" + +# Common name for the certificate. +tls_common_name: "{{ ansible_fqdn }}" +tls_hosts: >- + {{ [internal_ip, '127.0.0.1'] + + ([public_ip] if public_ip is defined else []) + + ([private_ip] if private_ip is defined else []) }} +tls_host_names: + - localhost + - "{{ ansible_fqdn }}" diff --git a/playbooks/roles/bifrost-tls/tasks/main.yml b/playbooks/roles/bifrost-tls/tasks/main.yml new file mode 100644 index 000000000..8386c013a --- /dev/null +++ b/playbooks/roles/bifrost-tls/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: "Ensure the certificate root directory" + file: + path: "{{ tls_root }}" + state: directory + owner: root + group: root + mode: 0755 + when: generate_tls | bool + +- name: "Generate private key" + openssl_privatekey: + path: "{{ tls_private_key_path }}" + force: "{{ tls_force_regenerate | bool }}" + owner: root + group: root + mode: 0600 + when: generate_tls | bool + +- name: "Generate certificate signing request" + openssl_csr: + path: "{{ tls_csr_path }}" + privatekey_path: "{{ tls_private_key_path }}" + force: "{{ tls_force_regenerate | bool }}" + owner: root + group: root + mode: 0600 + common_name: "{{ tls_common_name }}" + subject_alt_name: >- + {{ (tls_hosts | map('regex_replace', '^', 'IP:') | list) + + (tls_host_names | map('regex_replace', '^', 'DNS:') | list) }} + when: generate_tls | bool + +- name: "Generate self-signed TLS certificates" + openssl_certificate: + provider: selfsigned + path: "{{ tls_certificate_path }}" + privatekey_path: "{{ tls_private_key_path }}" + csr_path: "{{ tls_csr_path }}" + force: "{{ tls_force_regenerate | bool }}" + owner: root + group: root + mode: 0644 + when: generate_tls | bool + +- name: "Copy the key to the destination" + copy: + src: "{{ tls_private_key_path }}" + dest: "{{ dest_private_key_path }}" + remote_src: yes + owner: "{{ dest_private_key_owner }}" + group: "{{ dest_private_key_group }}" + mode: "{{ dest_private_key_mode }}" + when: dest_private_key_path is defined diff --git a/playbooks/roles/bifrost-unprovision-node-dynamic/tasks/main.yml b/playbooks/roles/bifrost-unprovision-node-dynamic/tasks/main.yml index f0d8a780a..77b51f3bc 100644 --- a/playbooks/roles/bifrost-unprovision-node-dynamic/tasks/main.yml +++ b/playbooks/roles/bifrost-unprovision-node-dynamic/tasks/main.yml @@ -20,6 +20,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" uuid: "{{ uuid | default() }}" name: "{{ name | default() }}" diff --git a/playbooks/roles/ironic-delete-dynamic/tasks/main.yml b/playbooks/roles/ironic-delete-dynamic/tasks/main.yml index 4e3ab467d..a7430c3c7 100644 --- a/playbooks/roles/ironic-delete-dynamic/tasks/main.yml +++ b/playbooks/roles/ironic-delete-dynamic/tasks/main.yml @@ -20,6 +20,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" driver: "" uuid: "{{ uuid | default() }}" diff --git a/playbooks/roles/ironic-enroll-dynamic/tasks/main.yml b/playbooks/roles/ironic-enroll-dynamic/tasks/main.yml index a04e3a0f1..3ac3d3adb 100644 --- a/playbooks/roles/ironic-enroll-dynamic/tasks/main.yml +++ b/playbooks/roles/ironic-enroll-dynamic/tasks/main.yml @@ -24,6 +24,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" driver: "{{ driver }}" uuid: "{{ uuid | default() }}" diff --git a/playbooks/roles/ironic-inspect-node/tasks/main.yml b/playbooks/roles/ironic-inspect-node/tasks/main.yml index 1d4bc4f5f..e8d2a5ef0 100644 --- a/playbooks/roles/ironic-inspect-node/tasks/main.yml +++ b/playbooks/roles/ironic-inspect-node/tasks/main.yml @@ -49,6 +49,7 @@ cloud: "{{ cloud_name | default(omit) }}" auth_type: "{{ auth_type | default(omit) }}" auth: "{{ auth | default(omit) }}" + ca_cert: "{{ tls_certificate_path | default(omit) }}" ironic_url: "{{ ironic_url | default(omit) }}" uuid: "{{ uuid | default('') }}" name: "{{ name | default('') }}" diff --git a/releasenotes/notes/tls-988e725820bb8aca.yaml b/releasenotes/notes/tls-988e725820bb8aca.yaml new file mode 100644 index 000000000..d5cae2117 --- /dev/null +++ b/releasenotes/notes/tls-988e725820bb8aca.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Supports TLS configuration by setting ``enable_tls=true`` and, optionally, + ``generate_tls=true``. The corresponding ``bifrost-cli`` argument is + ``--enable-tls`` (auto-generated certificates only). +issues: + - | + When using Keystone for authentication, it may not be possible to disable + TLS after enabling it if the certificate is in a non-standard location. + - | + Due to upgrade limitations, it may not be possible to enable TLS on + upgrading from a previous version. Do an upgrade first, then enable TLS + in a separate installation step. diff --git a/scripts/test-bifrost.sh b/scripts/test-bifrost.sh index f821a1ae8..f4e32e83a 100755 --- a/scripts/test-bifrost.sh +++ b/scripts/test-bifrost.sh @@ -13,6 +13,7 @@ ENABLE_KEYSTONE="${ENABLE_KEYSTONE:-false}" ZUUL_BRANCH=${ZUUL_BRANCH:-} CLI_TEST=${CLI_TEST:-false} BOOT_MODE=${BOOT_MODE:-} +ENABLE_TLS=${ENABLE_TLS:-false} # Set defaults for ansible command-line options to drive the different # tests. @@ -178,6 +179,8 @@ ${ANSIBLE} -vvvv \ -e enable_keystone=${ENABLE_KEYSTONE} \ -e wait_for_node_deploy=${WAIT_FOR_DEPLOY} \ -e not_enrolled_data_file=${BAREMETAL_DATA_FILE}.rest \ + -e enable_tls=${ENABLE_TLS} \ + -e generate_tls=${ENABLE_TLS} \ -e skip_install=${CLI_TEST} \ -e skip_package_install=${CLI_TEST} \ -e skip_bootstrap=${CLI_TEST} \ diff --git a/zuul.d/bifrost-jobs.yaml b/zuul.d/bifrost-jobs.yaml index 9533319eb..8ee1e0966 100644 --- a/zuul.d/bifrost-jobs.yaml +++ b/zuul.d/bifrost-jobs.yaml @@ -83,6 +83,7 @@ - openstack/keystone vars: enable_keystone: true + enable_tls: true test_driver: redfish - job: @@ -109,6 +110,7 @@ - openstack/keystone vars: enable_keystone: true + enable_tls: true test_driver: redfish - job: