basysKom AnwendungsEntwicklung

OPC UA and Rust in 2025
Essential Summary
What are the options to implement an OPC UA server in Rust in 2025?

Motivation

Lately, we have seen great interest in implementing OPC UA servers in Rust among our embedded customers. This article provides an overview of the available options and their level of maturity.

There are high quality, high conformance FOSS solutions like open62541 or NodeOPCUA available today – so what is driving the interest in Rust?

The open62541 library is written in C, its advantages are good performance and little resource overhead. Servers based on open62541 have been officially certified by the OPC certification test labs. Unfortunately it has been shown over and over again that it is quite hard to implement secure software in C (which is not a “memory-safe” language). Regulations such as the European Cyber Resilience Act are fueling the interest in finding a better solution.

On the other hand, NodeOPCUA runs on the Node.js platform and is written in TypeScript which is a memory-safe language, but has higher resource requirements which might be prohibitive for certain applications.

Rust Based Options

At the moment, there are two options to implement an OPC UA server in Rust. While locka99/opcua is a pure Rust implementation, the open62541 bindings project is based on open62541 and uses Rust’s FFI to make parts of its feature set available in Rust.

We conducted a thorough evaluation of both options and tried to implement a basic OPC UA server supporting some of the features we usually require in our customer projects:

  • Support for secure connections
  • Attaching external data sources to OPC UA Variable nodes
  • Method calls with custom callbacks
  • Events
  • Companion specifications and custom OPC UA nodesets

locka99/opcua

The pure Rust implementation is available under the Mozilla Public License 2.0. It offers fundamental features, such as a client/server API, TCP communication, endpoints with secure security policies, subscriptions and events.

This stack is modular and organized into several crates: opcua-core for common client-server functionality, opcua-types for defining data types, and opcua-crypto for encryption, opcua-client and opcua-server for the client and server API. The project also includes also offer a nodeset compiler and a certificate creator as a command line tool.

As part of the evaluation outlined above, we attempted to integrate two companion specification nodesets into a server. We successfully compiled and loaded DI and AutoID into the address space. However, we noticed that some things did not work as expected. Creating an instance of a an object type was not straightforward and custom data types were not supported. In some cases, mandatory nodes of the nodeset were missing in the address space. While method callbacks could be set, there was no way to utilize input and output arguments. Given the issues we faced, we aborted our evaluation at this point.

While some of our usually needed features are lacking or require lots of effort to get them working, the native stack is currently the only option if Rust’s inherent memory safety is the most important driver in choosing a Rust based implementation. Simple applications that do not rely on additional companion specifications might be possible, but we would not recommend going this route at this point.

async-opcua

Since locka99/opcua had become quite stale over the last year, a discussion on Github led to a fork in the freeopcua organization where lots of refactoring and changes to basic implementation approaches have been going on over the last few months. Time will show if a Rust based implementation with sufficient feature completeness and maturity emerges once the dust has settled.

We wanted to evaluate the state of the fork, especially regarding the issues we faced with the original project. One of the major changes is the migration of the nodeset compiler from JavaScript to a Rust crate. To generate a nodeset, a YAML file specifying the target files is required, along with the OPC UA BSD, XSD, and XML files.

We were able to generate the DI nodeset successfully, but generating the AutoID nodeset was not possible due to missing of BSD and XSD files. However, the support for custom data types has significantly improvement compared to the original project.

Since the fork is actively maintained, there is a chance that full support for these features will be available at some point. We will definitely keep an eye on this project.

open62541 bindings

The open62541 project offers a mature OPC UA implementation, covering many parts of the OPC UA specification, including must services, custom data-types, events, encryption and support for complex  nodesets. In theory, Rust developers can take advantage of this existing, mature OPC UA implementation by using the Foreign Function Interface (FFI) of Rust. This is the goal of the open62541 rust bindings project.

Creating safe high-quality bindings is manual work, so at the moment, the project only offers bindings for basic server and client usage, but is far from exposing the whole open62541 feature set. The developers of this project provide a second project called open62541-sys that enables users to generate bindings for arbitrary functions of the open62541 library (albeit unsafe ones).

While evaluating this project, one showstopper for us was the lack of a code generator that would generate code for companion specifications. We worked around this by using the open62541 code generator to generate C code and then used open62541-sys to generate Rust bindings. With some manual fixes, this kind of worked, but we do not consider this a long term solution as it vastly increases the amount of unsafe code.

Both the open62541 rust bindings as well as open62541-sys are available under the Mozilla Public License 2.0. We did not observe a large community of contributors.

Conclusion

Currently, there is no “production ready” solution to implement an OPC UA server in Rust. At this point in time, we would advise to use a high quality, well maintained solutions such as open62541 and to ensure that the various mitigations against memory safety exploits offered by toolchains and platforms are enabled. Given sufficient resources on the target system, one might also consider solutions like the Node.js based NodeOPCUA module to profit from a memory safe language.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Weitere Blogartikel

basysKom Newsletter

We collect only the data you enter in this form (no IP address or information that can be derived from it). The collected data is only used in order to send you our regular newsletters, from which you can unsubscribe at any point using the link at the bottom of each newsletter. We will retain this information until you ask us to delete it permanently. For more information about our privacy policy, read Privacy Policy