From 400c5a9f18325fd4dccb3e621fefd24d4e9a1d7b Mon Sep 17 00:00:00 2001
From: Martin Chlumsky <martin.chlumsky@ubisoft.com>
Date: Wed, 10 Mar 2021 21:58:02 -0500
Subject: [PATCH] Switch octavia-api to wsgi running under apache.

This change also adds support for Octavia backend TLS.

Closes-Bug: #1874228
Depends-On: https://review.opendev.org/c/openstack/kolla/+/779892
Change-Id: I5ff84aec4cdbc15f6a797391815243821dbdbd67
---
 .../conf/filter/01-rewrite-0.12.conf.j2       |  2 +-
 .../conf/filter/01-rewrite-0.14.conf.j2       |  2 +-
 ansible/roles/octavia/defaults/main.yml       | 11 ++++-
 ansible/roles/octavia/tasks/config.yml        | 20 ++++++++-
 .../octavia/templates/octavia-api.json.j2     | 31 +++++++++++--
 .../octavia/templates/octavia-wsgi.conf.j2    | 43 +++++++++++++++++++
 .../roles/octavia/templates/octavia.conf.j2   |  3 ++
 .../octavia-api-wsgi-cafa913364cb491c.yaml    |  9 ++++
 8 files changed, 114 insertions(+), 7 deletions(-)
 create mode 100644 ansible/roles/octavia/templates/octavia-wsgi.conf.j2
 create mode 100644 releasenotes/notes/octavia-api-wsgi-cafa913364cb491c.yaml

diff --git a/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2 b/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
index 1c607c8ad6..473b7a692c 100644
--- a/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
+++ b/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
@@ -1,7 +1,7 @@
 <match kolla.var.log.kolla.*.*.log>
     @type rewrite_tag_filter
     capitalize_regex_backreference yes
-    rewriterule1 programname ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|placement-api-access|panko-api-access)$ apache_access
+    rewriterule1 programname ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|octavia-api-access|placement-api-access|panko-api-access)$ apache_access
     rewriterule2 programname ^(aodh_wsgi_access|barbican_api_uwsgi_access|zun_api_wsgi_access|vitrage_wsgi_access)$ wsgi_access
     rewriterule3 programname ^(nova-api|nova-compute|nova-compute-ironic|nova-conductor|nova-manage|nova-novncproxy|nova-scheduler|nova-placement-api|placement-api|privsep-helper)$ openstack_python
     rewriterule4 programname ^(sahara-api|sahara-engine)$ openstack_python
diff --git a/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2 b/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
index 8ba4468ce4..4337b877f2 100644
--- a/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
+++ b/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
@@ -3,7 +3,7 @@
     capitalize_regex_backreference yes
   <rule>
     key     programname
-    pattern ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|placement-api-access|panko-api-access)$
+    pattern ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|octavia-api-access|placement-api-access|panko-api-access)$
     tag apache_access
   </rule>
   <rule>
diff --git a/ansible/roles/octavia/defaults/main.yml b/ansible/roles/octavia/defaults/main.yml
index 9876bf62bd..c2ca8e774a 100644
--- a/ansible/roles/octavia/defaults/main.yml
+++ b/ansible/roles/octavia/defaults/main.yml
@@ -16,11 +16,15 @@ octavia_services:
         mode: "http"
         external: false
         port: "{{ octavia_api_port }}"
+        listen_port: "{{ octavia_api_listen_port }}"
+        tls_backend: "{{ octavia_enable_tls_backend }}"
       octavia_api_external:
         enabled: "{{ enable_octavia }}"
         mode: "http"
         external: true
         port: "{{ octavia_api_port }}"
+        listen_port: "{{ octavia_api_listen_port }}"
+        tls_backend: "{{ octavia_enable_tls_backend }}"
   octavia-driver-agent:
     container_name: octavia_driver_agent
     group: octavia-driver-agent
@@ -105,7 +109,7 @@ octavia_api_enable_healthchecks: "{{ enable_container_healthchecks }}"
 octavia_api_healthcheck_interval: "{{ default_container_healthcheck_interval }}"
 octavia_api_healthcheck_retries: "{{ default_container_healthcheck_retries }}"
 octavia_api_healthcheck_start_period: "{{ default_container_healthcheck_start_period }}"
-octavia_api_healthcheck_test: ["CMD-SHELL", "healthcheck_curl http://{{ api_interface_address |  put_address_in_context('url') }}:{{ octavia_api_listen_port }}"]
+octavia_api_healthcheck_test: ["CMD-SHELL", "healthcheck_curl {{ 'https' if octavia_enable_tls_backend | bool else 'http' }}://{{ api_interface_address |  put_address_in_context('url') }}:{{ octavia_api_listen_port }}"]
 octavia_api_healthcheck_timeout: "{{ default_container_healthcheck_timeout }}"
 octavia_api_healthcheck:
   interval: "{{ octavia_api_healthcheck_interval }}"
@@ -327,3 +331,8 @@ octavia_amp_network_cidr: 10.1.0.0/24
 # Octavia provider drivers
 octavia_provider_drivers: "amphora:Amphora provider{% if neutron_plugin_agent == 'ovn'%}, ovn:OVN provider{% endif %}"
 octavia_provider_agents: "amphora_agent{% if neutron_plugin_agent == 'ovn'%}, ovn{% endif %}"
+
+####################
+# TLS
+####################
+octavia_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
diff --git a/ansible/roles/octavia/tasks/config.yml b/ansible/roles/octavia/tasks/config.yml
index b45adf4c94..c454ea668c 100644
--- a/ansible/roles/octavia/tasks/config.yml
+++ b/ansible/roles/octavia/tasks/config.yml
@@ -47,7 +47,7 @@
 
 - include_tasks: copy-certs.yml
   when:
-    - kolla_copy_ca_into_containers | bool
+    - kolla_copy_ca_into_containers | bool or octavia_enable_tls_backend | bool
 
 - name: Copying over config.json files for services
   template:
@@ -62,6 +62,24 @@
   notify:
     - "Restart {{ item.key }} container"
 
+- name: Copying over octavia-wsgi.conf
+  vars:
+    service: "{{ octavia_services['octavia-api'] }}"
+  become: true
+  template:
+    src: "{{ item }}"
+    dest: "{{ node_config_directory }}/octavia-api/octavia-wsgi.conf"
+    mode: "0660"
+  with_first_found:
+    - "{{ node_custom_config }}/octavia/{{ inventory_hostname }}/octavia-wsgi.conf"
+    - "{{ node_custom_config }}/octavia/octavia-wsgi.conf"
+    - "octavia-wsgi.conf.j2"
+  when:
+    - inventory_hostname in groups[service.group]
+    - service.enabled | bool
+  notify:
+    - Restart octavia-api container
+
 - name: Copying over octavia.conf
   vars:
     service_name: "{{ item.key }}"
diff --git a/ansible/roles/octavia/templates/octavia-api.json.j2 b/ansible/roles/octavia/templates/octavia-api.json.j2
index e62a7dc68e..42722db413 100644
--- a/ansible/roles/octavia/templates/octavia-api.json.j2
+++ b/ansible/roles/octavia/templates/octavia-api.json.j2
@@ -1,20 +1,45 @@
+{% 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": "octavia-api --config-file /etc/octavia/octavia.conf",
+    "command": "/usr/sbin/{{ apache_binary }} -DFOREGROUND",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/octavia.conf",
             "dest": "/etc/octavia/octavia.conf",
             "owner": "octavia",
             "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/octavia-wsgi.conf",
+            "dest": "/etc/{{ apache_conf_dir }}/octavia-wsgi.conf",
+            "owner": "octavia",
+            "perm": "0600"
         }{% if octavia_policy_file is defined %},
         {
             "source": "{{ container_config_directory }}/{{ octavia_policy_file }}",
             "dest": "/etc/octavia/{{ octavia_policy_file }}",
             "owner": "octavia",
             "perm": "0600"
-        }{% endif %}
-    ],
+        }{% endif %}{% if octavia_enable_tls_backend | bool %},
+        {
+            "source": "{{ container_config_directory }}/octavia-cert.pem",
+            "dest": "/etc/octavia/certs/octavia-cert.pem",
+            "owner": "octavia",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/octavia-key.pem",
+            "dest": "/etc/octavia/certs/octavia-key.pem",
+            "owner": "octavia",
+            "perm": "0600"
+        }
+    {% endif %}],
     "permissions": [
+        {
+            "path": "/var/log/kolla/octavia",
+            "owner": "octavia:octavia",
+            "recurse": true
+        },
         {
             "path": "/var/run/octavia",
             "owner": "octavia:octavia"
diff --git a/ansible/roles/octavia/templates/octavia-wsgi.conf.j2 b/ansible/roles/octavia/templates/octavia-wsgi.conf.j2
new file mode 100644
index 0000000000..c0c0bb40de
--- /dev/null
+++ b/ansible/roles/octavia/templates/octavia-wsgi.conf.j2
@@ -0,0 +1,43 @@
+{% set wsgi_directory = '/usr/bin' if octavia_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
+{% if octavia_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') }}:{{ octavia_api_listen_port }}
+
+ServerSignature Off
+ServerTokens Prod
+TraceEnable off
+TimeOut {{ kolla_httpd_timeout }}
+KeepAliveTimeout {{ kolla_httpd_keep_alive }}
+
+{% if octavia_logging_debug | bool %}
+LogLevel info
+{% endif %}
+
+<VirtualHost *:{{ octavia_api_listen_port }}>
+    WSGIDaemonProcess octavia-api processes={{ openstack_service_workers }} threads=1 user=octavia group=octavia display-name=octavia-api
+    WSGIProcessGroup octavia-api
+    WSGIScriptAlias / {{ wsgi_directory }}/octavia-wsgi
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/kolla/octavia/octavia-api-error.log
+    LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" logformat
+    CustomLog /var/log/kolla/octavia/octavia-api-access.log logformat
+    <Directory {{ wsgi_directory }}>
+        <Files octavia-wsgi>
+            Require all granted
+        </Files>
+    </Directory>
+{% if octavia_enable_tls_backend | bool %}
+    SSLEngine On
+    SSLCertificateFile /etc/octavia/certs/octavia-cert.pem
+    SSLCertificateKeyFile /etc/octavia/certs/octavia-key.pem
+{% endif %}
+</VirtualHost>
diff --git a/ansible/roles/octavia/templates/octavia.conf.j2 b/ansible/roles/octavia/templates/octavia.conf.j2
index 190dcc75d7..d766ed5198 100644
--- a/ansible/roles/octavia/templates/octavia.conf.j2
+++ b/ansible/roles/octavia/templates/octavia.conf.j2
@@ -2,6 +2,9 @@
 debug = {{ octavia_logging_debug }}
 
 log_dir = /var/log/kolla/octavia
+{% if service_name == "octavia-api" %}
+log_file = octavia-api.log
+{% endif %}
 
 transport_url = {{ rpc_transport_url }}
 
diff --git a/releasenotes/notes/octavia-api-wsgi-cafa913364cb491c.yaml b/releasenotes/notes/octavia-api-wsgi-cafa913364cb491c.yaml
new file mode 100644
index 0000000000..371c1f30c4
--- /dev/null
+++ b/releasenotes/notes/octavia-api-wsgi-cafa913364cb491c.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Switch octavia-api to wsgi running under apache.
+  - |
+    Added configuration options to enable backend TLS encryption from HAProxy
+    to the Octavia service. When used in conjunction with enabling TLS for
+    service API endpoints, network communication will be encrypted end to end,
+    from client through HAProxy to the Octavia service.
-- 
GitLab