Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1469.maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update gPRC documentation
5 changes: 4 additions & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/"),
],
Expand Down
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,6 @@ If you are not sure which mode to pick, see :doc:`getting_started/choose_your_mo
API reference <api/ansys/mechanical/core/index>
contribute
faq
contribute
kil/index
changelog
146 changes: 129 additions & 17 deletions doc/source/user_guide/remote_session/grpc_security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Check failure on line 19 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'untrusted'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'untrusted'?", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 19, "column": 162}}}, "severity": "ERROR"}

*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.

Check failure on line 51 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'plaintext'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'plaintext'?", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 51, "column": 48}}}, "severity": "ERROR"}

*Security risk:* Suitable only for local development on trusted machines. Never use in production or on untrusted networks.

Check failure on line 53 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'untrusted'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'untrusted'?", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 53, "column": 105}}}, "severity": "ERROR"}

.. _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.

Check failure on line 62 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'loopback'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'loopback'?", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 62, "column": 46}}}, "severity": "ERROR"}

- **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.

Check failure on line 66 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Google.Latin] Use 'for example' instead of 'e.g.'. Raw Output: {"message": "[Google.Latin] Use 'for example' instead of 'e.g.'.", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 66, "column": 20}}}, "severity": "ERROR"}

.. _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:

Expand Down Expand Up @@ -47,25 +151,33 @@
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

Check warning on line 160 in doc/source/user_guide/remote_session/grpc_security.rst

View workflow job for this annotation

GitHub Actions / Documentation Style Check

[vale] reported by reviewdog 🐶 [Google.Will] Avoid using 'will'. Raw Output: {"message": "[Google.Will] Avoid using 'will'.", "location": {"path": "doc/source/user_guide/remote_session/grpc_security.rst", "range": {"start": {"line": 160, "column": 68}}}, "severity": "WARNING"}
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
---------------
Expand Down Expand Up @@ -96,8 +208,8 @@
- **Windows**: Set as a user-level environment variable only. System-level variables are ignored.
- **Linux**: Can be set at any level (user or system).

When this variable is set and ``certs_dir`` is not explicitly specified, PyMechanical will
use the path from this environment variable.
When this variable is set and ``certs_dir`` is not explicitly specified, PyMechanical
uses the path from this environment variable.

Example (Windows PowerShell):

Expand Down
2 changes: 1 addition & 1 deletion src/ansys/mechanical/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

from ansys.mechanical.core.logging import Logger

LOG = Logger(level=logging.ERROR, to_file=False, to_stdout=True)
LOG = Logger(level=logging.WARN, to_file=False, to_stdout=True)
"""Create logger for package level use."""


Expand Down
4 changes: 2 additions & 2 deletions src/ansys/mechanical/core/embedding/find_mechanical.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def cli(version: int, path: str | None = None) -> 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
Expand Down
14 changes: 7 additions & 7 deletions src/ansys/mechanical/core/embedding/logger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@

Module to interact with the built-in logging system of Mechanical.

Usage
-----

Examples
--------
Configuring logger
~~~~~~~~~~~~~~~~~~

Configuring the logger can be done using the
:class:`Configuration <ansys.mechanical.core.embedding.logger.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 <ansys.mechanical.core.embedding.logger.Logger>` class can
be used to write messages to the log:
Expand Down
4 changes: 2 additions & 2 deletions src/ansys/mechanical/core/embedding/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand Down
4 changes: 2 additions & 2 deletions src/ansys/mechanical/core/ide_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 9 additions & 3 deletions src/ansys/mechanical/core/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
has_grpc_service_pack,
is_linux,
resolve_certs_dir,
verify_server_certificates,
)


Expand Down Expand Up @@ -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"]

Expand Down Expand Up @@ -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"
)

Expand Down
5 changes: 2 additions & 3 deletions src/ansys/mechanical/core/mechanical.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
48 changes: 38 additions & 10 deletions src/ansys/mechanical/core/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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}"
)
Loading
Loading