From 58b9551758575fd2d7494d1fb9e5459729a67c60 Mon Sep 17 00:00:00 2001 From: Lilko Petkov <99439480+LilkoPetkov@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:12:04 +0300 Subject: [PATCH] feat: Enable SSL flags for local development (#7060) * ssl-support: Enable SSL for local development to simulate a production-like environment. This would allow testing features requiring SSL during development, third party integrations, and allowing easier SSL integration without WAFs and reverse proxies * rebase * rebase fixes --------- Co-authored-by: lpetkov Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com> Co-authored-by: Jordan Frazier --- docs/docs/Configuration/configuration-cli.md | 2 ++ .../Configuration/environment-variables.md | 4 ++- src/backend/base/langflow/__main__.py | 35 +++++++++++++------ .../base/langflow/services/settings/base.py | 4 +++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/docs/docs/Configuration/configuration-cli.md b/docs/docs/Configuration/configuration-cli.md index 10f2cb679..cb4c286fa 100644 --- a/docs/docs/Configuration/configuration-cli.md +++ b/docs/docs/Configuration/configuration-cli.md @@ -122,6 +122,8 @@ python -m langflow run [OPTIONS] | `--auto-saving-interval` | `1000` | Integer | Set the interval for flow auto-saving in milliseconds.
See [`LANGFLOW_AUTO_SAVING_INTERVAL` variable](./environment-variables.md#LANGFLOW_AUTO_SAVING_INTERVAL). | | `--health-check-max-retries` | `5` | Integer | Set the maximum number of retries for the health check. Use `--no-health-check-max-retries` to disable the maximum number of retries for the health check.
See [`LANGFLOW_HEALTH_CHECK_MAX_RETRIES` variable](./environment-variables.md#LANGFLOW_HEALTH_CHECK_MAX_RETRIES). | | `--max-file-size-upload` | `100` | Integer | Set the maximum file size for the upload in megabytes.
See [`LANGFLOW_MAX_FILE_SIZE_UPLOAD` variable](./environment-variables.md#LANGFLOW_MAX_FILE_SIZE_UPLOAD). | +| `--ssl-cert-file-path` | Not set | String | Path to the SSL certificate file on the local system. | +| `--ssl-key-file-path` | Not set | String | Path to the SSL key file on the local system. | | `--help` | *Not applicable* | *Not applicable* | Display information about the command usage and its options and arguments. | ### langflow superuser diff --git a/docs/docs/Configuration/environment-variables.md b/docs/docs/Configuration/environment-variables.md index 63121f1e2..a76fd9479 100644 --- a/docs/docs/Configuration/environment-variables.md +++ b/docs/docs/Configuration/environment-variables.md @@ -226,9 +226,11 @@ The following table lists the environment variables supported by Langflow. | LANGFLOW_LOAD_FLOWS_PATH | String | Not set | Path to a directory containing flow JSON files to be loaded on startup. Note that this feature only works if LANGFLOW_AUTO_LOGIN is enabled. | | LANGFLOW_WORKER_TIMEOUT | Integer | `300` | Worker timeout in seconds.
See [`--worker-timeout` option](./configuration-cli.md#run-worker-timeout). | | LANGFLOW_WORKERS | Integer | `1` | Number of worker processes.
See [`--workers` option](./configuration-cli.md#run-workers). | - +| LANGFLOW_SSL_CERT_FILE | String | Not set | Path to the SSL certificate file on the local system. | +| LANGFLOW_SSL_KEY_FILE | String | Not set | Path to the SSL key file on the local system. | + ## Configure .env, override.conf, and tasks.json files The following examples show how to configure Langflow using environment variables in different scenarios. diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index 40e083698..aec731fa7 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -161,6 +161,10 @@ def run( help="Defines the polling interval for the webhook.", show_default=False, ), + ssl_cert_file_path: str | None = typer.Option( + None, help="Defines the SSL certificate file path.", show_default=False + ), + ssl_key_file_path: str | None = typer.Option(None, help="Defines the SSL key file path.", show_default=False), ) -> None: """Run Langflow.""" # Register SIGTERM handler @@ -202,6 +206,8 @@ def run( log_level = settings_service.settings.log_level frontend_path = settings_service.settings.frontend_path backend_only = settings_service.settings.backend_only + ssl_cert_file_path = settings_service.settings.ssl_cert_file if ssl_cert_file_path is None else ssl_cert_file_path + ssl_key_file_path = settings_service.settings.ssl_key_file if ssl_key_file_path is None else ssl_key_file_path # create path object if frontend_path is provided static_files_dir: Path | None = Path(frontend_path) if frontend_path else None @@ -215,7 +221,10 @@ def run( "bind": f"{host}:{port}", "workers": get_number_of_workers(workers), "timeout": worker_timeout, + "certfile": ssl_cert_file_path, + "keyfile": ssl_key_file_path, } + protocol = "https" if options["keyfile"] and options["certfile"] else "http" # Define an env variable to know if we are just testing the server if "pytest" in sys.modules: @@ -226,10 +235,10 @@ def run( # Run using uvicorn on MacOS and Windows # Windows doesn't support gunicorn # MacOS requires an env variable to be set to use gunicorn - run_on_windows(host, port, log_level, options, app) + run_on_windows(host, port, log_level, options, app, protocol) else: # Run using gunicorn on Linux - process = run_on_mac_or_linux(host, port, log_level, options, app) + process = run_on_mac_or_linux(host, port, log_level, options, app, protocol) if open_browser and not backend_only: click.launch(f"http://{host}:{port}") if process: @@ -250,12 +259,14 @@ def run( raise typer.Exit(1) from e -def wait_for_server_ready(host, port) -> None: +def wait_for_server_ready(host, port, protocol) -> None: """Wait for the server to become ready by polling the health endpoint.""" status_code = 0 while status_code != httpx.codes.OK: try: - status_code = httpx.get(f"http://{host}:{port}/health").status_code + status_code = httpx.get( + f"{protocol}://{host}:{port}/health", verify=host not in ("127.0.0.1", "localhost") + ).status_code except HTTPError: time.sleep(1) except Exception: # noqa: BLE001 @@ -263,18 +274,18 @@ def wait_for_server_ready(host, port) -> None: time.sleep(1) -def run_on_mac_or_linux(host, port, log_level, options, app): +def run_on_mac_or_linux(host, port, log_level, options, app, protocol): webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app)) webapp_process.start() - wait_for_server_ready(host, port) + wait_for_server_ready(host, port, protocol) - print_banner(host, port) + print_banner(host, port, protocol) return webapp_process -def run_on_windows(host, port, log_level, options, app) -> None: +def run_on_windows(host, port, log_level, options, app, protocol) -> None: """Run the Langflow server on Windows.""" - print_banner(host, port) + print_banner(host, port, protocol) run_langflow(host, port, log_level, options, app) @@ -358,7 +369,7 @@ def stylize_text(text: str, to_style: str, *, is_prerelease: bool) -> str: return text.replace(to_style, styled_text) -def print_banner(host: str, port: int) -> None: +def print_banner(host: str, port: int, protocol: str) -> None: notices = [] package_names = [] # Track package names for pip install instructions is_pre_release = False # Track if any package is a pre-release @@ -398,7 +409,7 @@ def print_banner(host: str, port: int) -> None: "We collect anonymous usage data to improve Langflow.\n" "You can opt-out by setting [bold]DO_NOT_TRACK=true[/bold] in your environment." ) - access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]" + access_link = f"Access [link={protocol}://{host}:{port}]{protocol}://{host}:{port}[/link]" panel_content = "\n\n".join([title, *styled_notices, info_text, telemetry_text, access_link]) panel = Panel(panel_content, box=box.ROUNDED, border_style="blue", expand=False) @@ -416,6 +427,8 @@ def run_langflow(host, port, log_level, options, app) -> None: port=port, log_level=log_level.lower(), loop="asyncio", + ssl_keyfile=options["keyfile"], + ssl_certfile=options["certfile"], ) else: from langflow.server import LangflowApplication diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index e89252e9c..e35c7b6f0 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -224,6 +224,10 @@ class Settings(BaseSettings): """The polling interval for the webhook in ms.""" fs_flows_polling_interval: int = 10000 """The polling interval in milliseconds for synchronizing flows from the file system.""" + ssl_cert_file: str | None = None + """Path to the SSL certificate file on the local system.""" + ssl_key_file: str | None = None + """Path to the SSL key file on the local system.""" # MCP Server mcp_server_enabled: bool = True