From a3e059e7e8d340f6f5e623bbc17acf18cb296626 Mon Sep 17 00:00:00 2001
From: Viacheslav Hletenko <v.gletenko@vyos.io>
Date: Fri, 8 Dec 2023 14:14:07 +0000
Subject: T5798: load-balancing revese-proxy add multiple SSL certificates

Add ability to configure multiple SSL certificates for
frontend/service

set load-balancing reverse-proxy service web mode http
set load-balancing reverse-proxy service web port 443
set load-balancing reverse-proxy service web ssl certificate cert1
set load-balancing reverse-proxy service web ssl certificate cert2

(cherry picked from commit fe99c45e05fd5794905145ddca80e6078145c2e8)
---
 data/templates/load-balancing/haproxy.cfg.j2         | 13 +++++++++----
 .../include/pki/certificate-multi.xml.i              | 15 +++++++++++++++
 interface-definitions/load-balancing-haproxy.xml.in  |  2 +-
 src/conf_mode/load-balancing-haproxy.py              | 20 +++++++++++---------
 4 files changed, 36 insertions(+), 14 deletions(-)
 create mode 100644 interface-definitions/include/pki/certificate-multi.xml.i

diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index a75ee9904..defb76fba 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -50,13 +50,19 @@ defaults
 {% if service is vyos_defined %}
 {%     for front, front_config in service.items() %}
 frontend {{ front }}
-{%         set ssl_front =  'ssl crt /run/haproxy/' ~ front_config.ssl.certificate ~ '.pem' if front_config.ssl.certificate is vyos_defined else '' %}
+{%         set ssl_front = [] %}
+{%         if front_config.ssl.certificate is vyos_defined and front_config.ssl.certificate is iterable %}
+{%             for cert in front_config.ssl.certificate %}
+{%                 set _ = ssl_front.append('crt /run/haproxy/' ~ cert ~ '.pem') %}
+{%             endfor %}
+{%         endif %}
+{%         set ssl_directive = 'ssl' if ssl_front else '' %}
 {%         if front_config.listen_address is vyos_defined %}
 {%             for address in front_config.listen_address %}
-    bind {{ address | bracketize_ipv6 }}:{{ front_config.port }} {{ ssl_front }}
+    bind {{ address | bracketize_ipv6 }}:{{ front_config.port }} {{ ssl_directive }} {{ ssl_front | join(' ') }}
 {%             endfor %}
 {%         else %}
-    bind :::{{ front_config.port }} v4v6 {{ ssl_front }}
+    bind :::{{ front_config.port }} v4v6 {{ ssl_directive }} {{ ssl_front | join(' ') }}
 {%         endif %}
 {%         if front_config.redirect_http_to_https is vyos_defined %}
     http-request redirect scheme https unless { ssl_fc }
@@ -161,4 +167,3 @@ backend {{ back }}
 
 {%     endfor %}
 {% endif %}
-
diff --git a/interface-definitions/include/pki/certificate-multi.xml.i b/interface-definitions/include/pki/certificate-multi.xml.i
new file mode 100644
index 000000000..c49c5d9b2
--- /dev/null
+++ b/interface-definitions/include/pki/certificate-multi.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from pki/certificate-multi.xml.i -->
+<leafNode name="certificate">
+  <properties>
+    <help>Certificate in PKI configuration</help>
+    <completionHelp>
+      <path>pki certificate</path>
+    </completionHelp>
+    <valueHelp>
+      <format>txt</format>
+      <description>Name of certificate in PKI configuration</description>
+    </valueHelp>
+    <multi/>
+  </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/load-balancing-haproxy.xml.in b/interface-definitions/load-balancing-haproxy.xml.in
index 564c335ec..8f6bd3a99 100644
--- a/interface-definitions/load-balancing-haproxy.xml.in
+++ b/interface-definitions/load-balancing-haproxy.xml.in
@@ -49,7 +49,7 @@
                   <help>SSL Certificate, SSL Key and CA</help>
                 </properties>
                 <children>
-                  #include <include/pki/certificate.xml.i>
+                  #include <include/pki/certificate-multi.xml.i>
                 </children>
               </node>
             </children>
diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py
index ec4311bb5..333ebc66c 100755
--- a/src/conf_mode/load-balancing-haproxy.py
+++ b/src/conf_mode/load-balancing-haproxy.py
@@ -108,17 +108,19 @@ def generate(lb):
         if 'ssl' in front_config:
 
             if 'certificate' in front_config['ssl']:
-                cert_name = front_config['ssl']['certificate']
-                pki_cert = lb['pki']['certificate'][cert_name]
-                cert_file_path = os.path.join(load_balancing_dir, f'{cert_name}.pem')
-                cert_key_path = os.path.join(load_balancing_dir, f'{cert_name}.pem.key')
+                cert_names = front_config['ssl']['certificate']
 
-                with open(cert_file_path, 'w') as f:
-                    f.write(wrap_certificate(pki_cert['certificate']))
+                for cert_name in cert_names:
+                    pki_cert = lb['pki']['certificate'][cert_name]
+                    cert_file_path = os.path.join(load_balancing_dir, f'{cert_name}.pem')
+                    cert_key_path = os.path.join(load_balancing_dir, f'{cert_name}.pem.key')
 
-                if 'private' in pki_cert and 'key' in pki_cert['private']:
-                    with open(cert_key_path, 'w') as f:
-                        f.write(wrap_private_key(pki_cert['private']['key']))
+                    with open(cert_file_path, 'w') as f:
+                        f.write(wrap_certificate(pki_cert['certificate']))
+
+                    if 'private' in pki_cert and 'key' in pki_cert['private']:
+                        with open(cert_key_path, 'w') as f:
+                            f.write(wrap_private_key(pki_cert['private']['key']))
 
             if 'ca_certificate' in front_config['ssl']:
                 ca_name = front_config['ssl']['ca_certificate']
-- 
cgit v1.2.3