.. include:: vars.rst .. _section-sec-HO1: 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| project created with |RTI_CODEGEN| (:command:`rtiddsgen`). .. only:: cpp98 .. tip:: For a brief introduction to |RTI_CODEGEN|, see :link_gsg_cpp98:`Run Code Generator, in the Introduction to Publish/Subscribe `. Full details are in the :link_codegen_users_manual:`RTI Code Generator User's Manual <>`. .. only:: cpp11 .. tip:: For a brief introduction to |RTI_CODEGEN|, see :link_gsg_cpp11:`Run Code Generator, in the Introduction to Publish/Subscribe `. Full details are in the :link_codegen_users_manual:`RTI Code Generator User's Manual <>`. As a first step, we will use some security artifacts provided with |CONNEXT|. The applications in your project will use these artifacts to provide the required configuration and credentials to the |SP|, including the identity and permissions of the |DPs|. 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. .. _section-sec-HO1-generating-connext-project: Generating a Connext DDS Project -------------------------------- #. Run :command:`rtisetenv_` in a new command prompt window, to avoid issues with paths and licensing. Where :command:`` depends on your target machine (where you will deploy your completed application). Architecture strings are listed in the :link_platform_notes:`RTI Connext DDS Core Libraries Platform Notes <>`. Examples are :command:`x64Win64VS2013` and :command:`i86Linux3gcc4.8.2`. .. only:: cpp98 (See :link_gsg_cpp98:`Set Up Environment Variables, in Introduction to Publish/Subscribe `.) |br| |br| .. only:: cpp11 (See :link_gsg_cpp11:`Set Up Environment Variables, in Introduction to Publish/Subscribe `.) |br| |br| #. Generate an example from an IDL file. You may want to use the following type definition for this example project: .. code-block:: IDL :caption: Sample contents of PatientMonitoring.idl struct PatientMonitoring { string<128> patient_condition; }; #. Put the IDL file in a directory called :file:`patient_monitoring_project`. |br| |br| #. Run |RTI_CODEGEN| (:command:`rtiddsgen`) from that directory: .. only:: cpp98 .. tabs:: .. group-tab:: Linux .. code-block:: console $ cd patient_monitoring_project $ rtiddsgen -example -language C++ PatientMonitoring.idl .. group-tab:: macOS .. code-block:: console $ cd patient_monitoring_project $ rtiddsgen -example -language C++ PatientMonitoring.idl .. group-tab:: Windows .. code-block:: doscon > cd patient_monitoring_project > rtiddsgen -example -language C++ -ppDisable PatientMonitoring.idl .. only:: cpp11 .. tabs:: .. group-tab:: Linux .. code-block:: console $ cd patient_monitoring_project $ rtiddsgen -example -language C++11 PatientMonitoring.idl .. group-tab:: macOS .. code-block:: console $ cd patient_monitoring_project $ rtiddsgen -example -language C++11 PatientMonitoring.idl .. group-tab:: Windows .. code-block:: doscon > cd patient_monitoring_project > rtiddsgen -example -language C++11 -ppDisable PatientMonitoring.idl .. note:: We'll use :file:`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: .. only:: cpp98 .. tabularcolumns:: |\X{50}{100}|\X{50}{100}| .. list-table:: Generated Files :name: TableGeneratedFilesCpp :widths: 20 80 :header-rows: 1 :class: longtable * - Files - Description * - :file:`PatientMonitoring.cxx` |br| :file:`PatientMonitoring.h` |br| :file:`PatientMonitoringPlugin.cxx` |br| :file:`PatientMonitoringPlugin.h` |br| :file:`PatientMonitoringSupport.cxx` |br| :file:`PatientMonitoringSupport.h` - Support for your types in C++ * - :file:`PatientMonitoring_publisher.cxx` |br| :file:`PatientMonitoring_subscriber.cxx` - Example publisher/subscriber application. |br| |br| Contains a |DP| with a single |DW|/|DR| for the last type defined in :file:`PatientMonitoring.idl`. * - Makefiles and |br| Visual Studio® project files (for Windows applications) - Architecture-dependent build files * - :file:`USER_QOS_PROFILES.xml` - QoS profiles * - :file:`README_.txt` - See this README for instructions on how to open and modify the files. .. only:: cpp11 .. tabularcolumns:: |\X{50}{100}|\X{50}{100}| .. list-table:: Generated Files :name: TableGeneratedFilesCpp11 :widths: 20 80 :header-rows: 1 :class: longtable * - Files - Description * - :file:`PatientMonitoring.cxx` |br| :file:`PatientMonitoring.hpp` |br| :file:`PatientMonitoringPlugin.cxx` |br| :file:`PatientMonitoringPlugin.hpp` |br| - Support for your types in C++ * - :file:`PatientMonitoring_publisher.cxx` |br| :file:`PatientMonitoring_subscriber.cxx` - Example publisher/subscriber application. |br| |br| Contains a |DP| with a single |DW|/|DR| for the first type defined in :file:`PatientMonitoring.idl`. * - Makefiles and |br| Visual Studio® project files (for Windows applications) - Architecture-dependent build files * - :file:`USER_QOS_PROFILES.xml` - QoS profiles * - :file:`README_.txt` - 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). .. _section-sec-HO1-adding-security-artifacts: Adding Security Artifacts to Your Project ----------------------------------------- We'll rely on some files from the :file:`examples` directory in :file:`rti_workspace` to define the security setup (see :ref:`section-sec-intro-paths`). These files will provide the applications you'll build with the security configuration and credentials required by the |SP|. For convenience, copy these files into your project's top level: .. tabs:: .. group-tab:: Linux .. code-block:: console $ cp -r /dds_security/* ./ Alternatively, copy/paste all the directories in :file:`/dds_security/` to :file:`patient_monitoring_project` (the working directory). .. group-tab:: macOS .. code-block:: console $ cp -r /dds_security/* ./ Alternatively, copy/paste all the directories in :file:`/dds_security/` to :file:`patient_monitoring_project` (the working directory). .. group-tab:: Windows .. code-block:: doscon > robocopy /E \dds_security\ . Alternatively, copy/paste all the directories in :file:`\dds_security\` to :file:`patient_monitoring_project` (the working directory). .. _section-sec-HO1-enabling-security-your-qos-profiles: 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 :file:`USER_QOS_PROFILES.xml` by replacing the existing default profile with the following sample profile: .. code-block:: xml :caption: Sample QoS profile with security enabled. dds.sec.auth.identity_ca file:./cert/ecdsa01/ca/ecdsa01RootCaCert.pem dds.sec.access.permissions_ca file:./cert/ecdsa01/ca/ecdsa01RootCaCert.pem dds.sec.auth.identity_certificate file:./cert/ecdsa01/identities/ecdsa01Peer01Cert.pem dds.sec.auth.private_key file:./cert/ecdsa01/identities/ecdsa01Peer01Key.pem dds.sec.access.governance file:./xml/signed/signed_Governance.p7s dds.sec.access.permissions file:./xml/signed/signed_PermissionsA.p7s This profile inherits from the builtin ``BuiltinQosLib::Generic.Security`` profile. This tells your |DP| to use the |SP| as the security plugin suite. (For further details, see :link_sec_um:`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: * :property:`dds.sec.auth.identity_ca` |br| Configures the Identity CA certificate, used to verify that |IdentityCerts| from other |DPs| are legitimate. Usually, all the participants in your secure system will load the same Identity CA certificate. [#]_ * :property:`dds.sec.access.permissions_ca` |br| 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. [#]_ * :property:`dds.sec.auth.identity_certificate` |br| Configures the |IdentityCert| for the local |DP|. Every secure participant should have its own unique |IdentityCert|, signed by the Identity CA. * :property:`dds.sec.auth.private_key` |br| Configures the Private Key for the local |DP|, used during the authentication process. Every secure participant should have its own unique Private Key, only known to the local participant. * :property:`dds.sec.access.governance` |br| Configures the |GovernanceFile|, which defines how your system is protected. All the participants in your secure system must load the same |GovernanceFile| [#]_, signed by the Permissions CA. * :property:`dds.sec.access.permissions` |br| Configures the |PermissionsFile| for the local |DP|, which specifies what it is allowed to do in every |SecDomain|. Every secure participant should have its own |PermissionsFile|, 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 |DP|. Make sure your QoS profile inherits from the builtin ``BuiltinQosLib::Generic.Security`` profile or refer to :link_sec_um:`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. .. [#] Depending on your use case, different |DPs| may trust a different Identity CA, for example, when several intermediate CAs exist in your PKI. For further details, see :link_sec_um:`Alternative CAs in the Security Plugins User's Manual `. .. [#] In systems where multiple Permissions Certificate Authorities may exist, you can use the :property:`access_control.alternative_permissions_authority_files` property to specify alternative Permissions CA certificates. For further details, see :link_sec_um:`Governance and Permissions in the Security Plugins User's Manual `. .. [#] If not the same, at least |GovernanceFiles| loaded by different |DPs| need to be compatible. For further information, see :link_sec_um:`Governance File in the Security Plugins User's Manual `. .. _section-sec-HO1-linking-your-applications: Linking Your Applications Against RTI Security Plugins and OpenSSL Libraries ---------------------------------------------------------------------------- So far, you have generated a |CONNEXT| project and configured its QoS profiles to load the required security artifacts and enable the |SP|. Now we will build the applications, so you can see them in action. For convenience, we will link the applications dynamically to both |CONNEXT| and the OpenSSL libraries (for other linking options, see :link_sec_um:`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 [#openssl_lm_path]_ (in |CONNEXT| |CONNEXT_currentVersion|, ``RTI_OPENSSLHOME`` is the path to openssl-|OpenSSL_currentVersion|). The installation process is described in the :link_sec_install_guide:`RTI Security Plugins Installation Guide `. .. _section-sec-HO1-building-application: Building the Application ^^^^^^^^^^^^^^^^^^^^^^^^ .. tabs:: .. group-tab:: Linux Use the generated Makefile with the ``SHAREDLIB`` variable set to ``1`` to force dynamic linking; set ``DEBUG=1`` to build in debug mode: .. code-block:: console $ make -f makefile_PatientMonitoring_ SHAREDLIB=1 DEBUG=1 .. group-tab:: macOS Use the generated Makefile with the ``SHAREDLIB`` variable set to ``1`` to force dynamic linking; set ``DEBUG=1`` to build in debug mode: .. code-block:: console $ make -f makefile_PatientMonitoring_ SHAREDLIB=1 DEBUG=1 .. group-tab:: Windows We will build the solution generated by |RTI_CODEGEN| with the Debug DLL configuration. You can do this by using the :command:`msbuild` command-line tool provided with Visual Studio. To do this, open a Visual Studio command prompt and change to the :file:`patient_monitoring_project` directory (``cd patient_monitoring_project``). Then build the Debug DLL solution, which is configured for dynamic linking: .. code-block:: doscon > msbuild /property:Configuration="Debug DLL" PatientMonitoring-.sln Alternatively, you can open :file:`PatientMonitoring-.sln` with Visual Studio, as shown in the following screenshot. If you choose this method, select the **Debug DLL** option in the :guilabel:`Solution Configurations` dropdown menu (1), then select **Build** for both **PatientMonitoring_publisher** and **PatientMonitoring_subscriber** (2). .. figure:: static/HandsOn2.png :name: FigureHandsOn2 :align: center Building the application with Visual Studio 2015. .. _section-sec-HO1-running-applications: 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. .. _section-sec-HO1-configuring-environment: Configuring the Environment in Both Command Prompts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #. Configure the environment for |CONNEXT| by running :command:`rtisetenv_` from the |CONNEXT| installation path. .. only:: cpp98 For details, see :link_gsg_cpp98:`Set Up Environment Variables, in Introduction to Publish/Subscribe `. .. only:: cpp11 For details, see :link_gsg_cpp11:`Set Up Environment Variables, in Introduction to Publish/Subscribe `. This will update your **PATH** to include the location of the |CONNEXT| binaries. |br| |br| #. 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. .. tabs:: .. group-tab:: Linux .. code-block:: console $ export LD_LIBRARY_PATH=$RTI_OPENSSLHOME//debug/lib:$LD_LIBRARY_PATH .. group-tab:: macOS .. code-block:: console $ export DYLD_LIBRARY_PATH=$RTI_OPENSSLHOME//debug/lib:$DYLD_LIBRARY_PATH .. group-tab:: Windows .. code-block:: doscon > set PATH=%RTI_OPENSSLHOME%\\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. 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 |SecDomain|. |br| |br| #. You should see the message **Received data** in the subscriber's command prompt, which indicates successful communication: .. only:: cpp98 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_ok :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_ok :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp98_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp98_ok :end-before: """ .. only:: cpp11 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_ok :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_ok :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp11_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp11_ok :end-before: """ Congratulations! You have added |SP| to your applications. .. _section-sec-HO1-checking-applications-communicate-securely: Checking that Your Applications Communicate Securely ---------------------------------------------------- To check that communication is actually secure, now let's have a non-secure |CONNEXT| application try to subscribe to the *PatientMonitoring* |TOPIC|. For convenience, we will use |RTI_ADMINCONSOLE| as the non-secure subscriber. Verifying that Eavesdropping Attempts are Frustrated ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #. Open |ADMINCONSOLE| and join |DOMAIN| *0*. .. only:: cpp98 (For details on using |ADMINCONSOLE|, see :link_gsg_cpp98:`Viewing Your Data, in Introduction to Publish/Subscribe `. |br| |br| .. only:: cpp11 (For details on using |ADMINCONSOLE|, see :link_gsg_cpp11:`Viewing Your Data, in Introduction to Publish/Subscribe `.) |br| |br| #. Notice that your secure |DPs| do not show up in |ADMINCONSOLE|. The security configuration of your |SecDomains| requires |DPs| 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 |GovernanceFile| 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 :ref:`section-sec-HO2`. Detecting Eavesdropping Attempts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When |ADMINCONSOLE| runs in |DOMAIN| *0*, it sends discovery information to all other |DPs| in the same |DOMAIN|. When your secure participants receive this information, they will require |ADMINCONSOLE| to authenticate. However, |ADMINCONSOLE| does not hold a valid credential and cannot authenticate, making discovery fail and preventing further communication. You might want to detect that an unauthorized |DP| tried to communicate in your secure system. To do this: #. Modify your QoS profile to increase the verbosity of the |LoggingPlugin|: .. code-block:: xml com.rti.serv.secure.logging.verbosity WARNING ... #. Rerun your secure publisher or/and subscriber. A message like this should show up in its command prompt: .. code-block:: text PRESParticipant_getRemoteParticipantInitialSecurityState:unauthenticated remote participant 1018db2.74f9482c.2712840d denied This message indicates a failure during the authentication of a remote |DP|. It will be logged every time an unauthenticated participant attempts to join a |SecDomain| where :xmltag:`allow_unauthenticated_participants` is set to :xmlval:`FALSE`. .. _section-sec-HO1-further-exercises: Further Exercises ----------------- So far, we have defined a single QoS profile named Alice to enable the |SP|. Since this profile has the attribute ``is_default_qos=true``, any |CONNEXT| application loading :file:`USER_QOS_PROFILES.xml` will have the same security artifacts. This makes it convenient to load the same |GovernanceFile| 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 |DPs| still have valid credentials, i.e., the Identity CA has issued all the identities. Also note that the |PermissionsFile| the publisher and the subscriber are loading gives them permission to publish and subscribe to any |TOPIC|, making communication possible. 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 application * A 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 |GovernanceFile| twice in :file:`USER_QOS_PROFILES.xml`. #. Modify Bob's identity and permissions. For example, use :file:`ecdsa01Peer02Cert.pem`, :file:`ecdsa01Peer02Key.pem`, and :file:`signed_PermissionsB.p7s`. Note that :file:`ecdsa01Peer02Key.pem` is password-protected, so use the :property:`dds.sec.auth.password` property to specify the password in Bob's QoS profile: .. code-block:: xml dds.sec.auth.identity_certificate file:./cert/ecdsa01/identities/ecdsa01Peer02Cert.pem dds.sec.auth.private_key file:./cert/ecdsa01/identities/ecdsa01Peer02Key.pem dds.sec.auth.password VG9tQjEy dds.sec.access.permissions file:./xml/signed/signed_PermissionsB.p7s #. Modify the applications' source code in :file:`PatientMonitoring_publisher.cxx` and :file:`PatientMonitoring_subscriber.cxx` to use these QoS profiles. .. only:: cpp98 To do so, you can create your |DPs| with ``DDSDomainParticipantFactory::create_participant_with_profile`` (see :link_api_cpp98:`DomainParticipantFactory in the Traditional C++ API Reference `). |br| |br| .. only:: cpp11 To do so, use the default ``QosProvider`` to make your |DPs| load their corresponding profiles (see :link_api_cpp11:`QosProvider in the Modern C++ API Reference `). Note that you will need to include :file:`ddscore.hpp`. .. code-block:: C++ #include ... // 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. .. _section-sec-HO1-troubleshooting: Troubleshooting --------------- * When I try to run |CODEGEN| (:command:`rtiddsgen`), my system does not recognize or find the command. Make sure |CONNEXT| is installed correctly. .. only:: cpp98 Also make sure you've correctly set the **PATH** environment variable (your **PATH** should include :file:`/bin`). You can run :command:`rtisetenv_` to add the location of the |CONNEXT| binaries to your **PATH** (see :link_gsg_cpp98:`Set Up Environment Variables, in Introduction to Publish/Subscribe `). |br| |br| .. only:: cpp11 Also make sure you've correctly set the **PATH** environment variable (your **PATH** should include :file:`/bin`). You can run :command:`rtisetenv_` to add the location of the |CONNEXT| binaries to your **PATH** (see :link_gsg_cpp11:`Set Up Environment Variables, in Introduction to Publish/Subscribe `). |br| |br| * When I run |CODEGEN| (:command:`rtiddsgen`), I get this error: .. code-block:: text 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 :command:`rtiddsgen` with the :command:`-ppDisable` option. |br| |br| * When I run |CODEGEN| (:command:`rtiddsgen`), I get warnings stating: .. code-block:: text 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. |br| |br| * When I run the publisher/subscriber, I get this error: .. code-block:: text error while loading shared libraries: libnddscpp.so Make sure the shared library environment variable includes the directory where the |CONNEXT| libraries reside. See :ref:`section-sec-HO1-configuring-environment` for a solution. |br| |br| * When I run the publisher/subscriber, I get this error: .. code-block:: text !open library=libnddssecurity.so * Make sure the OpenSSL target bundle provided by RTI is installed correctly. [#openssl_lm_path]_ |br| |br| * Make sure the shared library environment variable includes the directory where OpenSSL libraries reside. See :ref:`section-sec-HO1-configuring-environment` for a solution. |br| |br| * Make sure your environment is pointing to the correct version of the OpenSSL libraries. |br| |br| * When I run the publisher and subscriber, communication does not occur. * Make sure you run both applications from the :file:`patient_monitoring_project` directory, so they load the same :file:`USER_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. |br| |br| * Make sure your applications are loading the right security artifacts or artifacts combinations (that is, you don't get a ``!certificate verify fail`` error). |br| |br| * 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. |br| |br| * When I run the publisher/subscriber, I get this error: .. code-block:: text !certificate verify fail Make sure your applications are loading the right security artifacts or artifacts combinations. .. [#openssl_lm_path] In the license-managed version (with "``lm``" in the bundle name), OpenSSL is installed automatically when you install the |CONNEXT| host bundle. After installation, OpenSSL will be in ``/third_party/openssl-``.