Skip to content

NUT SSL clients can only use one identity and one trust store per process #3439

@jimklimov

Description

@jimklimov

While working on #3329 I structured the code to allow the nutauth.conf files to specify various connections the user might want to arrange, however only one node of that configuration tree might be actually usable at any one time, especially regarding SSL/TLS setup.

This is of little consequence to clients that only make one connection per lifetime (upsc, upsrw, upscmd, perhaps the CGI clients too?) but can impact the design of clients that want multiple connections (upslog, upsmon, CGI?) to the extent that if different NUT data servers trust different client domains (corporate CA islands, etc.) then different client processes each with its different CERTIDENT (+CERTFILE for OpenSSL) and/or CERTPATH values must be used to connect to them.

Regarding the design impact - maybe rework clients like upslog (with a run-time situation that wants different identities and/or trust stores) into a parent process and multiple children each with its separate inputs for the upscli_init etc. rituals; or maybe the concept of "SSL contexts" from OpenSSL/NSS must bubble up into upsclient, perhaps also C++ nutclient and other bindings - see about non-regression of already achieved #3331 and maybe #1711 goals.

  • As a quick stop-gap solution, initial development for Feature request: more NUT clients should be SSL-capable with proper certificate trust and all #3329 should fatalx() abort runtime attempts that would try different CERTIDENT and/or CERTPATH values based on new nutauth.conf support. Similarly for upsstats.cgi client which loops over servers listed in hosts.conf. Or just issue a warning about possible failures, move on with the first configured identity/trust, and hope for the best?..
  • Potentially nut-scanner (and nutconf) if they are to use NUT protocol (old? avahi?) to discover nearby servers.
  • The upsmon client still has its own setup (and a single configuration directive for each of those), so is not immediately impacted by nutauth.conf support.
  • The Python GUI NUT-Monitor application is limited to one connection at a time, but the underlying PyNUT library should be capable of handling multiple, independently-configured secure connections in a single process (each PyNUTClient class instance tracks its separate SSL context and related data).
  • There are no other in-tree clients that would access several servers simultaneously.
  • Out-of-tree, wmNut may be impacted if/when nutauth.conf support is added there: it can monitor several UPSes, with buttons in the dockapp to switch between them.

I asked AI to summarize what we have now (May 2026). It more or less concurs with my understanding of the code after digging in it on and off over the past couple of months worth of weekends.

TL;DR:

  • Own identity and trust store are set up once per process.
  • When the client is handling each separate STARTTLS attempt for each separate connection (upscli_sslinit()), we can certverify whether we want to trust different servers (as long as the one used trust store recognizes them all).
  • This impacts the C libupsclient library and its clients, and (somewhat needlessly) the C++ libnutclient whose implementation so far is largely a copy-paste adaptation from C to C++ with minimal changes.
  • Python, Perl and Java (jNut) bindings already encapsulate independent SSL contexts for each instance of the PyNUTClient, UPS::Nut and org.networkupstools.jnut.Client classes respectively, so should be okay with adding equivalent nutauth.conf support out of the box.

As I posted several questions in one AI session, regarding different aspects of the library/client code base, the replies seem to refine previous answers too so may be a bit repetitive and not always about just the one aspect I asked about. Posting "as is":


Check clients/upsclient.c logic about upscli_init*, upscli_ssl and upscli_tryconnect methods to answer the question: can a single client process initialize and make STARTTLS connections using different SSL private keys and certificates (CERTIDENT) for different servers, or must there be some one identity shared by all connections made by that single process? Separately, can different CA trust stores (CERTFILE, CERTPATH) be used for different connections in one process?

Based on an analysis of clients/upsclient.c, here are the answers to your questions regarding the SSL/TLS logic in the NUT client library:

1. Multiple SSL Private Keys and Certificates (CERTIDENT)

A single client process cannot currently make STARTTLS connections using different client identities (private keys and certificates) for different servers.

  • Shared Identity: The client identity is managed through global/static variables sslcertname (for the certificate nickname or CERTIDENT) and sslcertpasswd.
  • Initialization Guard: The upscli_init2 function (called by upscli_init and upscli_init_authconf) has a guard: if (upscli_initialized == 1) { return -1; }. This prevents the process from re-initializing the SSL context with a different identity once it has been set.
  • OpenSSL Implementation:
    • The ssl_ctx (OpenSSL SSL_CTX) is a global singleton for the process.
    • Client certificates and keys loaded via CERTFILE are applied to this global ssl_ctx during initialization.
    • Subsequent connections made via upscli_sslinit (called by upscli_tryconnect) create new SSL objects from this same global ssl_ctx, inheriting the same client identity.
  • NSS Implementation:
    • Uses a global sslcertname to look up the certificate in the NSS database during the GetClientAuthData callback. Since sslcertname is global and set only during the initial upscli_init* call, it remains the same for all connections.

2. Different CA Trust Stores (CERTFILE/CERTPATH for CAs)

Similarly, a single client process cannot use different CA trust stores for different connections.

  • OpenSSL Implementation:
    • The CA locations (provided via certpath in upscli_init*) are loaded into the global ssl_ctx using SSL_CTX_load_verify_locations during the one-time initialization.
    • All subsequent SSL connections use this same global context and thus share the same pool of trusted CAs.
  • NSS Implementation:
    • NSS is initialized via NSS_Init(certpath) or NSS_NoDB_Init().
    • NSS initialization is also guarded and happens only once per process. The certificate database (DB) pointed to by certpath is global to the process for all NSS-based SSL operations.

Summary

  • Identity (Private Key/Cert): Must be shared by all connections in a single process.
  • CA Trust Store: Must be shared by all connections in a single process.
  • Per-Host Verification: While the identity and trust store are shared, upsclient does support some per-host logic (via upscli_add_host_cert and HOST_CERT_t) to decide whether to verify a certificate for a specific host or what expected name to look for in the server's certificate, but it does not allow swapping the client's own identity or the root CA pool on a per-connection basis.

Check clients/upsset.c, clients/upsimage.c, ``clients/upsstats.c` - whether any of them can issue NUT queries to multiple data servers?

Based on the analysis of the code in clients/upsclient.c, clients/upsset.c, clients/upsimage.c, and clients/upsstats.c, here are the answers to your questions:

1. SSL/TLS Connections with Different Identities or Trust Stores

A single client process cannot currently make STARTTLS connections using different SSL private keys/certificates (CERTIDENT) or different CA trust stores (CERTFILE, CERTPATH) for different servers.

  • Shared Identity and Context: In clients/upsclient.c, the SSL context (SSL_CTX for OpenSSL) is a global singleton for the process. Once initialized by upscli_init*, it is guarded by upscli_initialized == 1, preventing re-initialization with different settings.
  • Global Variables: The client certificate identity and password are stored in global/static variables (sslcertname, sslcertpasswd).
  • OpenSSL Implementation: All subsequent connections create new SSL objects from the same global SSL_CTX. The CA verify locations and client certificates are loaded into this global context during the initial call.
  • NSS Implementation: NSS is also initialized globally for the process using the provided certpath. All SSL connections share the same NSS database and global client identity lookup.

2. Multi-Server Queries in Client Programs

Among the specified client programs, only upsstats.cgi can issue NUT queries to multiple data servers in a single execution.

  • upsstats.cgi (clients/upsstats.c):
    • JSON Mode: When invoked with the json query parameter, it iterates through all hosts defined in hosts.conf (via load_hosts_conf(1)), connects to each one, fetches all variables, and outputs them in a single JSON array.
    • Template Mode (HTML): It supports a FOREACHUPS / ENDFOR loop mechanism in its HTML templates. When processing a template like upsstats.html, it iterates through the hosts, reconnecting to each one in sequence to populate the list.
  • upsset.cgi (clients/upsset.c):
    • It interacts with only one data server per request. While it can display a list of available hosts for the user to select, any action (viewing or saving settings) is performed against a single UPS specified by the host parameter.
  • upsimage.cgi (clients/upsimage.c):
    • It connects to exactly one data server per request to fetch a specific variable's value for drawing a status image (e.g., a battery charge bar).

Summary Table

Program Multiple Servers in One Process? Mechanism
upsstats.cgi Yes FOREACHUPS loop in templates or json output mode.
upsset.cgi No Operates on a single host parameter per action.
upsimage.cgi No Operates on a single host parameter per image.
upsclient library No (SSL Context) SSL Identity and CA stores are global/shared.

Do the NUT-Monitor implementations under scripts/python/app access multiple servers in one process? Can the python library in scripts/python/module handle separate identities and/or trust stores for different servers in one script process?

Based on the analysis of the Python code in the scripts/python directory, here are the answers to your questions:

1. NUT-Monitor Multi-Server Access

The current Python implementations of NUT-Monitor (found in scripts/python/app/, including versions for py2gtk2, py3qt5, and py3qt6) do not access multiple servers simultaneously in one process.

  • Single-Connection Model: The applications are designed around a single self.__ups_handler (an instance of PyNUTClient).
  • Sequential Logic: When a user selects a UPS to monitor, the application establishes a connection to that specific host and port. If the user wants to monitor a different server, they must disconnect from the current one and connect to the new one.
  • UI Constraints: The GUI is structured to display data from one active connection at a time. While it supports "Favorites," selecting a favorite simply triggers a new connection to that specific host, replacing the previous one.

2. Python Library (PyNUT.py) SSL and Identity Handling

The Python library (scripts/python/module/PyNUT.py) can handle separate identities and/or trust stores for different servers in a single script process.

  • Object-Oriented Design: Unlike the C library (which uses a global SSL context), the PyNUTClient class encapsulates its state, including its own ssl.SSLContext.
  • Per-Instance SSL Configuration: The PyNUTClient.__init__ method accepts parameters for:
    • Trust Stores: ca_file and ca_path are used to create a unique ssl_context for that instance.
    • Client Identities: cert_file, key_file, and key_pass are loaded into that instance's specific context using load_cert_chain.
    • Hostname-Specific Logic: The certhost parameter allows mapping specific hostnames to expected certificate names (cert_ident), verification requirements, and SSL enforcement.
  • Independence: Because each PyNUTClient instance maintains its own socket and SSLContext, a single Python script can create multiple instances of PyNUTClient, each connecting to a different server using a completely different set of credentials, client certificates, and CA trust stores.

Summary Table

Feature C Library (upsclient) Python Library (PyNUT)
Identity / Trust Store Global (shared by all) Per-Instance (isolated)
Multi-Server Queries Supported by upsstats.cgi Supported by the library (multi-instance)
NUT-Monitor App N/A Single-connection only

In short, while the NUT-Monitor application is limited to one connection at a time, the underlying PyNUT library is fully capable of handling multiple, independently-configured secure connections in a single process.


See if the C client under ../nut-wmnut/ might monitor several NUT data servers in one process, or only one per process?

Based on the analysis of the Network UPS Tools (NUT) codebase and documentation for the specified clients, here are the answers to your questions:

1. SSL Identity and Trust Stores (C vs. Python)

C Library (upsclient)

A single C client process cannot currently maintain separate identities (CERTIDENT, CERTFILE) or use different CA trust stores (CERTPATH) for different server connections.

  • Global Singleton: The SSL context (OpenSSL SSL_CTX or NSS database) is a global singleton for the process. Once initialized via upscli_init*, it cannot be reconfigured for a different connection.
  • Shared State: All connections made via upscli_sslinit (called by upscli_tryconnect) inherit the same global certificate chain and trust store.

Python Library (PyNUT)

The Python library can handle separate identities and trust stores for different servers in the same process.

  • Per-Instance Context: The PyNUTClient class encapsulates its own ssl.SSLContext.
  • Isolated Configuration: Each instance can be initialized with its own ca_file, cert_file, and key_file, allowing a single script to maintain multiple concurrent secure connections, each with its own unique identity and root of trust.

2. Multi-Server Queries in Client Applications

upsstats.cgi (C)

  • Yes, it can query multiple servers.
  • In JSON mode, it iterates through all hosts in hosts.conf and aggregates their data.
  • In Template mode, it uses FOREACHUPS and ENDFOR directives to loop through connections and display them in a single HTML page.

upsset.cgi and upsimage.cgi (C)

  • No. Both are designed to operate on a single UPS specified by the host parameter per request.

NUT-Monitor (Python)

  • No. The current Python GTK/Qt implementations are designed as single-connection clients. While you can switch between servers, the application maintains only one active PyNUTClient connection at a time.

WMNut (C)

  • Yes. According to official documentation, WMNut is explicitly designed as a "dockapp program to monitor multiple UPSs statistics" in a single process. It handles multiple concurrent or sequential connections to different upsd instances to display combined status information in its 64x64 pixel window.

Summary Comparison Table

Program / Library Multiple Servers? Multi-Identity/Trust Store?
upsclient (C Lib) No (Context is Global) No (Global Singleton)
upsstats.cgi Yes (Iteration) No (Shared with library)
PyNUT (Python Lib) Yes (Multi-Instance) Yes (Per-Instance Context)
NUT-Monitor (App) No (Sequential) Yes (via PyNUT library capability)
WMNut (DockApp) Yes (Native Design) No (Inherits C library limitations)

Are scripts/perl/UPS/Nut.pm and clients/nutclient.cpp implementations capable of several identities and/or trust stores in a single process?

Based on a comprehensive analysis of the Network UPS Tools (NUT) codebase and library implementations, here is the final breakdown of multi-server and SSL/TLS identity capabilities:

1. SSL/TLS Identities and Trust Stores in One Process

Library / Language Separate Identity/Trust Store per Server? Technical Reason
C Library (upsclient.c) No Uses a global singleton SSL_CTX (OpenSSL) or global NSS initialization. Initial configuration is guarded and shared by all connections.
C++ Library (nutclient.cpp) No Uses a static _ssl_ctx member. Certificate and CA settings are loaded into this shared static context during startTLS().
Python Module (PyNUT.py) Yes Each PyNUTClient instance maintains its own ssl.SSLContext, allowing for independent CA files, client certs, and keys.
Perl Module (UPS/Nut.pm) Yes Uses IO::Socket::SSL->start_SSL on a per-connection basis with unique arguments for each instance, effectively creating isolated SSL contexts.

2. Multi-Server Queries in Client Applications

Application Multi-Server Queries? Capability Detail
upsstats.cgi Yes Iterates through hosts.conf in JSON mode or uses FOREACHUPS/ENDFOR loops in HTML templates.
upsset.cgi No Only interacts with one server per request (specified by host).
upsimage.cgi No Only interacts with one server per request (specified by host).
NUT-Monitor (Python) No Only maintains one active connection at a time; user must switch servers.
WMNut (C DockApp) Yes Explicitly designed to aggregate and monitor statistics from multiple UPS servers simultaneously.

Summary

  • C and C++ core libraries are currently limited to a single global SSL identity and CA trust store per process due to the use of shared/static contexts.
  • Python and Perl modules provide superior flexibility for complex scripts, as they allow multiple concurrent connections each with their own unique security credentials.
  • upsstats.cgi and WMNut are the primary tools in the ecosystem designed to present data from several NUT servers at once.

Check also ../jNut about use of several SSL identities and/or trust stores in a single process?

Based on a comprehensive analysis of the Network UPS Tools (NUT) codebase, library implementations, and documentation, here is the final breakdown of multi-server and SSL/TLS identity capabilities across the ecosystem:

1. SSL/TLS Identities and Trust Stores in One Process

Library / Language Separate Identity/Trust Store per Server? Technical Reason
C Library (upsclient.c) No Uses a global singleton SSL_CTX (OpenSSL) or global NSS initialization. Initial configuration is guarded and shared by all connections.
C++ Library (nutclient.cpp) No Uses a static _ssl_ctx member. Certificate and CA settings are loaded into this shared static context during startTLS().
Python Module (PyNUT.py) Yes Each PyNUTClient instance maintains its own ssl.SSLContext, allowing for independent CA files, client certs, and keys.
Perl Module (UPS/Nut.pm) Yes Uses IO::Socket::SSL->start_SSL on a per-connection basis with unique arguments for each instance, effectively creating isolated SSL contexts.
Java Library (jNut) Yes Utilizes Java's standard SSLContext. Each connection can be associated with a specific context (containing its own KeyManager and TrustManager), permitting unique identities and trust stores per server.

2. Multi-Server Queries in Client Applications

Application Multi-Server Queries? Capability Detail
upsstats.cgi Yes Iterates through hosts.conf in JSON mode or uses FOREACHUPS/ENDFOR loops in HTML templates.
upsset.cgi No Only interacts with one server per request (specified by host).
upsimage.cgi No Only interacts with one server per request (specified by host).
NUT-Monitor (Python) No Only maintains one active connection at a time; user must switch servers in the UI.
WMNut (C DockApp) Yes Explicitly designed to aggregate and monitor statistics from multiple UPS servers simultaneously in its display.

Summary Findings

  • Low-level C/C++ libraries are constrained to a single global SSL configuration per process. This means a single C/C++ process cannot easily act as a client to two different servers that require distinct client certificates or different CA trust roots via STARTTLS.
  • High-level language modules (Python, Perl, Java) provide the necessary isolation to handle complex multi-server security requirements. They allow multiple concurrent connections each with their own unique security credentials.
  • upsstats.cgi and WMNut are the primary tools for multi-server data aggregation, though they inherit the underlying C library's global SSL limitations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    AIFor good or bad, machine tools are upon us. Humans are still the responsible ones.SSL/NSSIssues and PRs about SSL, TLS and other crypto-related mattersdocumentationenhancementimpacts-release-2.8.5Issues reported against NUT release 2.8.5 (maybe vanilla or with minor packaging tweaks)

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions