diff --git a/doc/changelog.d/1469.maintenance.md b/doc/changelog.d/1469.maintenance.md new file mode 100644 index 0000000000..53fb594a06 --- /dev/null +++ b/doc/changelog.d/1469.maintenance.md @@ -0,0 +1 @@ +Update gPRC documentation diff --git a/doc/source/conf.py b/doc/source/conf.py index 86aecd0742..9a35283822 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -116,6 +116,9 @@ numpydoc_validation_exclude = { # set of regex # grpc files r"\.*pb2\.*", + # Exclude built-in exception attributes from all exception classes + r".*__cause__$", + r".*__context__$", } # Favicon @@ -227,7 +230,7 @@ "show_breadcrumbs": True, "collapse_navigation": True, "use_edit_page_button": True, - "header_links_before_dropdown": 5, # number of links before the dropdown menu + "header_links_before_dropdown": 6, # number of links before the dropdown menu "additional_breadcrumbs": [ ("PyAnsys", "https://docs.pyansys.com/"), ], diff --git a/doc/source/index.rst b/doc/source/index.rst index c7011c8b03..34252540e4 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -146,5 +146,6 @@ If you are not sure which mode to pick, see :doc:`getting_started/choose_your_mo API reference contribute faq + contribute kil/index changelog \ No newline at end of file diff --git a/doc/source/user_guide/remote_session/grpc_security.rst b/doc/source/user_guide/remote_session/grpc_security.rst index bd3f140f89..322d02ec47 100644 --- a/doc/source/user_guide/remote_session/grpc_security.rst +++ b/doc/source/user_guide/remote_session/grpc_security.rst @@ -3,11 +3,115 @@ Secure gRPC connections ======================= -PyMechanical supports secure gRPC connections using mTLS, WNUA, or insecure modes. +PyMechanical supports gRPC connections using mTLS, WNUA, or insecure modes. -.. warning:: - Secure connections (mTLS, WNUA) require specific service packs for each version. - Versions without the required service pack only support insecure mode. +.. _transport_modes: + +Transport mode comparison +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. _mtls_mode: + +**mTLS (Mutual TLS)** + +Provides certificate-based mutual authentication and encryption. Both server and client must possess valid certificates signed by the same Certificate Authority (CA). This mode is platform-independent and recommended for production deployments requiring strong security. + +*Authentication mechanism:* Both parties validate each other's certificates against a trusted CA. The connection is rejected if either certificate is invalid or untrusted. + +*Encryption:* All data transmitted is encrypted using TLS protocol. + +*Cross-platform:* Works on Windows and Linux. + +*Network scope:* Can operate across any network including the internet, cloud deployments, and remote connections. + +.. _wnua_mode: + +**WNUA (Windows Named User Authentication)** + +Windows-only mode that authenticates using the current Windows user's credentials through SSPI/Negotiate protocol. Provides authentication based on Windows identity but does not encrypt the data channel. + +*Authentication mechanism:* The client authenticates as the currently logged-in Windows user. Only the same Windows user account can connect to the server, regardless of which machine they're connecting from within the domain/workgroup. + +*Encryption:* Authentication is secure, but the data channel is **not encrypted**. + +*Platform restriction:* Windows only. Both server and client must be Windows systems. + +*Network scope:* Limited to Windows domain/workgroup networks. Does not work across domain boundaries without proper trust relationships. + +*Access control:* Only the same Windows user who started the server can connect, even from different machines. + +.. _insecure_mode: + +**Insecure** + +Provides no authentication or encryption. Any client that can reach the network port can connect without credentials. + +*Authentication mechanism:* None. Any client can connect. + +*Encryption:* None. All data is transmitted in plaintext. + +*Security risk:* Suitable only for local development on trusted machines. Never use in production or on untrusted networks. + +.. _host_and_ip: + +Host binding +^^^^^^^^^^^^ + +The server's host address determines which network interfaces accept connections: + +- **127.0.0.1 (localhost)**: Listens only on loopback. Only same-machine clients can connect. Traffic never leaves the machine. + +- **0.0.0.0 (all interfaces)**: Listens on all network interfaces. Any client that can route to the machine can attempt connection, subject to transport mode authentication. + +- **Specific IP** (e.g., 192.168.1.100): Listens only on that interface. Only clients that can route to that specific IP can attempt connection. + +.. _ip_connectivity: + +Client connectivity by IP and transport mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Server on 0.0.0.0 (all interfaces):** + +- **mTLS**: Any client from any network with valid certificates can connect. Access controlled by certificate validation. +- **WNUA**: Only same Windows user can connect, from same or different machines within domain/workgroup. Different users blocked. +- **Insecure**: Anyone who can reach the port can connect. No restrictions. High security risk. + +**Server on 127.0.0.1 (localhost only):** + +- **mTLS**: Only local clients with valid certificates can connect. +- **WNUA**: Only same Windows user on same machine can connect. +- **Insecure**: Only local clients can connect. Relatively safe for development. + +**Server on specific IP:** + +- **mTLS**: Clients that can route to that IP with valid certificates can connect. +- **WNUA**: Same Windows user from machines that can route to that IP can connect. +- **Insecure**: Anyone who can route to that IP can connect. Security risk on shared networks. + +**Summary table:** + +.. list-table:: + :header-rows: 1 + :widths: 20 30 25 25 + + * - Transport Mode + - Authentication + - Encryption + - Connectivity Scope + * - mTLS + - Certificate-based + - Yes (TLS) + - Any network with valid certs + * - WNUA + - Windows user identity + - No + - Same Windows user only + * - Insecure + - None + - No + - Unrestricted (dangerous) + +See the table below for version-specific support and service pack requirements. .. _grpc_security_version_requirements: @@ -47,25 +151,33 @@ Version and service pack requirements Ansys installation directory. .. warning:: - **Breaking Change**: Version mismatch behavior + **Breaking Change for Linux Users** + + If you have the **latest PyMechanical** with an **older version of Mechanical** + that doesn't support secure connections (pre-2024 R2 or without required service pack): - When using ``launch_mechanical()`` without explicitly specifying ``transport_mode``: + - **Linux**: The default transport mode is ``mtls``. Calling ``launch_mechanical()`` + without explicitly specifying ``transport_mode="insecure"`` **will fail** because + old Mechanical versions don't support mtls. - - If you have a **newer version of PyMechanical** with an **older version of Mechanical** - that doesn't support secure connections, the connection will fail. - - If you have an **older version of PyMechanical** with a **newer version of Mechanical** - that requires secure connections by default, the connection will fail. + - **Windows**: The default transport mode is ``wnua``. A warning is issued and the + connection **automatically falls back to insecure mode**. The connection succeeds, + but you should explicitly specify ``transport_mode="insecure"`` to avoid the warning. - **Solution**: Always explicitly specify ``transport_mode`` to avoid compatibility issues: + **Required Action for Linux**: Always explicitly specify ``transport_mode="insecure"`` + when using old Mechanical versions: .. code-block:: python - # For older Mechanical versions (241 or versions without required SP) + # Required for Mechanical 241 or versions without required SP mechanical = launch_mechanical(transport_mode="insecure") - # For newer Mechanical versions with secure support - mechanical = launch_mechanical(transport_mode="wnua") # Windows - mechanical = launch_mechanical(transport_mode="mtls") # Linux +.. note:: + **Forward Compatibility** + + If you have an **older version of PyMechanical** with a **newer version of Mechanical** + that supports secure connections by default, you may need to update PyMechanical to + take advantage of secure transport modes. Transport modes --------------- @@ -96,8 +208,8 @@ See `PyAnsys mTLS guide tuple[int, str]: Optional path to the Ansys installation directory. eg: "usr/ansys_inc/v251/" - Example - ------- + Examples + -------- Get the version and location of the installation directory. >>> find-mechanical -r 251 diff --git a/src/ansys/mechanical/core/embedding/logger/__init__.py b/src/ansys/mechanical/core/embedding/logger/__init__.py index 3622edb727..d96f5eedba 100644 --- a/src/ansys/mechanical/core/embedding/logger/__init__.py +++ b/src/ansys/mechanical/core/embedding/logger/__init__.py @@ -24,9 +24,8 @@ Module to interact with the built-in logging system of Mechanical. -Usage ------ - +Examples +-------- Configuring logger ~~~~~~~~~~~~~~~~~~ @@ -34,11 +33,12 @@ :class:`Configuration ` class: .. code:: python - import ansys.mechanical.core as mech - from ansys.mechanical.core.embedding.logger import Configuration, Logger - Configuration.configure(level=logging.INFO, to_stdout=True, base_directory=None) - app = mech.App(version=252) + import ansys.mechanical.core as mech + from ansys.mechanical.core.embedding.logger import Configuration, Logger + + Configuration.configure(level=logging.INFO, to_stdout=True, base_directory=None) + app = mech.App(version=252) Then, the :class:`Logger ` class can be used to write messages to the log: diff --git a/src/ansys/mechanical/core/embedding/transaction.py b/src/ansys/mechanical/core/embedding/transaction.py index a855d39065..773d407d55 100644 --- a/src/ansys/mechanical/core/embedding/transaction.py +++ b/src/ansys/mechanical/core/embedding/transaction.py @@ -26,8 +26,8 @@ class Transaction: # When ansys-pythonnet issue #14 is fixed, this class will b """ A class to speed up bulk user interactions using Ansys ACT Mechanical Transaction. - Example - ------- + Examples + -------- >>> with Transaction() as transaction: ... pass # Perform bulk user interactions here """ diff --git a/src/ansys/mechanical/core/ide_config.py b/src/ansys/mechanical/core/ide_config.py index 70a2ae3ea7..98b6c2c1d8 100644 --- a/src/ansys/mechanical/core/ide_config.py +++ b/src/ansys/mechanical/core/ide_config.py @@ -270,8 +270,8 @@ def cli(ide: str, target: str, revision: int) -> None: The Mechanical revision number. For example, "261". If unspecified, it finds the default Mechanical version from ansys-tools-path. - Usage - ----- + Examples + -------- The following example demonstrates the main use of this tool: $ ansys-mechanical-ideconfig --ide vscode --target user --revision 261 diff --git a/src/ansys/mechanical/core/launcher.py b/src/ansys/mechanical/core/launcher.py index 15963a0a07..d2070bc3d4 100644 --- a/src/ansys/mechanical/core/launcher.py +++ b/src/ansys/mechanical/core/launcher.py @@ -37,6 +37,7 @@ has_grpc_service_pack, is_linux, resolve_certs_dir, + verify_server_certificates, ) @@ -101,8 +102,12 @@ def __init__( else: transport_mode = "wnua" self.transport_mode = transport_mode + # Resolve certs_dir using environment variable if needed for mTLS self.certs_dir = resolve_certs_dir(transport_mode, certs_dir) + if transport_mode.lower() == "mtls": + # Check that certs exist if needed for mTLS + verify_server_certificates(self.certs_dir) self.__ui_arg_list = ["-DSApplet", "-nosplash", "-notabctrl"] self.__batch_arg_list = ["-DSApplet", "-b"] @@ -210,10 +215,11 @@ def __get_commandline_args(self): # Validate transport mode requirements if not supports_grpc and self.transport_mode.lower() != "insecure": - raise Exception( + LOG.warning( f"Mechanical version {version} does not support secure transport modes. " - f"{get_service_pack_message(version)}" - f" Please refer to the documentation for more details." + f"{get_service_pack_message(version)} " + f"Using INSECURE transport mode instead. " + f"Please refer to the documentation for more details. " f"https://mechanical.docs.pyansys.com/version/stable/user_guide/remote_session/grpc_security.html" ) diff --git a/src/ansys/mechanical/core/mechanical.py b/src/ansys/mechanical/core/mechanical.py index c3389102ea..ef86ca4c00 100644 --- a/src/ansys/mechanical/core/mechanical.py +++ b/src/ansys/mechanical/core/mechanical.py @@ -265,9 +265,8 @@ def get_mechanical_path(allow_input=True): def check_valid_mechanical(): """Change to see if the default Mechanical path is valid. - Example (windows) - ----------------- - + Examples + -------- >>> from ansys.mechanical.core import mechanical >>> from ansys.tools.common.path import change_default_mechanical_path >>> mechanical_path = "C:/Program Files/ANSYS Inc/v261/aisol/bin/win64/AnsysWBU.exe" diff --git a/src/ansys/mechanical/core/misc.py b/src/ansys/mechanical/core/misc.py index c5b7119115..d7b9b318cd 100644 --- a/src/ansys/mechanical/core/misc.py +++ b/src/ansys/mechanical/core/misc.py @@ -42,6 +42,16 @@ def is_windows(): return False +def is_linux() -> bool: + """Check if the host machine is Linux. + + Returns + ------- + ``True`` if the host machine is Linux, ``False`` otherwise. + """ + return os.name == "posix" + + def get_mechanical_bin(release_version): """Get the path for the Mechanical executable file based on the release version. @@ -303,16 +313,6 @@ def get_service_pack_message(version): return "Update to Ansys 2024 R2 or later for secure gRPC support." -def is_linux() -> bool: - """Check if the host machine is Linux. - - Returns - ------- - ``True`` if the host machine is Linux, ``False`` otherwise. - """ - return os.name == "posix" - - def resolve_certs_dir(transport_mode, certs_dir=None): """Resolve the certificate directory for mTLS connections. @@ -355,3 +355,31 @@ def resolve_certs_dir(transport_mode, certs_dir=None): # On Linux, read at any level return os.environ.get("ANSYS_GRPC_CERTIFICATES", "certs") return certs_dir if certs_dir is not None else "certs" + + +def verify_server_certificates(certs_dir: str) -> None: + """Check if the required server certificate files exist in the specified directory. + + Parameters + ---------- + certs_dir : str + Directory path where the certificate files are expected. + + Raises + ------ + FileNotFoundError + If any of the required certificate files are missing. + """ + required_files_server = ["server.cert", "server.key", "ca.cert"] + missing_files = [] + for filename in required_files_server: + file_path = Path(certs_dir) / filename + if not file_path.is_file(): + missing_files.append(filename) + + if missing_files: + missing_str = ", ".join(missing_files) + raise FileNotFoundError( + f"Could not launch Ansys Mechanical in mtls transport mode. " + f"The following certificate files are missing in '{certs_dir}': {missing_str}" + ) diff --git a/src/ansys/mechanical/core/run.py b/src/ansys/mechanical/core/run.py index c662dd4a9c..c36416659a 100644 --- a/src/ansys/mechanical/core/run.py +++ b/src/ansys/mechanical/core/run.py @@ -478,8 +478,8 @@ def cli( ): """CLI tool to run mechanical. - USAGE: - + Examples + -------- The following example demonstrates the main use of this tool: $ ansys-mechanical -r 261 -g