.. include:: vars.rst .. _section-sec-HO3: Hands-On 3: Defining the DomainParticipant Permissions ====================================================== In this Hands-On, we will specify what the |DPs| in your project will be allowed to do within your secure system. We will achieve this by writing a |PermissionsDoc| for each of your participants. Going back to our example, Alice will only have permission to publish the *PatientMonitoring* data, while Bob will be restricted to subscribe to patient-related |TOPICs|. We will associate each |PermissionsDoc| with its corresponding participant based on their identities, limiting the system damage if one of your applications becomes compromised. To put the |PermissionsDocs| into effect, we will sign them with the provided Permissions CA. Finally, we will modify your secure participants' QoS profiles to point to the new |PermissionsDocs| and see how the new permissions are applied. .. note:: We will use the **OpenSSL CLI** to perform the security operations in the generation of the security artifacts. Make sure to include in the path your OpenSSL binary directory [#openssl_lm_path]_. The installation process is described in the :link_connext_dds_secure_ig:`RTI Security Plugins Installation Guide <>`. Note that the |SP| do not depend on OpenSSL to generate these artifacts; you can use the security toolkit of your choice. With that said, we recommend using |OpenSSL| to make sure that the certificates are in the right format. .. _section-sec-HO3-granting-permissions-your-secure-participants: Granting Permissions to Your Secure Participants ------------------------------------------------ As the DDS Security expert at Patient Monitoring Innovations (PMI), you are going to specify the permissions of every |DP| in your system. You will define what |TOPICs| your publisher, Alice, can read/write by associating rules with the subject in her |IdentityCert|. Create a file called :file:`pmiPermissionsAlice.xml` in the :file:`xml` directory (along with the rest of XML files) and add the following content: .. literalinclude:: snippets/pmiPermissionsAlice.xml :language: xml :caption: Sample |PermissionsDoc| configuring a single grant for *ParticipantAlice* This |PermissionsDoc| configures a grant for *ParticipantAlice*, identified by its |IdentityCert| (1). We will define the grant's subject name to make it apply to your publisher in :ref:`section-sec-HO3-binding-permissions-file`. The validity of this grant is restricted in time (2). This grant has a single allow rule (3), so *ParticipantAlice* will only be allowed to: * Join |DOMAIN| *1* * Publish the |TOPIC| "Example PatientMonitoring" — only in |DOMAIN| *1* *ParticipantAlice* will be denied attempts to perform any other action (4). In addition to configuring *allow* rules, we can specify *deny* rules with the opposite effect. For details, see the :link_connext_dds_secure_um:`Permissions Document in the RTI Security Plugins User's Manual `). .. note:: |PermissionsDocs| are always exchanged during authentication. The larger a |PermissionsDoc| is, the more network overhead it will cause. As such, we recommend that you keep separate |PermissionsDocs| per identity (i.e., per |DP|). For further details, see :link_connext_dds_secure_um:`Choosing the Granularity of Your Permissions Documents for DomainParticipants in the Security Plugins User's Manual `. .. _section-sec-HO3-binding-permissions-file: Binding the Permissions Document to Your DomainParticipants ----------------------------------------------------------- When a |DP| loads a |PermissionsDoc|, it looks for a grant with a :xmltag:`subject_name` matching its identity. In other words, the :xmltag:`subject_name` identifies the |DP| to which the grant's permissions apply. The contents of the :xmltag:`subject_name` tag should be the X.509 subject name for the |DP|, as given in the **Subject** field of its |IdentityCert|. If you followed the steps in :ref:`section-sec-HO1`, Alice's |IdentityCert| should be :file:`cert/ecdsa01/identities/ecdsa01Peer01Cert.pem`. The subject of Alice's certificate does not match the :xmltag:`subject_name` section in grant *ParticipantAlice*. We will modify :file:`pmiPermissionsAlice.xml` to make this grant apply to Alice. #. Check the information from Alice's |IdentityCert| (:file:`ecdsa01Peer01Cert.pem`) with the following command: .. tabs:: .. group-tab:: Linux .. code-block:: console :emphasize-lines: 12 $ openssl x509 -in cert/ecdsa01/identities/ecdsa01Peer01Cert.pem -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 7b:ba:b9:c9:2c:be:ee:b9:71:d0:62:4e:59:6b:de:89:3a:33:5c:ad Signature Algorithm: ecdsa-with-SHA256 Issuer: C=US, ST=CA, L=Santa Clara, O=Real Time Innovations, CN=RTI ECDSA01 (p256) ROOT CA, emailAddress=ecdsa01RootCa@rti.com Validity Not Before: Jan 28 21:26:58 2021 GMT Not After : Jan 27 21:26:58 2026 GMT Subject: C=US, ST=CA, O=Real Time Innovations, emailAddress=ecdsa01Peer01@rti.com, CN=RTI ECDSA01 (p256) PEER01 Subject Public Key Info: ... .. group-tab:: macOS .. code-block:: console :emphasize-lines: 12 $ openssl x509 -in cert/ecdsa01/identities/ecdsa01Peer01Cert.pem -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 7b:ba:b9:c9:2c:be:ee:b9:71:d0:62:4e:59:6b:de:89:3a:33:5c:ad Signature Algorithm: ecdsa-with-SHA256 Issuer: C=US, ST=CA, L=Santa Clara, O=Real Time Innovations, CN=RTI ECDSA01 (p256) ROOT CA, emailAddress=ecdsa01RootCa@rti.com Validity Not Before: Jan 28 21:26:58 2021 GMT Not After : Jan 27 21:26:58 2026 GMT Subject: C=US, ST=CA, O=Real Time Innovations, emailAddress=ecdsa01Peer01@rti.com, CN=RTI ECDSA01 (p256) PEER01 Subject Public Key Info: ... .. group-tab:: Windows .. code-block:: doscon :emphasize-lines: 12 > openssl x509 -in cert\ecdsa01\identities\ecdsa01Peer01Cert.pem -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 7b:ba:b9:c9:2c:be:ee:b9:71:d0:62:4e:59:6b:de:89:3a:33:5c:ad Signature Algorithm: ecdsa-with-SHA256 Issuer: C=US, ST=CA, L=Santa Clara, O=Real Time Innovations, CN=RTI ECDSA01 (p256) ROOT CA, emailAddress=ecdsa01RootCa@rti.com Validity Not Before: Jan 28 21:26:58 2021 GMT Not After : Jan 27 21:26:58 2026 GMT Subject: C=US, ST=CA, O=Real Time Innovations, emailAddress=ecdsa01Peer01@rti.com, CN=RTI ECDSA01 (p256) PEER01 Subject Public Key Info: ... #. Replace the :xmltag:`subject_name` in the |PermissionsDoc| (:file:`pmiPermissionsAlice.xml`) with the **Subject** field of the |IdentityCert| (:file:`ecdsa01Peer01Cert.pem`). |br| |br| #. You may also want to update the :xmltag:`validity` tag with the information from the |IdentityCert|. Note that you are not required to have the same validity dates in the |PermissionsDoc| and the |IdentityCert| (upon creation, your |DP| will independently verify that the |IdentityCert| and the grant in your |PermissionsDoc| are valid for the current date). If you decide to update the :xmltag:`validity` tag, pay attention to the date/time format. This is the result of updating Alice's grant in :file:`pmiPermissionsAlice.xml`: .. code-block:: xml :emphasize-lines: 6 ... C=US, ST=CA, O=Real Time Innovations, emailAddress=ecdsa01Peer01@rti.com, CN=RTI ECDSA01 (p256) PEER01 2019-11-15T20:24:34 2024-11-13T20:24:34 ... .. _section-sec-HO3-signing-permissions-files: Signing the Permissions Documents --------------------------------- We will use the provided Permissions CA's certificate and key to sign the |PermissionsDocs| that we composed. [#]_ Run the command below to create the signed |PermissionsDoc| (with PKCS#7 format) named :file:`xml/signed/signed_pmiPermissionsAlice.p7s`: .. tabs:: .. group-tab:: Linux .. code-block:: console $ openssl smime -sign -in xml/pmiPermissionsAlice.xml -text -out xml/signed/signed_pmiPermissionsAlice.p7s -signer cert/ecdsa01/ca/ecdsa01RootCaCert.pem -inkey cert/ecdsa01/ca/private/ecdsa01RootCaKey.pem .. group-tab:: macOS .. code-block:: console $ openssl smime -sign -in xml/pmiPermissionsAlice.xml -text -out xml/signed/signed_pmiPermissionsAlice.p7s -signer cert/ecdsa01/ca/ecdsa01RootCaCert.pem -inkey cert/ecdsa01/ca/private/ecdsa01RootCaKey.pem .. group-tab:: Windows .. code-block:: doscon > openssl smime -sign -in xml\pmiPermissionsAlice.xml -text -out xml\signed\signed_pmiPermissionsAlice.p7s -signer cert\ecdsa01\ca\ecdsa01RootCaCert.pem -inkey cert\ecdsa01\ca\private\ecdsa01RootCaKey.pem .. [#] In this example, we have control of the Permissions CA. This is not always the case, and we may be required to send the |PermissionsDoc| to an external entity to get it signed. .. _section-sec-HO3-updating-qos-profiles-your-project: Updating the QoS Profiles in Your Project ----------------------------------------- Update :file:`USER_QOS_PROFILES.xml` so that your |DPs| will load the new |PermissionsDoc|: .. code-block:: xml ... dds.sec.access.permissions file:./xml/signed/signed_pmiPermissionsAlice.p7s ... Here again, the ``file:`` prefix means that the signed |PermissionsDoc| will be loaded from the specified path in the file system. Note that the path is relative to the working directory from which you run your application (unless you specify an absolute path). For more details, see :link_connext_dds_secure_um:`DDS Security Properties for Configuring Access Control in the RTI Security Plugins User's Manual `. .. _section-sec-HO3-checking-that-new-permissions-applied: Checking that the New Permissions Are Applied --------------------------------------------- To verify that Alice is correctly loading her new permissions, we will do two tests. First, we will check that communication only works in |DOMAIN| *1*, as specified in the |PermissionsDoc|. Then we will make sure that Alice is only allowed to publish data. Communication Only Works in Domain 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #. Run your publisher and subscriber as explained in :ref:`section-sec-HO1-running-applications`. Do not provide any arguments to the applications, so that they will try to communicate in |DOMAIN| *0*. If the publisher has loaded the new permissions, communication should not occur. .. only:: cpp98 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_waiting :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_waiting :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp98_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp98_waiting :end-before: """ .. only:: cpp11 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_waiting :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_waiting :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp11_no_rule_found :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp11_waiting :end-before: """ #. Run your publisher and subscriber using |DOMAIN| *1* (specified with the :command:`-d` option in the command line). Now communication should succeed. .. only:: cpp98 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_d1_ok :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_d1_ok :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp98_d1_ok :end-before: """ .. only:: cpp11 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_d1_ok :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_d1_ok :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp11_d1_ok :end-before: """ Alice Is Only Allowed to Publish Data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We will now verify that a subscriber application with Alice's permissions is not able to receive data. This means that even if Alice becomes compromised, she can't listen for data she is not authorized to access. #. Temporarily change your subscriber's QoS profile to load the same security artifacts as Alice, including her identity and permissions: .. code-block:: xml This means we are now configuring Bob with Alice's permissions. |br| |br| #. Run your publisher and subscriber using |DOMAIN| *1*. Communication should not occur: .. only:: cpp98 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_d1_no_rule_found :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp98_d1_no_rule_found :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_publisher_cpp98_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp98_d1_no_rule_found :end-before: """ .. only:: cpp11 .. tabs:: .. group-tab:: Linux Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_d1_no_rule_found :end-before: """ .. group-tab:: macOS Publisher: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: console :start-after: snippet_console_subscriber_cpp11_d1_no_rule_found :end-before: """ .. group-tab:: Windows Publisher: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_console_publisher_cpp11_d1_ok :end-before: """ Subscriber: .. literalinclude:: snippets/snippets.py :language: doscon :start-after: snippet_doscon_subscriber_cpp11_d1_no_rule_found :end-before: """ This test requires your |GovernanceDoc| to set :xmltag:`enable_read_access_control` to :xmlval:`TRUE` (see :ref:`section-sec-HO2-further-exercises` in Hands-On 2); otherwise, endpoint-level permissions related to |DRs| will not be checked and Bob will be able to receive the data. When adding new rules to your |GovernanceDoc|, keep in mind (as we mentioned in :ref:`section-adding-topic-rule-PatientMonitoring`) that the |GovernanceDoc| is processed from top to bottom, and that only the first matching rule is applied. Also, make sure the Permissions CA signs the modified |GovernanceDoc| (see :ref:`section-sec-HO2-signing-governance-file`); otherwise the changes will not be applied. This exercise illustrates what happens if an application tries to do something it does not have permissions to do, by temporarily giving Bob (the subscriber) the wrong permissions. .. tip:: Make sure you revert the changes to your subscriber's QoS profile before continuing the exercises. .. _section-sec-HO3-further-exercises: Further Exercises ----------------- At this point, you have defined the permissions that will be applied to your publisher application, restricting it to publish *PatientMonitoring* data. However, we have not modified your subscriber's permissions. Define Different Permissions for Each Application in Your System ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To minimize the damage in case one of your secure applications is compromised, a |DP| should only have permissions to perform the actions that are necessary for its legitimate purpose. [#]_ #. Define the permissions for Bob in a file called :file:`pmiPermissionsBob.xml`. This |PermissionsDoc| should contain a single grant named *ParticipantBob*, defining permissions to subscribe to any |TOPIC| containing the string ``Patient`` — only in |DOMAIN| *1*. You may want to use the following :xmltag:`allow_rule` as a reference: .. code-block:: xml 1 *Patient* *ParticipantBob* should be denied any other actions. |br| |br| #. Bind Bob's permissions to your subscriber application (see :ref:`section-sec-HO3-binding-permissions-file`). If you completed the exercises in :ref:`section-sec-HO1`), Bob's identity certificate should be :file:`cert/ecdsa01/identities/ecdsa01Peer02Cert.pem`. You will have to bind Bob's permissions to the subject of this certificate. |br| |br| #. Make sure the Permissions CA signs the new |PermissionsDoc| and modify Bob's QoS profile to load it. After applying this configuration, your subscriber application, Bob, will only be allowed to subscribe to any |TOPIC| containing the string ``Patient`` in |DOMAIN| *1*, while your publisher application, Alice, will only be allowed to publish |TOPIC| "Example PatientMonitoring" in |DOMAIN| *1*. .. [#] This is known as the Principle of Least Privilege. For further information, see :link_connext_dds_secure_um:`Applying DDS Protection in the Security Plugins User's Manual `. .. [#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-``. .. _section-sec-HO3-troubleshooting: Troubleshooting --------------- * When I run the publisher/subscriber, I get this error: .. code-block:: text RTI_Security_PermissionsCfgFileParser_getGrantFromCertificate:XML file doesn't contain a grant for subject name Make sure your |DPs| load the correct |IdentityCert| and |PermissionsDoc|. Make sure the subject in your |PermissionsDoc|'s grant matches the information in your participant's identity certificate (see :ref:`section-sec-HO3-binding-permissions-file`). Pay attention to the format. |br| |br| * When I run the publisher/subscriber, I get this error: .. code-block:: text RTI_Security_AccessControl_create_participant:participant not allowed: no rule found for the participant's domainId; default DENY Make sure you run your applications in the right |DOMAIN|, by specifying the Domain ID with the :command:`-d` option in the command line. In this example, we are using |DOMAIN| *1*. |br| |br| * The subscriber is able to receive data, even if it does not have permissions to subscribe. Make sure your |GovernanceDoc| sets :xmltag:`enable_read_access_control` :xmlval:`TRUE`. Otherwise, permissions will not be enforced on locally created or remotely discovered |DRs|. You may be using the |GovernanceDoc| from :ref:`section-composing-governance-security-requirements`, with a specific topic rule appearing after the wildcard rule that applies to all topics. If your topic rule is more restrictive than the wildcard rule, it must be located first. The |GovernanceDoc| is processed from top to bottom, and the first matching rule is applied. |br| |br| * When I run the publisher/subscriber, I get an error I do not understand. See :link_connext_dds_secure_um:`Logging Messages in the RTI Security Plugins User's Manual `.