diff --git a/doc/source/conf.py b/doc/source/conf.py index 01773e85243..068b0ea3d51 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -217,6 +217,8 @@ html_theme = "ansys_sphinx_theme" html_favicon = ansys_favicon html_theme_options = { + "announcement": """Starting with PyDPF-Core 0.15.0, gRPC communication with the DPF server defaults to requiring authentication. +Refer to this page for more information.""", "logo": { "image_dark": pyansys_logo_dark_mode, "image_light": pyansys_logo_light_mode, diff --git a/doc/source/getting_started/dpf_server.rst b/doc/source/getting_started/dpf_server.rst index 5b7986582d5..f810c4880f7 100644 --- a/doc/source/getting_started/dpf_server.rst +++ b/doc/source/getting_started/dpf_server.rst @@ -120,3 +120,39 @@ DPF Server can be run in a Docker container. docker build . -t dpf-core:v2025.1.pre0 --build-arg DPF_VERSION=251 5. To run the DPF Docker container, license it. For more information, see :ref:`DPF Preview License Agreement`. + +.. _ref_dpf_server_secure_mode: + +Run DPF Server in Secure mode wih mTLS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. warning:: + + Starting with Ansys 2026 R1 (DPF 2026.1.0, kernel version 11.0) and PyDPF-Core 0.15.0, + DPF Server gRPC connections default to using `authenticated mTLS Transport`. + + This change is also brought by service packs for the following older Ansys versions: + + - `Ansys 2025 R2 SP03` + - `Ansys 2025 R1 SP04` + - `Ansys 2024 R2 SP05` + + +.. note:: + + The default client-server communication mode being InProcess, + this change only impacts users connecting to a remote DPF Server over gRPC. + + +Both client and server now require mTLS certificates to establish a gRPC connection. + +The location to the mTLS certificates can be set using an environment variable ``ANSYS_GRPC_CERTIFICATES``. + +More information on the generation of certificates can be read on `Generating certificates for mtls`. + +This environment variable must be set both on the server machine and on the client machine when working remotely. + +The mTLS Transport mode can be disabled by setting ``DPF_DEFAULT_GRPC_MODE`` to ``insecure`` both client-side and server-side. + +This allows to fall back to the previous behavior when using gRPC communication. + diff --git a/src/ansys/dpf/core/server.py b/src/ansys/dpf/core/server.py index 841ded7744e..e89d65bb807 100644 --- a/src/ansys/dpf/core/server.py +++ b/src/ansys/dpf/core/server.py @@ -32,6 +32,7 @@ import functools import inspect import os +from pathlib import Path import platform import socket import sys @@ -43,8 +44,10 @@ from ansys import dpf from ansys.dpf.core import errors, server_context from ansys.dpf.core.misc import get_ansys_path, is_ubuntu +from ansys.dpf.core.server_context import ServerContext from ansys.dpf.core.server_factory import ( CommunicationProtocols, + DockerConfig, GrpcMode, ServerConfig, ServerFactory, @@ -158,17 +161,17 @@ def shutdown_all_session_servers(): def start_local_server( - ip=LOCALHOST, - port=DPF_DEFAULT_PORT, - ansys_path=None, - as_global=True, - load_operators=True, - use_docker_by_default=True, - docker_config=RUNNING_DOCKER, - timeout=20.0, - config=None, - use_pypim_by_default=True, - context=None, + ip: str = LOCALHOST, + port: int = DPF_DEFAULT_PORT, + ansys_path: Path | str = None, + as_global: bool = True, + load_operators: bool = True, + use_docker_by_default: bool = True, + docker_config: DockerConfig = RUNNING_DOCKER, + timeout: float = 20.0, + config: ServerConfig = None, + use_pypim_by_default: bool = True, + context: ServerContext = None, ) -> AnyServerType: """Start a new local DPF server at a given port and IP address. @@ -176,45 +179,58 @@ def start_local_server( the default) the server is stored globally, replacing the one stored previously. Otherwise, a user must keep a handle on their server. + .. warning:: + Starting with DPF 2026 R1 and PyDPF 0.15.0, the default gRPC server uses mTLS authentication. + Please refer to :ref:`ref_dpf_server_secure_mode` for more information on how to set up the + certificates and configure the server and client accordingly. + See the ``config`` parameter for more details. + Parameters ---------- - ip : str, optional + ip: IP address of the remote or local instance to connect to. The default is ``"LOCALHOST"``. - port : int, optional + port: Port to connect to the remote instance on. The default is ``"DPF_DEFAULT_PORT"``, which is 50054. - ansys_path : str or os.PathLike, optional + ansys_path: Root path for the Ansys installation directory. For example, ``"/ansys_inc/v212/"``. The default is the latest Ansys installation. - as_global : bool, optional + as_global: Global variable that stores the IP address and port for the DPF module. All DPF objects created in this Python session will use this IP and port. The default is ``True``. - load_operators : bool, optional + load_operators: Whether to automatically load the math operators. The default is ``True``. - use_docker_by_default : bool, optional + use_docker_by_default: If the environment variable DPF_DOCKER is set to a docker name and use_docker_by_default is True, the server is ran as a docker (default is True). - docker_config : server_factory.DockerConfig, optional + docker_config: To start DPF server as a docker, specify the docker configuration options here. - timeout : float, optional + timeout: Maximum number of seconds for the initialization attempt. The default is ``10``. Once the specified number of seconds passes, the connection fails. - config: ServerConfig, optional - Manages the type of server connection to use. - use_pypim_by_default: bool, optional + config: + Manages the type of server connection to use. Forced to LegacyGrpc on macOS. + Defaults to mTLS authenticated gRPC connection. + Define the path to the mTLS authentication certificates here if needed. + Define the default server authentication configuration with environment variables: + - ANSYS_GRPC_CERTIFICATES: path to the certificates directory + - DPF_GRPC_MODE: gRPC authentication mode, options are 'mtls' and 'insecure'. + More information available at :ref:`ref_dpf_server_secure_mode`. + use_pypim_by_default: Whether to use PyPIM functionalities by default when a PyPIM environment is detected. Defaults to True. - context: ServerContext, optional + context: Defines the settings that will be used to load DPF's plugins. A DPF xml file can be used to list the plugins and set up variables. Default is `server_context.SERVER_CONTEXT`. Returns ------- - server : server.ServerBase + server: + The newly created server. """ from ansys.dpf.core.misc import is_pypim_configured @@ -266,6 +282,8 @@ def start_local_server( if config is not None: grpc_mode = config.grpc_mode certs_dir = config.certificates_dir + else: + config = ServerConfig() server = server_type( ansys_path, ip, @@ -315,37 +333,49 @@ def start_local_server( def connect_to_server( - ip=LOCALHOST, - port=DPF_DEFAULT_PORT, - as_global=True, - timeout=10.0, - config=None, - context=None, + ip: str = LOCALHOST, + port: int = DPF_DEFAULT_PORT, + as_global: bool = True, + timeout: float = 10.0, + config: ServerConfig = None, + context: ServerContext = None, ): """Connect to an existing DPF server. This method sets the global default channel that is then used for the duration of the DPF session. + .. warning:: + Starting with DPF 2026 R1 and PyDPF 0.15.0, the default gRPC server uses mTLS authentication. + Please refer to :ref:`ref_dpf_server_secure_mode` for more information on how to set up the + certificates and configure the server and client accordingly. + See the ``config`` parameter for more details. + Parameters ---------- - ip : str + ip: IP address of the remote or local instance to connect to. The default is ``"LOCALHOST"``. - port : int + port: Port to connect to the remote instance on. The default is ``"DPF_DEFAULT_PORT"``, which is 50054. - as_global : bool, optional + as_global: Global variable that stores the IP address and port for the DPF module. All DPF objects created in this Python session will use this IP and port. The default is ``True``. - timeout : float, optional + timeout: Maximum number of seconds for the initialization attempt. The default is ``10``. Once the specified number of seconds passes, the connection fails. - config: ServerConfig, optional + config: Manages the type of server connection to use. Forced to LegacyGrpc on macOS. - context: ServerContext, optional + Defaults to mTLS authenticated gRPC connection. + Define the path to the mTLS authentication certificates here if needed. + Define the default server authentication configuration with environment variables: + - ANSYS_GRPC_CERTIFICATES: path to the certificates directory + - DPF_GRPC_MODE: gRPC authentication mode, options are 'mtls' and 'insecure'. + More information available at :ref:`ref_dpf_server_secure_mode`. + context: Defines the settings that will be used to load DPF's plugins. A DPF xml file can be used to list the plugins and set up variables. Default is `server_context.SERVER_CONTEXT`. diff --git a/src/ansys/dpf/core/server_factory.py b/src/ansys/dpf/core/server_factory.py index 912e90c4b9a..a5663d3bda3 100644 --- a/src/ansys/dpf/core/server_factory.py +++ b/src/ansys/dpf/core/server_factory.py @@ -66,6 +66,7 @@ class GrpcMode: DEFAULT_COMMUNICATION_PROTOCOL = CommunicationProtocols.InProcess DEFAULT_LEGACY = False DEFAULT_GRPC_MODE = GrpcMode.mTLS +DEFAULT_CERTIFICATES_DIR = None class DockerConfig: @@ -265,18 +266,33 @@ def find_port_available_for_docker_bind(port: int) -> int: class ServerConfig: """Provides an instance of ServerConfig object to manage the server type used. + .. warning:: + Starting with DPF 2026 R1 and PyDPF 0.15.0, the default gRPC server uses mTLS authentication. + Please refer to :ref:`ref_dpf_server_secure_mode` for more information on how to set up the + certificates and configure the server and client accordingly. + See the ``config`` parameter for more details. + The default parameters can be overwritten using the DPF_SERVER_TYPE environment variable. DPF_SERVER_TYPE=INPROCESS, DPF_SERVER_TYPE=GRPC, DPF_SERVER_TYPE=LEGACYGRPC can be used. Parameters ---------- - protocol : CommunicationProtocols, optional + protocol: Communication protocol for DPF server (e.g. InProcess, gRPC) - legacy : bool, optional + legacy: If legacy is set to True, the server will be using ansys-grpc-dpf Python module. If not, it will communicate with DPF binaries using ctypes and DPF CLayer calls. + grpc_mode: + Grpc mode to use when launching DPF server. + Can be one of the members of :class:`ansys.dpf.core.server_factory.GrpcMode`. + Defaults to mTLS authenticated mode. + More information available at :ref:`ref_dpf_server_secure_mode`. + certificates_dir: + Path to a directory containing the certificates to use for mTLS authentication. + More information available at :ref:`ref_dpf_server_secure_mode`. + Examples -------- @@ -307,7 +323,7 @@ def __init__( protocol: str = DEFAULT_COMMUNICATION_PROTOCOL, legacy: bool = DEFAULT_LEGACY, grpc_mode: str = DEFAULT_GRPC_MODE, - certificates_dir: Path = None, + certificates_dir: Path = DEFAULT_CERTIFICATES_DIR, ): self.legacy = legacy if not protocol: @@ -319,7 +335,11 @@ def __init__( self.grpc_mode = DEFAULT_GRPC_MODE else: self.grpc_mode = grpc_mode - self.certificates_dir = certificates_dir + self.certificates_dir = ( + certificates_dir + if certificates_dir + else os.environ.get("ANSYS_GRPC_CERTIFICATES", None) + ) def __str__(self): """Return a string representation of the ServerConfig instance. diff --git a/src/ansys/dpf/core/server_types.py b/src/ansys/dpf/core/server_types.py index 2028f4dab0a..87d12cb1a42 100644 --- a/src/ansys/dpf/core/server_types.py +++ b/src/ansys/dpf/core/server_types.py @@ -255,6 +255,12 @@ def launch_dpf( passes, the connection fails. context : , optional Context to apply to DPF server when launching it. + grpc_mode: + Grpc mode to use when launching DPF server. + Can be one of the members of :class:`ansys.dpf.core.server_factory.GrpcMode`. + Defaults to mTLS authenticated mode. + certificates_dir: + Path to a directory containing the certificates to use for mTLS authentication. """ process = _run_launch_server_process( ip, @@ -803,7 +809,46 @@ def __del__(self): class GrpcServer(CServer): - """Server using the gRPC communication protocol.""" + """Server using the gRPC communication protocol. + + Parameters + ---------- + ansys_path: + Root path for the Ansys installation directory. For example, ``"/ansys_inc/v212/"``. + The default is the latest Ansys installation. + ip: + IP address of the remote or local instance to connect to. The + default is ``"LOCALHOST"``. + port: + Port to connect to the remote instance on. The default is + ``"DPF_DEFAULT_PORT"``, which is 50054. + timeout: + Maximum number of seconds for the initialization attempt. + The default is ``10``. Once the specified number of seconds + passes, the connection fails. + as_global: + Set this server as the global server used by default by PyDPF. + The default is ``True``. + load_operators: + Whether to load the operators upon server initialization. + The default is ``True``. + launch_server: + Whether to launch a new server process. If ``False``, connects to an existing server. + The default is ``True``. + docker_config: + To start DPF server as a docker, specify the docker configurations here. + use_pypim: + Whether to use PyPIM to launch a remote DPF server if PyPIM is configured. + The default is ``True``. + context: + Context to apply to DPF server when launching it. + grpc_mode: + Grpc mode to use when launching DPF server. + Can be one of the members of :class:`ansys.dpf.core.server_factory.GrpcMode`. + Defaults to mTLS authenticated mode. + certificates_dir: + Path to a directory containing the certificates to use for mTLS authentication. + """ def __init__( self, @@ -1235,32 +1280,40 @@ class LegacyGrpcServer(BaseServer): Parameters ---------- - ansys_path : str + ansys_path: Path for the DPF executable. - ip : str + ip: IP address of the remote or local instance to connect to. The default is ``"LOCALHOST"``. - port : int + port: Port to connect to the remote instance on. The default is ``"DPF_DEFAULT_PORT"``, which is 50054. - timeout : float, optional + timeout: Maximum number of seconds for the initialization attempt. The default is ``10``. Once the specified number of seconds passes, the connection fails. - as_global : bool, optional + as_global: Global variable that stores the IP address and port for the DPF module. All DPF objects created in this Python session will use this IP and port. The default is ``True``. - load_operators : bool, optional - Whether to automatically load the math operators. The default + load_operators: + Whether to automatically load the operators. The default is ``True``. - launch_server : bool, optional + launch_server: Whether to launch the server on Windows. - docker_config : server_factory.DockerConfig, optional + docker_config: To start DPF server as a docker, specify the docker name here. - use_pypim: bool, optional + use_pypim: Whether to use PyPIM functionalities by default when a PyPIM environment is detected. Defaults to True. + context: + Context to apply to DPF server when launching it. + grpc_mode: + Grpc mode to use when launching DPF server. + Can be one of the members of :class:`ansys.dpf.core.server_factory.GrpcMode`. + Defaults to mTLS authenticated mode. + certificates_dir: + Path to a directory containing the certificates to use for mTLS authentication. """ def __init__(