From 1832b2bb3e9948f8bf6dfb3be50fd10363bc3a38 Mon Sep 17 00:00:00 2001 From: omnom62 Date: Wed, 12 Aug 2026 15:22:05 +1000 Subject: http-api: T8989: address further review comments - Scope mTLS CA chain to selected CA only (not all configured CAs) - Pass verify-client value to nginx template instead of hardcoding optional - Allow mTLS-only REST configuration without API keys - Add mTLS warning when no API keys are configured --- src/conf_mode/service_https.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'src/conf_mode/service_https.py') diff --git a/src/conf_mode/service_https.py b/src/conf_mode/service_https.py index b1381eca5..093209304 100755 --- a/src/conf_mode/service_https.py +++ b/src/conf_mode/service_https.py @@ -156,11 +156,18 @@ def verify(https): # If only key-based methods are enabled, # fail the commit if no valid key configurations are found - if (not valid_keys_exist) and (not jwt_auth): - raise ConfigError('At least one HTTPS API key is required unless GraphQL token authentication is enabled!') + mtls_auth = dict_search('certificates.verify_client', https) is not None + if (not valid_keys_exist) and (not jwt_auth) and (not mtls_auth): + raise ConfigError( + 'At least one HTTPS API key is required unless GraphQL token or mTLS authentication is enabled!' + ) if (not valid_keys_exist) and jwt_auth: Warning(f'API keys are not configured: classic (non-GraphQL) API will be unavailable!') + if (not valid_keys_exist) and mtls_auth and not jwt_auth: + Warning( + 'API keys are not configured: only mTLS client certificate authentication will be available for the REST API!' + ) return None @@ -224,15 +231,21 @@ def generate(https): ca_name = https['certificates']['ca_certificate'] pki_ca = dict_search(f'pki.ca.{ca_name}', https) if pki_ca: - # Build full chain: intermediate + root CAs for client cert verification - loaded_ca_certs = { + # Build chain for the selected CA only (not all configured CAs) + # to prevent any other CA from authenticating REST API clients + all_ca_certs = { load_certificate(cert_data['certificate']) for cert_data in dict_search('pki.ca', https, default={}).values() } + selected_ca = load_certificate(pki_ca['certificate']) + ca_chain_certs = find_chain(selected_ca, all_ca_certs) mtls_ca_path = os.path.join(cert_dir, f'{ca_name}_mtls_ca.pem') - ca_chain = '\n'.join(encode_certificate(c) for c in loaded_ca_certs) + ca_chain = '\n'.join(encode_certificate(c) for c in ca_chain_certs) write_file(mtls_ca_path, ca_chain, user=user, group=group, mode=0o644) https['certificates']['mtls_ca_path'] = mtls_ca_path + https['certificates']['verify_client'] = dict_search( + 'certificates.verify_client', https + ) render(config_file, 'https/nginx.default.j2', https) render(systemd_override, 'https/override.conf.j2', https) -- cgit v1.2.3