4. Hands-On 1: Securing Connext DDS Applications¶
Suppose you are the Secure DDS expert in a company called Patient Monitoring Innovations (PMI). In this exercise, we will secure a Connext DDS project created with RTI Code Generator (rtiddsgen).
Tip
For a brief introduction to RTI Code Generator, see Run Code Generator, in the Introduction to Publish/Subscribe. Full details are in the RTI Code Generator User’s Manual.
As a first step, we will use some security artifacts provided with Connext DDS. The applications in your project will use these artifacts to provide the required configuration and credentials to the Security Plugins, including the identity and permissions of the DomainParticipants. Then we will run the applications to check that we have communication. Finally, we will verify that eavesdropping is not possible with the configured level of security.
4.1. Generating a Connext DDS Project¶
Run rtisetenv_<architecture> in a new command prompt window, to avoid issues with paths and licensing.
Where <architecture> depends on your target machine (where you will deploy your completed application). Architecture strings are listed in the RTI Connext DDS Core Libraries Platform Notes. Examples are x64Win64VS2017 and x64Linux4gcc7.3.0.
(See Set Up Environment Variables, in Introduction to Publish/Subscribe.)
Generate an example from an IDL file. You may want to use the following type definition for this example project:
struct PatientMonitoring { string<128> patient_condition; };
Put the IDL file in a directory called
patient_monitoring_project
.
Run RTI Code Generator (rtiddsgen) from that directory:
$ cd patient_monitoring_project $ rtiddsgen -example <architecture> -language C++11 PatientMonitoring.idl
$ cd patient_monitoring_project $ rtiddsgen -example <architecture> -language C++11 PatientMonitoring.idl
> cd patient_monitoring_project > rtiddsgen -example <architecture> -language C++11 -ppDisable PatientMonitoring.idl
Note
We’ll use patient_monitoring_project
as the working directory. Unless
otherwise indicated, we’ll run all commands from this directory.
The generated example will be composed of the following files:
Files |
Description |
---|---|
|
Support for your types in C++ |
|
Example publisher/subscriber application.
|
Makefiles and |
Architecture-dependent build files |
|
QoS profiles |
|
See this README for instructions on how to open and modify the files. |
Our goal is to secure the generated publisher and subscriber applications (Alice and Bob, respectively, using the example from the previous sections).
4.2. Adding Security Artifacts to Your Project¶
We’ll rely on some files from the examples
directory in
rti_workspace
to define the security setup (see
Paths Mentioned in Documentation).
These files will provide the applications you’ll build with
the security configuration and credentials required by the Security Plugins.
For convenience, copy these files into your project’s top level:
$ cp -r <path to examples>/dds_security/* ./
Alternatively, copy/paste all the directories in
<path to examples>/dds_security/
to
patient_monitoring_project
(the working directory).
$ cp -r <path to examples>/dds_security/* ./
Alternatively, copy/paste all the directories in
<path to examples>/dds_security/
to
patient_monitoring_project
(the working directory).
> robocopy /E <path to examples>\dds_security\ .
Alternatively, copy/paste all the directories in
<path to examples>dds_security` to
:file:`patient_monitoring_project
(the working directory).
4.3. Enabling Security in Your QoS Profiles¶
Now modify the QoS profiles (XML) so that the applications will
load the security libraries and the required security artifacts.
To do so, define a profile named Alice in your USER_QOS_PROFILES.xml
by replacing the existing default profile with the following sample profile:
<qos_profile name="Alice" base_name="BuiltinQosLib::Generic.Security" is_default_qos="true">
<domain_participant_qos>
<property>
<value>
<!-- Certificate Authorities -->
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:./cert/ecdsa01/ca/ecdsa01RootCaCert.pem</value>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:./cert/ecdsa01/ca/ecdsa01RootCaCert.pem</value>
</element>
<!-- Participant Public Certificate and Private Key -->
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:./cert/ecdsa01/identities/ecdsa01Peer01Cert.pem</value>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:./cert/ecdsa01/identities/ecdsa01Peer01Key.pem</value>
</element>
<!-- Signed Governance and Permissions files -->
<element>
<name>dds.sec.access.governance</name>
<value>file:./xml/signed/signed_Governance.p7s</value>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:./xml/signed/signed_PermissionsA.p7s</value>
</element>
</value>
</property>
</domain_participant_qos>
</qos_profile>
This profile inherits from the builtin BuiltinQosLib::Generic.Security
profile. This tells your DomainParticipant to use the Security Plugins as the
security plugin suite. (For further details, see Properties for
Enabling Security in the RTI Security Plugins User’s Manual.)
Note that not indicating a security plugin suite will
result in a working but unsecure application.
The profile also configures the following properties:
dds.sec.auth.identity_ca
Configures the Identity CA certificate, used to verify that Identity Certificates from other DomainParticipants are legitimate. Usually, all the participants in your secure system will load the same Identity CA certificate. 1
dds.sec.access.permissions_ca
Configures the Permissions CA certificate, used to verify that the Governance and Permissions Files are legitimate. All the participants in your secure system must load the same Permissions CA certificate. 2
dds.sec.auth.identity_certificate
Configures the Identity Certificate for the local DomainParticipant. Every secure participant should have its own unique Identity Certificate, signed by the Identity CA.
dds.sec.auth.private_key
Configures the Private Key for the local DomainParticipant, used during the authentication process. Every secure participant should have its own unique Private Key, only known to the local participant.
dds.sec.access.governance
Configures the Governance File, which defines how your system is protected. All the participants in your secure system must load the same Governance File 3, signed by the Permissions CA.
dds.sec.access.permissions
Configures the Permissions File for the local DomainParticipant, which specifies what it is allowed to do in every Secure Domain. Every secure participant should have its own Permissions File, signed by the Permissions CA.
Important
A participant that is not configured to use a security plugin
suite will omit any security-related properties, resulting in
a working but unsecure DomainParticipant. Make sure your QoS
profile inherits from the builtin
BuiltinQosLib::Generic.Security
profile or refer to
Building and Running Security Plugins-Based Applications
in the RTI Security Plugins User’s Manual.
In later hands-on exercises, you will learn how to generate all the files listed above. For now, we’ll use the example files you copied in the previous step.
- 1
Depending on your use case, different DomainParticipants may trust a different Identity CA, for example, when several intermediate CAs exist in your PKI. For further details, see Alternative CAs in the Security Plugins User’s Manual.
- 2
In systems where multiple Permissions Certificate Authorities may exist, you can use the
access_control.alternative_permissions_authority_files
property to specify alternative Permissions CA certificates. For further details, see Governance and Permissions in the Security Plugins User’s Manual.- 3
If not the same, at least Governance Files loaded by different DomainParticipants need to be compatible. For further information, see Governance File in the Security Plugins User’s Manual.
4.4. Linking Your Applications Against RTI Security Plugins and OpenSSL Libraries¶
So far, you have generated a Connext DDS project and configured its QoS profiles to load the required security artifacts and enable the Security Plugins. Now we will build the applications, so you can see them in action. For convenience, we will link the applications dynamically to both Connext DDS and the OpenSSL libraries (for other linking options, see Building and Running Security Plugins-Based Applications in the RTI Security Plugins User’s Manual.
Note
We use RTI_OPENSSLHOME
to refer to the path where the
OpenSSL bundle is installed in your system 4 (in Connext DDS
6.1.2, RTI_OPENSSLHOME
is the path to
openssl-1.1.1n). The installation process is described in
the RTI Security Plugins Installation Guide.
4.4.1. Building the Application¶
Use the generated Makefile with the SHAREDLIB
variable set
to 1
to force dynamic linking; set DEBUG=1
to build in debug mode:
$ make -f makefile_PatientMonitoring_<architecture> SHAREDLIB=1 DEBUG=1
Use the generated Makefile with the SHAREDLIB
variable set
to 1
to force dynamic linking; set DEBUG=1
to build in debug mode:
$ make -f makefile_PatientMonitoring_<architecture> SHAREDLIB=1 DEBUG=1
We will build the solution generated by RTI Code Generator
with the Debug DLL configuration. You can do this by using the
msbuild command-line tool provided with Visual Studio.
To do this, open a Visual Studio command prompt and change to the
patient_monitoring_project
directory (cd
patient_monitoring_project
).
Then build the Debug DLL solution, which is configured for dynamic
linking:
> msbuild /property:Configuration="Debug DLL" PatientMonitoring-<architecture>.sln
Alternatively, you can open PatientMonitoring-<architecture>.sln
with Visual Studio, as shown in the following screenshot. If you
choose this method, select the Debug DLL option in the
Solution Configurations dropdown menu (1), then select
Build for both PatientMonitoring_publisher and
PatientMonitoring_subscriber (2).
4.5. Running the Applications¶
It’s time to see your secure publisher and subscriber working together! Open two command prompts and configure the environment in both to point to the location of the dynamic libraries, as explained below.
4.5.1. Configuring the Environment in Both Command Prompts¶
Configure the environment for Connext DDS by running rtisetenv_<architecture> from the Connext DDS installation path.
For details, see Set Up Environment Variables, in Introduction to Publish/Subscribe.
This will update your PATH to include the location of the Connext DDS binaries.
Update the shared library environment variable to include the directory where the OpenSSL libraries reside. For this example, you may want to use OpenSSL debug libraries.
$ export LD_LIBRARY_PATH=$RTI_OPENSSLHOME/<architecture>/debug/lib:$LD_LIBRARY_PATH
$ export DYLD_LIBRARY_PATH=$RTI_OPENSSLHOME/<architecture>/debug/lib:$DYLD_LIBRARY_PATH
> set PATH=%RTI_OPENSSLHOME%\<architecture>\debug\bin;%PATH%
If you run the applications from within Visual Studio, you need to set up the environment before opening the solution in Visual Studio.
Tip
Make sure you set the environment in both command prompts.
4.5.2. Checking Communication¶
Run the publisher in one of the command prompts and the subscriber in the other.
Do not provide any arguments to the applications, so that they communicate in Domain 0. This will be your Secure Domain.
You should see the message Received data in the subscriber’s command prompt, which indicates successful communication:
Publisher:
$ ./objs/<architecture>/PatientMonitoring_publisher Writing PatientMonitoring, count 0 Writing PatientMonitoring, count 1 Writing PatientMonitoring, count 2 Writing PatientMonitoring, count 3
Subscriber:
$ ./objs/<architecture>/PatientMonitoring_subscriber PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec...
Publisher:
$ ./objs/<architecture>/PatientMonitoring_publisher Writing PatientMonitoring, count 0 Writing PatientMonitoring, count 1 Writing PatientMonitoring, count 2 Writing PatientMonitoring, count 3
Subscriber:
$ ./objs/<architecture>/PatientMonitoring_subscriber PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec...
Publisher:
> objs\<architecture>\PatientMonitoring_publisher.exe Writing PatientMonitoring, count 0 Writing PatientMonitoring, count 1 Writing PatientMonitoring, count 2 Writing PatientMonitoring, count 3
Subscriber:
> objs\<architecture>\PatientMonitoring_subscriber.exe PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec... PatientMonitoring subscriber sleeping up to 1 sec... [patient_condition: ] PatientMonitoring subscriber sleeping up to 1 sec...
Congratulations! You have added Security Plugins to your applications.
4.6. Checking that Your Applications Communicate Securely¶
To check that communication is actually secure, now let’s have a non-secure Connext DDS application try to subscribe to the PatientMonitoring Topic. For convenience, we will use RTI Administration Console as the non-secure subscriber.
4.6.1. Verifying that Eavesdropping Attempts are Frustrated¶
Open Administration Console and join Domain 0.
(For details on using Administration Console, see Viewing Your Data, in Introduction to Publish/Subscribe.)
Notice that your secure DomainParticipants do not show up in Administration Console.
The security configuration of your Secure Domains requires DomainParticipants to authenticate in order to join the Domain. Any participant with wrong or no credentials will fail to authenticate and will be disallowed from communicating in the Domain. In this example, the Governance File you copied into the project (and added to Alice’s XML QoS configuration) protects the endpoint discovery with encryption. As a result, intruders and eavesdroppers will not have any information regarding the Topics in your secure system. We will show the configuration values to force authentication and to enable encryption in Hands-On 2: Defining Your System’s Security Requirements.
4.6.2. Detecting Eavesdropping Attempts¶
When Administration Console runs in Domain 0, it sends discovery information to all other DomainParticipants in the same Domain. When your secure participants receive this information, they will require Administration Console to authenticate. However, Administration Console does not hold a valid credential and cannot authenticate, making discovery fail and preventing further communication.
You might want to detect that an unauthorized DomainParticipant tried to communicate in your secure system. To do this:
Modify your QoS profile to increase the verbosity of the Logging Plugin:
<qos_profile name="Alice" base_name="BuiltinQosLib::Generic.Security" is_default_qos="true"> <domain_participant_qos> <property> <value> <!-- Logging Plugin setup --> <element> <name>com.rti.serv.secure.logging.verbosity</name> <value>WARNING</value> </element> ...
Rerun your secure publisher or/and subscriber.
A message like this should show up in its command prompt:
PRESParticipant_getRemoteParticipantInitialSecurityState:unauthenticated remote participant 1018db2.74f9482c.2712840d denied
This message indicates a failure during the authentication of a remote DomainParticipant. It will be logged every time an unauthenticated participant attempts to join a Secure Domain where
allow_unauthenticated_participants
is set toFALSE
.
4.7. Further Exercises¶
So far, we have defined a single QoS profile named Alice to enable the
Security Plugins. Since this profile has the attribute is_default_qos=true
, any
Connext DDS application loading USER_QOS_PROFILES.xml
will have the
same security artifacts. This makes it convenient to load the same
Governance File and CA certificates in an example. However, all your
applications will have the same identity and permissions, which is
strongly discouraged. Please note that the authentication succeeds in
this situation since the DomainParticipants still have valid
credentials, i.e., the Identity CA has issued all the identities.
Also note that the Permissions File the publisher and the subscriber
are loading gives them permission to publish and subscribe to any
Topic, making communication possible.
4.7.1. Give Different Credentials to Each Application in Your System¶
To maximize security, different applications should always have different identities and permissions. Write a second QoS profile for your project in the same XML QoS file. You should end up with the following:
A profile called
Alice
for the publisher applicationA profile called
Bob
for the subscriber application
Tip
You may want to have Bob’s profile inherit from Alice’s, with
base_name="Alice"
. This way, you don’t need to specify the CA certificates and the Governance File twice inUSER_QOS_PROFILES.xml
.Modify Bob’s identity and permissions.
For example, use
ecdsa01Peer02Cert.pem
,ecdsa01Peer02Key.pem
, andsigned_PermissionsB.p7s
.Note that
ecdsa01Peer02Key.pem
is password-protected, so use thedds.sec.auth.password
property to specify the password in Bob’s QoS profile:<qos_profile name="Bob" base_name="Alice"> <domain_participant_qos> <property> <value> <!-- Participant Public Certificate and Private Key --> <element> <name>dds.sec.auth.identity_certificate</name> <value>file:./cert/ecdsa01/identities/ecdsa01Peer02Cert.pem</value> </element> <element> <name>dds.sec.auth.private_key</name> <value>file:./cert/ecdsa01/identities/ecdsa01Peer02Key.pem</value> </element> <element> <name>dds.sec.auth.password</name> <value>VG9tQjEy</value> </element> <!-- Signed Permissions file --> <element> <name>dds.sec.access.permissions</name> <value>file:./xml/signed/signed_PermissionsB.p7s</value> </element> </value> </property> </domain_participant_qos> </qos_profile>
Modify the applications’ source code in
PatientMonitoring_publisher.cxx
andPatientMonitoring_subscriber.cxx
to use these QoS profiles. The publisher’s DomainParticipant should use theAlice
QoS profile, and the subscriber’s DomainParticipant should use theBob
QoS profile.To do so, use the default
QosProvider
to make your DomainParticipants load their corresponding profiles (see QosProvider in the Modern C++ API Reference). Note that you will need to includeddscore.hpp
.#include <dds/core/ddscore.hpp> ... // Load Bob's QoS dds::domain::qos::DomainParticipantQos bob_qos = dds::core::QosProvider::Default().participant_qos("PatientMonitoring_Library::Bob"); // Create a DomainParticipant with Bob's QoS dds::domain::DomainParticipant participant( domain_id, bob_qos);
Rebuild your project and run your publisher and subscriber applications. You should see communication.
4.8. Troubleshooting¶
When I try to run Code Generator (rtiddsgen), my system does not recognize or find the command.
Make sure Connext DDS is installed correctly.
Also make sure you’ve correctly set the PATH environment variable (your PATH should include
<NDDSHOME>/bin
). You can run rtisetenv_<architecture> to add the location of the Connext DDS binaries to your PATH (see Set Up Environment Variables, in Introduction to Publish/Subscribe).
When I run Code Generator (rtiddsgen), I get this error:
The preprocessor 'CL.EXE' cannot be found in your path.
Make sure your toolchain’s preprocessor is available. If you are using Visual Studio, make sure you are using a Visual Studio command prompt. Alternatively, run rtiddsgen with the -ppDisable option.
When I run Code Generator (rtiddsgen), I get warnings stating:
File exists and will not be overwritten
Some files that would normally be generated already exist and will not be overwritten. These errors are expected.
When I run the publisher/subscriber, I get this error:
error while loading shared libraries: libnddscpp.so
Make sure the shared library environment variable includes the directory where the Connext DDS libraries reside. See Configuring the Environment in Both Command Prompts for a solution.
When I run the publisher/subscriber, I get this error:
!open library=libnddssecurity.so
Make sure the OpenSSL target bundle provided by RTI is installed correctly. 4
Make sure the shared library environment variable includes the directory where OpenSSL libraries reside. See Configuring the Environment in Both Command Prompts for a solution.
Make sure your environment is pointing to the correct version of the OpenSSL libraries.
When I run the publisher and subscriber, communication does not occur.
Make sure you run both applications from the
patient_monitoring_project
directory, so they load the sameUSER_QOS_PROFILES.xml
file. Note: If the project file for your IDE was auto-generated and you are running from within your IDE, it should run from that directory automatically.
Make sure your applications are loading the right security artifacts or artifacts combinations (that is, you don’t get a
!certificate verify fail
error).
Be careful with Bob’s QoS profile depending on Alice’s — you may be changing properties in Alice’s profile and loading Bob’s, or the opposite.
When I run the publisher/subscriber, I get this error:
!certificate verify fail
Make sure your applications are loading the right security artifacts or artifacts combinations.