Context
ros2_medkit_opcua (added in #365) currently connects to OPC-UA servers with Anonymous authentication and SecurityPolicy=None. The plugin logs a warning on startup making this explicit:
OPC-UA security: Anonymous auth, SecurityPolicy=None. Not suitable for untrusted networks.
This is acceptable for isolated LANs, demo environments, and CI testing, but it is not suitable for any deployment where the network between the gateway and the PLC is not fully trusted - which is the normal case in industrial deployments where OPC-UA is deployed alongside safety-critical control traffic.
This issue tracks adding support for certificate-based authentication using Basic256Sha256 (the most widely supported secure OPC-UA profile in industrial PLC servers like Siemens S7, Beckhoff TwinCAT, and Allen-Bradley).
Scope
Code changes
-
Extend OpcuaClientConfig (include/ros2_medkit_opcua/opcua_client.hpp) with new fields:
std::string security_policy - one of None, Basic128Rsa15, Basic256, Basic256Sha256 (default None for backward compat)
std::string security_mode - one of None, Sign, SignAndEncrypt (default None)
std::string client_cert_path - path to DER or PEM client certificate
std::string client_key_path - path to private key
std::vector<std::string> trusted_server_certs - optional list of trusted server certificates (or directory)
-
Wire these through to open62541pp::ClientConfig in OpcuaClient::connect(). The relevant open62541pp APIs are setSecurityPolicy, setCertificate, setPrivateKey, and the trust list configuration.
-
Handle certificate loading errors gracefully with clear log messages (missing file, wrong format, expired cert, etc.).
-
Extend the YAML node map schema with a new top-level security: section so configuration is not hardcoded in the plugin parameters.
-
Environment variable overrides following the existing pattern (OPCUA_CLIENT_CERT, OPCUA_CLIENT_KEY, etc.) for Docker deployment flexibility.
Documentation
- Update
design/index.rst - replace the Security section with real documentation of the supported profiles and the trust model.
- Update
README.md with an OPC-UA Security section showing a complete example of running against a PLC with cert auth enabled (certificate generation, trust list, config snippet).
- Remove the
Security limitations subsection from README once covered, or convert to a Known limitations note listing still-missing features (e.g., username/password, CA chain validation).
Tests
- Unit test: parameter loading from YAML into
OpcuaClientConfig with all security fields (failure cases for missing cert file, bad profile name).
- Unit test:
OpcuaClient::connect with a mock OPC-UA server that requires cert auth (requires a fixture server with known cert).
- Integration test: new CI job or extension of
opcua-plugin.yml that runs OpenPLC configured with cert auth and verifies the plugin connects successfully.
The integration test requires generating a test CA and client certificate during CI setup. This is the main complexity driver of this issue - OpenPLC needs to be configured with a proper trust list, and the test scripts need to create matching client material at runtime.
Acceptance criteria
Out of scope for this issue
- Username / password authentication (separate issue)
- Client certificate generation / rotation inside the plugin
- Integration with PKI / cert management systems (Hashicorp Vault, etc.)
- CA chain validation beyond what
open62541 provides natively
References
Context
ros2_medkit_opcua(added in #365) currently connects to OPC-UA servers with Anonymous authentication andSecurityPolicy=None. The plugin logs a warning on startup making this explicit:This is acceptable for isolated LANs, demo environments, and CI testing, but it is not suitable for any deployment where the network between the gateway and the PLC is not fully trusted - which is the normal case in industrial deployments where OPC-UA is deployed alongside safety-critical control traffic.
This issue tracks adding support for certificate-based authentication using
Basic256Sha256(the most widely supported secure OPC-UA profile in industrial PLC servers like Siemens S7, Beckhoff TwinCAT, and Allen-Bradley).Scope
Code changes
Extend
OpcuaClientConfig(include/ros2_medkit_opcua/opcua_client.hpp) with new fields:std::string security_policy- one ofNone,Basic128Rsa15,Basic256,Basic256Sha256(defaultNonefor backward compat)std::string security_mode- one ofNone,Sign,SignAndEncrypt(defaultNone)std::string client_cert_path- path to DER or PEM client certificatestd::string client_key_path- path to private keystd::vector<std::string> trusted_server_certs- optional list of trusted server certificates (or directory)Wire these through to
open62541pp::ClientConfiginOpcuaClient::connect(). The relevant open62541pp APIs aresetSecurityPolicy,setCertificate,setPrivateKey, and the trust list configuration.Handle certificate loading errors gracefully with clear log messages (missing file, wrong format, expired cert, etc.).
Extend the YAML node map schema with a new top-level
security:section so configuration is not hardcoded in the plugin parameters.Environment variable overrides following the existing pattern (
OPCUA_CLIENT_CERT,OPCUA_CLIENT_KEY, etc.) for Docker deployment flexibility.Documentation
design/index.rst- replace theSecuritysection with real documentation of the supported profiles and the trust model.README.mdwith anOPC-UA Securitysection showing a complete example of running against a PLC with cert auth enabled (certificate generation, trust list, config snippet).Security limitationssubsection from README once covered, or convert to aKnown limitationsnote listing still-missing features (e.g., username/password, CA chain validation).Tests
OpcuaClientConfigwith all security fields (failure cases for missing cert file, bad profile name).OpcuaClient::connectwith a mock OPC-UA server that requires cert auth (requires a fixture server with known cert).opcua-plugin.ymlthat runs OpenPLC configured with cert auth and verifies the plugin connects successfully.The integration test requires generating a test CA and client certificate during CI setup. This is the main complexity driver of this issue - OpenPLC needs to be configured with a proper trust list, and the test scripts need to create matching client material at runtime.
Acceptance criteria
OpcuaClientconnects successfully to a server requiringBasic256Sha256 + SignAndEncryptAnonymous + Nonestill works for backward compat (existing tests keep passing)Out of scope for this issue
open62541provides nativelyReferences
ClientConfig: https://github.com/open62541pp/open62541pp/blob/v0.16.0/include/open62541pp/config.hpp