From 7c2df87dede8db83f8e4a5a57beca4ec729256e4 Mon Sep 17 00:00:00 2001 From: James Kirsch Date: Fri, 11 Sep 2020 14:12:44 -0700 Subject: [PATCH] Add support for encrypting Ironic API This patch introduces an optional backend encryption for the Ironic API service. When used in conjunction with enabling TLS for service API endpoints, network communcation will be encrypted end to end, from client through HAProxy to the Ironic service. Change-Id: I9edf7545c174ca8839ceaef877bb09f49ef2b451 Partially-Implements: blueprint add-ssl-internal-network --- ansible/roles/ironic/defaults/main.yml | 7 +++ ansible/roles/ironic/handlers/main.yml | 1 + ansible/roles/ironic/tasks/config.yml | 14 +++++- .../ironic/templates/ironic-api-wsgi.conf.j2 | 50 +++++++++++++++++++ .../roles/ironic/templates/ironic-api.json.j2 | 22 +++++++- ansible/roles/ironic/templates/ironic.conf.j2 | 7 --- ...ices-backend-haproxy-29467a9771e99917.yaml | 7 +++ tests/run.yml | 2 + tests/test-ironic.sh | 10 +++- 9 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 ansible/roles/ironic/templates/ironic-api-wsgi.conf.j2 create mode 100644 releasenotes/notes/encrypt-additional-services-backend-haproxy-29467a9771e99917.yaml diff --git a/ansible/roles/ironic/defaults/main.yml b/ansible/roles/ironic/defaults/main.yml index 2305157d68..b4c0d2c1f7 100644 --- a/ansible/roles/ironic/defaults/main.yml +++ b/ansible/roles/ironic/defaults/main.yml @@ -16,12 +16,14 @@ ironic_services: external: false port: "{{ ironic_api_port }}" listen_port: "{{ ironic_api_listen_port }}" + tls_backend: "{{ ironic_enable_tls_backend }}" ironic_api_external: enabled: "{{ enable_ironic }}" mode: "http" external: true port: "{{ ironic_api_port }}" listen_port: "{{ ironic_api_listen_port }}" + tls_backend: "{{ ironic_enable_tls_backend }}" ironic-conductor: container_name: ironic_conductor group: ironic-conductor @@ -253,3 +255,8 @@ ironic_ks_users: user: "{{ ironic_inspector_keystone_user }}" password: "{{ ironic_inspector_keystone_password }}" role: "admin" + +#################### +# TLS +#################### +ironic_enable_tls_backend: "{{ kolla_enable_tls_backend }}" diff --git a/ansible/roles/ironic/handlers/main.yml b/ansible/roles/ironic/handlers/main.yml index 458d648e1b..a1b701506e 100644 --- a/ansible/roles/ironic/handlers/main.yml +++ b/ansible/roles/ironic/handlers/main.yml @@ -40,6 +40,7 @@ module_name: uri module_args: url: "{{ ironic_internal_endpoint }}" + validate_certs: false register: result until: result is success retries: 12 diff --git a/ansible/roles/ironic/tasks/config.yml b/ansible/roles/ironic/tasks/config.yml index 26111f48f5..45410533b5 100644 --- a/ansible/roles/ironic/tasks/config.yml +++ b/ansible/roles/ironic/tasks/config.yml @@ -33,7 +33,7 @@ - include_tasks: copy-certs.yml when: - - kolla_copy_ca_into_containers | bool + - kolla_copy_ca_into_containers | bool or ironic_enable_tls_backend | bool - name: Copying over config.json files for services template: @@ -244,5 +244,17 @@ notify: - "Restart {{ item.key }} container" +- name: Copying over ironic-api-wsgi.conf + template: + src: "ironic-api-wsgi.conf.j2" + dest: "{{ node_config_directory }}/ironic-api/ironic-api-wsgi.conf" + mode: "0660" + become: true + when: + - inventory_hostname in groups["ironic-api"] + - ironic_services["ironic-api"].enabled | bool + notify: + - "Restart ironic-api container" + - import_tasks: check-containers.yml when: kolla_action != "config" diff --git a/ansible/roles/ironic/templates/ironic-api-wsgi.conf.j2 b/ansible/roles/ironic/templates/ironic-api-wsgi.conf.j2 new file mode 100644 index 0000000000..c83138f274 --- /dev/null +++ b/ansible/roles/ironic/templates/ironic-api-wsgi.conf.j2 @@ -0,0 +1,50 @@ +{% set ironic_log_dir = '/var/log/kolla/ironic' %} +{% set wsgi_directory = '/usr/bin' if ironic_install_type == 'binary' else '/var/lib/kolla/venv/bin' %} +{% if ironic_enable_tls_backend | bool %} +{% if kolla_base_distro in ['centos'] %} +LoadModule ssl_module /usr/lib64/httpd/modules/mod_ssl.so +{% else %} +LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so +{% endif %} +{% endif %} +Listen {{ api_interface_address | put_address_in_context('url') }}:{{ ironic_api_listen_port }} + +ServerSignature Off +ServerTokens Prod +TraceEnable off +KeepAliveTimeout {{ kolla_httpd_keep_alive }} + + + + Options None + Require all granted + + + +ErrorLog "{{ ironic_log_dir }}/apache-error.log" + +CustomLog "{{ ironic_log_dir }}/apache-access.log" common + + +{% if ironic_logging_debug | bool %} +LogLevel info +{% endif %} + + + WSGIDaemonProcess ironic-api processes={{ openstack_service_workers }} threads=1 user=ironic group=ironic display-name=%{GROUP} + WSGIProcessGroup ironic-api + WSGIScriptAlias / {{ wsgi_directory }}/ironic-api-wsgi + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog "{{ ironic_log_dir }}/ironic-api-error.log" + LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" logformat + CustomLog "{{ ironic_log_dir }}/ironic-api-access.log" logformat +{% if ironic_enable_tls_backend | bool %} + SSLEngine on + SSLCertificateFile /etc/ironic/certs/ironic-cert.pem + SSLCertificateKeyFile /etc/ironic/certs/ironic-key.pem +{% endif %} + diff --git a/ansible/roles/ironic/templates/ironic-api.json.j2 b/ansible/roles/ironic/templates/ironic-api.json.j2 index 075b0d04ee..a03a6a6719 100644 --- a/ansible/roles/ironic/templates/ironic-api.json.j2 +++ b/ansible/roles/ironic/templates/ironic-api.json.j2 @@ -1,17 +1,37 @@ +{% set apache_binary = 'apache2' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd' %} +{% set apache_conf_dir = 'apache2/conf-enabled' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd/conf.d' %} { - "command": "ironic-api", + "command": "/usr/sbin/{{ apache_binary }} -DFOREGROUND", "config_files": [ { "source": "{{ container_config_directory }}/ironic.conf", "dest": "/etc/ironic/ironic.conf", "owner": "ironic", "perm": "0600" + }, + { + "source": "{{ container_config_directory }}/ironic-api-wsgi.conf", + "dest": "/etc/{{ apache_conf_dir }}/ironic-api-wsgi.conf", + "owner": "ironic", + "perm": "0600" }{% if ironic_policy_file is defined %}, { "source": "{{ container_config_directory }}/{{ ironic_policy_file }}", "dest": "/etc/ironic/{{ ironic_policy_file }}", "owner": "ironic", "perm": "0600" + }{% endif %}{% if ironic_enable_tls_backend | bool %}, + { + "source": "{{ container_config_directory }}/ironic-cert.pem", + "dest": "/etc/ironic/certs/ironic-cert.pem", + "owner": "ironic", + "perm": "0600" + }, + { + "source": "{{ container_config_directory }}/ironic-key.pem", + "dest": "/etc/ironic/certs/ironic-key.pem", + "owner": "ironic", + "perm": "0600" }{% endif %} ], "permissions": [ diff --git a/ansible/roles/ironic/templates/ironic.conf.j2 b/ansible/roles/ironic/templates/ironic.conf.j2 index 7e79cd2789..666adc5934 100644 --- a/ansible/roles/ironic/templates/ironic.conf.j2 +++ b/ansible/roles/ironic/templates/ironic.conf.j2 @@ -35,13 +35,6 @@ driver = noop policy_file = {{ ironic_policy_file }} {% endif %} -{% if service_name == 'ironic-api' %} -[api] -host_ip = {{ api_interface_address }} -port = {{ ironic_api_listen_port }} -api_workers = {{ openstack_service_workers }} -{% endif %} - {% if service_name == 'ironic-conductor' %} [conductor] automated_clean=false diff --git a/releasenotes/notes/encrypt-additional-services-backend-haproxy-29467a9771e99917.yaml b/releasenotes/notes/encrypt-additional-services-backend-haproxy-29467a9771e99917.yaml new file mode 100644 index 0000000000..76e8326172 --- /dev/null +++ b/releasenotes/notes/encrypt-additional-services-backend-haproxy-29467a9771e99917.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds configuration options to enable backend TLS encryption from HAProxy + to the Nova and Ironic services. When used in conjunction with enabling TLS + for service API endpoints, network communcation will be encrypted end to + end, from client through HAProxy to the backend service. diff --git a/tests/run.yml b/tests/run.yml index de2ca429fe..7265c6b260 100644 --- a/tests/run.yml +++ b/tests/run.yml @@ -383,6 +383,8 @@ cmd: test-ironic.sh executable: /bin/bash chdir: "{{ kolla_ansible_src_dir }}" + environment: + TLS_ENABLED: "{{ tls_enabled }}" when: scenario == "ironic" - name: Run test-masakari.sh script diff --git a/tests/test-ironic.sh b/tests/test-ironic.sh index 6cc3ede3f3..9e7876f2a1 100755 --- a/tests/test-ironic.sh +++ b/tests/test-ironic.sh @@ -35,7 +35,13 @@ function wait_for_placement_resources { for i in $(seq 1 120); do # Fetch provider UUIDs from Placement local providers - providers=$(curl -sH "X-Auth-Token: $token" $endpoint/resource_providers \ + args=( + -sH "X-Auth-Token: $token" + ) + if [[ "$TLS_ENABLED" = "True" ]]; then + args+=(--cacert $OS_CACERT) + fi + providers=$(curl "${args[@]}" $endpoint/resource_providers \ | ./jq -r '.resource_providers[].uuid') local p @@ -46,7 +52,7 @@ function wait_for_placement_resources { # A resource class inventory record looks something like # {"max_unit": 1, "min_unit": 1, "step_size": 1, "reserved": 0, "total": 1, "allocation_ratio": 1} # Subtract reserved from total (defaulting both to 0) - amount=$(curl -sH "X-Auth-Token: $token" $endpoint/resource_providers/$p/inventories \ + amount=$(curl "${args[@]}" $endpoint/resource_providers/$p/inventories \ | ./jq ".inventories.CUSTOM_$resource_class as \$cls | (\$cls.total // 0) - (\$cls.reserved // 0)") if [ $amount -gt 0 ]; then