Security#
PyPIM involves gRPC connections at two different points, and each can be secured independently:
The connection from the PyPIM client to the PIM server itself, used for
list_definitions(),create_instance(), and the other PIM API calls.The connection from the PyPIM client to a product instance started by the PIM server. The transport used for this connection is decided by the PIM server and communicated back to the client through the created
Instance.
Both connections support the same set of transports:
insecure: no transport security. This is the default.tls: TLS encryption. Used only for the client-to-PIM-server connection, authenticated with a bearer token rather than a client certificate.mtls: mutual TLS, authenticated with a client certificate, a private key, and a certificate authority (CA) file.uds: Unix Domain Socket. Only available on platforms and gRPC versions that support it.wnua: Windows Named User Authentication. Windows only, and only for local connections.
Securing the client-to-PIM-server connection#
This is the connection created by connect(). It can be configured
either through a configuration file or programmatically.
Configuration file#
The configuration file referenced by the
ANSYS_PLATFORM_INSTANCEMANAGEMENT_CONFIG environment variable has two
supported formats.
Version 1 (tls flag)#
The original format supports only insecure and tls:
{
"version": 1,
"pim": {
"uri": "dns:pim.svc.com:80",
"headers": {
"authorization": "Bearer <token>"
},
"tls": true
}
}
When tls is true, the authorization header must contain a
Bearer token, which is used to build the secure channel credentials. This
format continues to work unchanged; it is never removed by adding version 2
support.
Version 2 (security block)#
The version 2 format replaces the tls flag with a security block
that can select any of the five transports:
{
"version": 2,
"pim": {
"uri": "dns:pim.svc.com:80",
"headers": {
"metadata-info": "value"
},
"security": {
"transport": "mtls",
"certificate_files": {
"cert_file": "client.crt",
"key_file": "client.key",
"ca_file": "ca.crt"
}
}
}
}
The security.transport value must be one of insecure, tls,
uds, mtls, or wnua.
For
tls, theauthorizationheader must still contain aBearertoken, exactly as in version 1.For
mtls, provide client certificates either as individual files withcertificate_files(cert_file,key_file, andca_file, all required together) or as acertificates_directorypath. Provide at most one of the two; providing neither is valid and lets the underlying transport layer resolve its own defaults.For
uds, the socket path is taken directly from aunix:uri. Because the PIM server is expected to already be listening on that socket, the socket path is checked for existence when the configuration is loaded.For
wnuaandinsecure, no additionalsecurityfields are needed.
Any invalid configuration (an unknown transport, conflicting mTLS certificate
options, a missing UDS socket, or a missing bearer token for tls) raises
InvalidConfigurationError as soon as the configuration is loaded,
rather than failing later on the first request.
Programmatic configuration#
When no configuration file is present, connect() accepts the
connection settings directly:
import ansys.platform.instancemanagement as pypim
from ansys.platform.instancemanagement import ConnectionSecurity
from ansys.tools.common.cyberchannel import CertificateFiles
client = pypim.connect(
uri="dns:pim.svc.com:80",
headers={"identity": "james"},
security=ConnectionSecurity(
transport="mtls",
cert_files=CertificateFiles(
cert_file="client.crt", key_file="client.key", ca_file="ca.crt"
),
),
)
ConnectionSecurity mirrors the version 2 configuration file’s
security options. Its transport is one of insecure, tls, uds,
mtls, or wnua. For mtls, provide client certificates as either
cert_files (a CertificateFiles) or certs_dir (a directory path),
but not both; providing neither is valid and lets the underlying transport
layer resolve its own defaults. An invalid combination raises ValueError
at construction time.
Precedence between the configuration file and these parameters is
file-exclusive and all-or-nothing: if the environment is configured with
a configuration file (is_configured() is True), that file is used
in full and the uri, headers, and security parameters are
ignored entirely. The parameters are used only when there is no
configuration file. If neither a file nor a uri parameter is available,
connect() raises NotConfiguredError.
Securing a product instance at creation#
Client.create_instance() accepts an optional security_settings
parameter describing which transport the PIM server should use to expose the
created instance:
import ansys.platform.instancemanagement as pypim
from ansys.platform.instancemanagement import MtlsSettings
client = pypim.connect()
instance = client.create_instance(
product_name="mapdl",
security_settings=MtlsSettings(certificates_directory="/path/to/certs"),
)
security_settings accepts one of:
InsecureSettings: no transport security (the default whensecurity_settingsis not provided; the actual choice is up to the PIM server).MtlsSettings: mutual TLS, with certificates provided either as acertificates_directoryor as individualcertificate_paths(MtlsCertificatePaths).UdsSettings: Unix Domain Socket, with the socket location provided either as a fullsocket_pathor as asocket_directory/socket_identifierpair.WnuaSettings: Windows Named User Authentication. Windows only.
For MtlsSettings and UdsSettings, supplying a conflicting
combination of options (for example both certificate sources, or a
socket_path together with a socket_directory) raises ValueError
at construction time.
These settings are a request: the PIM server ultimately decides what transport the instance is actually exposed with, and communicates that choice back through the instance’s services (see the next section).
Reading security settings from a service#
Once an instance is ready, each entry in Instance.services is a
Service describing how to reach that particular endpoint. When the
server reports transport security for a service, Service.transport
exposes the resolved transport name (one of insecure, uds, mtls,
or wnua), or None when the server did not report any security
information for that service:
instance.wait_for_ready()
service = instance.services["grpc"]
print(service.transport) # e.g. "mtls"
You do not need to build the gRPC channel yourself based on this
information: Instance.build_grpc_channel() reads the resolved
transport (and, for mtls, the client certificate files) and builds a
correctly secured channel automatically.