summaryrefslogtreecommitdiff
path: root/src/conf_mode/service_https.py
diff options
context:
space:
mode:
authorLee Clements <lclements0@gmail.com>2026-06-26 10:48:38 -0400
committerLee Clements <lclements0@gmail.com>2026-06-27 01:34:17 -0400
commit78bec494bd6df824875ff132920f6f293d82ac0c (patch)
tree14979628f0e6e96a4d4f470f0d07b3c07cac1471 /src/conf_mode/service_https.py
parent477b1b9137ef42407e18b68684485ae623834456 (diff)
downloadvyos-1x-78bec494bd6df824875ff132920f6f293d82ac0c.tar.gz
vyos-1x-78bec494bd6df824875ff132920f6f293d82ac0c.zip
https: T9022: serve the full CA certificate chain
When a certificate is assigned to the HTTPS service, nginx was only sent the leaf certificate. An intermediate CA was included only when an operator manually configured "ca-certificate", and even then just that single CA - the rest of the issuer chain was never followed. As a result, clients that do not already trust the issuing intermediate CA (for example Let's Encrypt's newer E- and R-series intermediates) could not build a path to a trusted root and rejected the connection. Build the complete chain from the CA certificates present in the PKI using find_chain(), the same helper already used by HAProxy, OpenConnect, stunnel and the other PKI consumers. The intermediate chain is now discovered automatically, so configuring "ca-certificate" is no longer required; it remains accepted for backwards compatibility.
Diffstat (limited to 'src/conf_mode/service_https.py')
-rwxr-xr-xsrc/conf_mode/service_https.py23
1 files changed, 16 insertions, 7 deletions
diff --git a/src/conf_mode/service_https.py b/src/conf_mode/service_https.py
index 13a4930fd..4a9311bfb 100755
--- a/src/conf_mode/service_https.py
+++ b/src/conf_mode/service_https.py
@@ -30,7 +30,9 @@ from vyos.configverify import verify_pki_ca_certificate
from vyos.configverify import verify_pki_dh_parameters
from vyos.configdiff import get_config_diff
from vyos.defaults import api_config_state
-from vyos.pki import wrap_certificate
+from vyos.pki import encode_certificate
+from vyos.pki import find_chain
+from vyos.pki import load_certificate
from vyos.pki import wrap_private_key
from vyos.pki import wrap_dh_parameters
from vyos.template import render
@@ -176,12 +178,19 @@ def generate(https):
cert_path = os.path.join(cert_dir, f'{cert_name}_cert.pem')
key_path = os.path.join(cert_dir, f'{cert_name}_key.pem')
- server_cert = str(wrap_certificate(pki_cert['certificate']))
-
- # Append CA certificate if specified to form a full chain
- if 'ca_certificate' in https['certificates']:
- ca_cert = https['certificates']['ca_certificate']
- server_cert += '\n' + str(wrap_certificate(https['pki']['ca'][ca_cert]['certificate']))
+ # Build the full certificate chain (server certificate followed by any
+ # intermediate CA certificates up to the root) from the CA certificates
+ # available in the PKI. Serving the complete chain lets clients that do
+ # not yet trust the issuing intermediate CA validate the presented
+ # certificate. This mirrors the other PKI consumers (HAProxy, stunnel).
+ loaded_ca_certs = {
+ load_certificate(cert_data['certificate'])
+ for cert_data in dict_search('pki.ca', https, default={}).values()
+ }
+
+ loaded_pki_cert = load_certificate(pki_cert['certificate'])
+ cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
+ server_cert = '\n'.join(encode_certificate(c) for c in cert_full_chain)
write_file(cert_path, server_cert, user=user, group=group, mode=0o644)
write_file(key_path, wrap_private_key(pki_cert['private']['key']),