summaryrefslogtreecommitdiff
path: root/src/conf_mode/service_https.py
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2026-09-02 08:37:55 -0500
committerGitHub <noreply@github.com>2026-09-02 08:37:55 -0500
commitccd46229b1e2501662e92dc3af03442dac8cd9c6 (patch)
treeba80d40245ae3a2e7cd93ced5409e4ed9471dbbe /src/conf_mode/service_https.py
parent07c75ec9a9b2f59e3d0050e9c76893242a379679 (diff)
parent71ea2f3d9f654f1b3ab9e08fa6cdff4447b3fc0a (diff)
downloadvyos-1x-ccd46229b1e2501662e92dc3af03442dac8cd9c6.tar.gz
vyos-1x-ccd46229b1e2501662e92dc3af03442dac8cd9c6.zip
Merge pull request #5381 from omnom62/T8989-rest-api-bearer
http-api: T8989: add REST Bearer token authentication
Diffstat (limited to 'src/conf_mode/service_https.py')
-rwxr-xr-xsrc/conf_mode/service_https.py52
1 files changed, 48 insertions, 4 deletions
diff --git a/src/conf_mode/service_https.py b/src/conf_mode/service_https.py
index 4a9311bfb..d10825ab5 100755
--- a/src/conf_mode/service_https.py
+++ b/src/conf_mode/service_https.py
@@ -77,9 +77,13 @@ def get_config(config=None):
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrieved.
default_values = conf.get_config_defaults(**https.kwargs, recursive=True)
- if 'api' not in https or 'graphql' not in https['api']:
+ if 'api' in https:
+ if 'graphql' not in https['api']:
+ del default_values['api']['graphql']
+ if 'rest' not in https['api']:
+ del default_values['api']['rest']
+ else:
del default_values['api']
-
# merge CLI and default dictionary
https = config_dict_merge(default_values, https)
@@ -108,6 +112,14 @@ def verify(https):
else:
Warning('No certificate specified, using build-in self-signed certificates. '\
'Do not use them in a production environment!')
+ if dict_search('certificates.verify_client', https) is not None:
+ if dict_search('certificates.ca_certificate', https) is None:
+ raise ConfigError(
+ 'CA certificate must be configured for mTLS client verification'
+ )
+ if dict_search('api.rest.authentication.oidc.jwks_url', https) is not None:
+ if dict_search('api.rest.authentication.oidc.issuer', https) is None:
+ raise ConfigError('OIDC issuer must be configured when jwks-url is set')
# Check if server port is already in use by a different application
listen_address = ['0.0.0.0']
@@ -147,11 +159,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
@@ -208,6 +227,31 @@ def generate(https):
tmp_path.update({'dh_file' : dh_path})
https['certificates'].update(tmp_path)
+ # Write mTLS CA chain if verify-client is configured
+ if dict_search('certificates.verify_client', https) and dict_search(
+ 'certificates.ca_certificate', https
+ ):
+ ca_name = https['certificates']['ca_certificate']
+ pki_ca = dict_search(f'pki.ca.{ca_name}', https)
+ if pki_ca:
+ # Write only the selected CA certificate to the nginx trust bundle.
+ # Using find_chain() would walk up to the root CA, allowing sibling
+ # intermediates under the same root to authenticate. Writing only the
+ # configured CA restricts the mTLS trust boundary to certificates
+ # issued directly by that CA.
+ selected_ca = load_certificate(pki_ca['certificate'])
+ mtls_ca_path = os.path.join(cert_dir, f'{ca_name}_mtls_ca.pem')
+ write_file(
+ mtls_ca_path,
+ encode_certificate(selected_ca),
+ 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)