diff options
| author | John Estabrook <jestabro@vyos.io> | 2026-09-02 08:37:55 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-02 08:37:55 -0500 |
| commit | ccd46229b1e2501662e92dc3af03442dac8cd9c6 (patch) | |
| tree | ba80d40245ae3a2e7cd93ced5409e4ed9471dbbe /src/conf_mode/service_https.py | |
| parent | 07c75ec9a9b2f59e3d0050e9c76893242a379679 (diff) | |
| parent | 71ea2f3d9f654f1b3ab9e08fa6cdff4447b3fc0a (diff) | |
| download | vyos-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-x | src/conf_mode/service_https.py | 52 |
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) |
