From e3ad17ec5e2e10b668aa9288d02b92319085ffaa Mon Sep 17 00:00:00 2001 From: grs Date: Mon, 12 May 2025 17:08:36 -0400 Subject: [PATCH] feat: enable mutual tls (#2140) # What does this PR do? This adds a config option for a CA to be specified with which client certs are verified. If specified client certs are required. This offers a simple way of securing access to the server. (Note: at present it is not possible to access the details of the client certificate using uvicorn (unless it was monkey patched). Though there is a defined TLS extension for ASGI, this is not implemented in uvicorn pending a review and likely change to the specification. See https://github.com/encode/uvicorn/pull/1119 and https://github.com/django/asgiref/issues/466. Without access to the DN it isn't possible to set user access attributes for a mutually authentication tls connection, so more fine grained access control is not yet possible). [//]: # (If resolving an issue, uncomment and update the line below) [//]: # (Closes #[issue-number]) ## Test Plan Used proposed config option to specify a CA and verified that the server can only be accessed with a valid client certificate. [//]: # (## Documentation) Signed-off-by: Gordon Sim --- llama_stack/distribution/datatypes.py | 4 ++++ llama_stack/distribution/server/server.py | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/llama_stack/distribution/datatypes.py b/llama_stack/distribution/datatypes.py index 7de009b87..d36e21c6d 100644 --- a/llama_stack/distribution/datatypes.py +++ b/llama_stack/distribution/datatypes.py @@ -249,6 +249,10 @@ class ServerConfig(BaseModel): default=None, description="Path to TLS key file for HTTPS", ) + tls_cafile: str | None = Field( + default=None, + description="Path to TLS CA file for HTTPS with mutual TLS authentication", + ) auth: AuthenticationConfig | None = Field( default=None, description="Authentication configuration for the server", diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index e34a62b00..32046d2b1 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -9,6 +9,7 @@ import asyncio import inspect import json import os +import ssl import sys import traceback import warnings @@ -484,7 +485,14 @@ def main(args: argparse.Namespace | None = None): "ssl_keyfile": keyfile, "ssl_certfile": certfile, } - logger.info(f"HTTPS enabled with certificates:\n Key: {keyfile}\n Cert: {certfile}") + if config.server.tls_cafile: + ssl_config["ssl_ca_certs"] = config.server.tls_cafile + ssl_config["ssl_cert_reqs"] = ssl.CERT_REQUIRED + logger.info( + f"HTTPS enabled with certificates:\n Key: {keyfile}\n Cert: {certfile}\n CA: {config.server.tls_cafile}" + ) + else: + logger.info(f"HTTPS enabled with certificates:\n Key: {keyfile}\n Cert: {certfile}") listen_host = ["::", "0.0.0.0"] if not config.server.disable_ipv6 else "0.0.0.0" logger.info(f"Listening on {listen_host}:{port}")