Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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 @@ -118,6 +118,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 @@ -230,7 +233,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 / style / 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 / style / 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 / style / 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 / style / 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 / style / 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 / style / 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 fails.
- If you have an **older version of PyMechanical** with a **newer version of Mechanical**
that requires secure connections by default, the connection fails.
- **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 uses
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/winx64/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