From 316b0496b3dd7a9b33692b171391d9d17d535116 Mon Sep 17 00:00:00 2001 From: James Kirsch Date: Mon, 17 Aug 2020 14:28:39 -0700 Subject: [PATCH] Add support for encrypting Ironic API This patch introduces an optional backend encryption for the Ironic API and Ironic Inspector 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: I3e82c8ec112e53f907e89fea0c8c849072dcf957 Partially-Implements: blueprint add-ssl-internal-network Depends-On: https://review.opendev.org/#/c/742776/ --- ansible/roles/ironic/defaults/main.yml | 9 ++++ ansible/roles/ironic/handlers/main.yml | 1 + ansible/roles/ironic/tasks/config.yml | 26 +++++++++- .../ironic/templates/ironic-api-wsgi.conf.j2 | 50 +++++++++++++++++++ .../roles/ironic/templates/ironic-api.json.j2 | 22 +++++++- .../templates/ironic-inspector-wsgi.conf.j2 | 50 +++++++++++++++++++ .../ironic/templates/ironic-inspector.json.j2 | 22 +++++++- ansible/roles/ironic/templates/ironic.conf.j2 | 7 --- 8 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 ansible/roles/ironic/templates/ironic-api-wsgi.conf.j2 create mode 100644 ansible/roles/ironic/templates/ironic-inspector-wsgi.conf.j2 diff --git a/ansible/roles/ironic/defaults/main.yml b/ansible/roles/ironic/defaults/main.yml index 2305157d68..f516a58909 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 @@ -45,12 +47,14 @@ ironic_services: external: false port: "{{ ironic_inspector_port }}" listen_port: "{{ ironic_inspector_listen_port }}" + tls_backend: "{{ ironic_enable_tls_backend }}" ironic_inspector_external: enabled: "{{ enable_ironic }}" mode: "http" external: true port: "{{ ironic_inspector_port }}" listen_port: "{{ ironic_inspector_listen_port }}" + tls_backend: "{{ ironic_enable_tls_backend }}" ironic-pxe: container_name: ironic_pxe group: ironic-pxe @@ -253,3 +257,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..6f0ce9934a 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,29 @@ 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" + +- name: Copying over ironic-inspector-wsgi.conf + template: + src: "ironic-inspector-wsgi.conf.j2" + dest: "{{ node_config_directory }}/ironic-inspector/ironic-inspector-wsgi.conf" + mode: "0660" + become: true + when: + - inventory_hostname in groups["ironic-inspector"] + - ironic_services["ironic-inspector"].enabled | bool + notify: + - "Restart ironic-inspector 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-inspector-wsgi.conf.j2 b/ansible/roles/ironic/templates/ironic-inspector-wsgi.conf.j2 new file mode 100644 index 0000000000..9e0ab3ed2d --- /dev/null +++ b/ansible/roles/ironic/templates/ironic-inspector-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_inspector_listen_port }} + +ServerSignature Off +ServerTokens Prod +TraceEnable off +KeepAliveTimeout {{ kolla_httpd_keep_alive }} + + + + Options None + Require all granted + + + +ErrorLog "{{ ironic_log_dir }}/apache-error-ironic-inspector.log" + +CustomLog "{{ ironic_log_dir }}/apache-access-ironic-inspector.log" common + + +{% if ironic_logging_debug | bool %} +LogLevel info +{% endif %} + + + WSGIDaemonProcess ironic-inspector processes={{ openstack_service_workers }} threads=1 user=ironic group=ironic display-name=%{GROUP} + WSGIProcessGroup ironic-inspector + WSGIScriptAlias / {{ wsgi_directory }}/ironic-inspector-wsgi + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog "{{ ironic_log_dir }}/ironic-inspector-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-inspector-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-inspector.json.j2 b/ansible/roles/ironic/templates/ironic-inspector.json.j2 index d82d506d3d..d07e605be0 100644 --- a/ansible/roles/ironic/templates/ironic-inspector.json.j2 +++ b/ansible/roles/ironic/templates/ironic-inspector.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-inspector --config-file /etc/ironic-inspector/inspector.conf", + "command": "/usr/sbin/{{ apache_binary }} -DFOREGROUND", "config_files": [ { "source": "{{ container_config_directory }}/inspector.conf", "dest": "/etc/ironic-inspector/inspector.conf", "owner": "ironic-inspector", "perm": "0600" + }, + { + "source": "{{ container_config_directory }}/ironic-inspector-wsgi.conf", + "dest": "/etc/{{ apache_conf_dir }}/ironic-inspector-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 %} ] } 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