Skip to content

Commit f25c713

Browse files
committed
Added SecureSystemObserver snippet. Added ability to generate secure-apps qos with fully resolved absolute paths. Improved logging of setup_security scrip when skipping files.
1 parent 0fd3f8b commit f25c713

10 files changed

Lines changed: 393 additions & 17 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ This reference architecture defines the following DomainParticipants in [Partici
310310
| OperationalDataDomain | Orchestrator | `t/DeviceStatus`, `t/DeviceHeartbeat` | `t/DeviceCommand` | Administer device-level commands and monitor presence and status of all devices.
311311
| OperationalDataDomain | PatientSensor | `t/DeviceCommand` | `t/Vitals`, `t/DeviceStatus`, `t/DeviceHeartbeat` | Stream simulated patient vitals.
312312
| OperationalDataDomain | PatientMonitor | `t/DeviceCommand`, `t/Vitals` | `t/DeviceStatus`, `t/DeviceHeartbeat` | Process and display patient vitals.
313+
| OperationalDataDomain | SystemObserver (external/optional) | All available topics | -- | Read-only observer integration: subscribes to every operational Topic and publishes nothing. Its DDS Security permissions allow subscribe on any Topic and deny all publication.
313314
| SecureLogDomain | SecureLogReader | `DDS:Security:LogTopicV2` | -- | Subscribe to the DDS Security builtin secure-log topic.
314315

315316
*Note, this reference architecture utilizes one DomainParticipant for each device application. It is a **best practice** to define one DomainParticipant per application. However, in more complex systems, an application may be required to operate on multiple Domains. This requires defining multiple DomainParticipants for those applications that run in parallel.*
@@ -328,6 +329,10 @@ The reference architecture configures security in [SecureAppsQos.xml](./system_a
328329
| **WAN Communications** | `TeleopWanDomain` governance for WAN connections (Module 03), including PSK-protected RTPS
329330
| **RTI Services** | Dedicated security profiles for Recording/Replay Services and Routing Services
330331

332+
For independent, security-specific observer integrations (not part of the demo applications), use [SecureExternalAppsQos.xml](./system_arch/qos/SecureExternalAppsQos.xml). It provides the `SecureExternalAppsQosLib::SecureSystemObserver` QoS snippet, which is intended to be composed into external DomainParticipants.
333+
334+
This external snippet is ideal for Connext Studio when configuring an RTI Spy source: include [SecureExternalAppsQos.xml](./system_arch/qos/SecureExternalAppsQos.xml) and apply `SecureSystemObserver` so Spy can attach as a read-only secure observer.
335+
331336
Security Artifacts Structure in [security](./system_arch/security/):
332337

333338
- `ca/` - Certificate Authority hierarchy (root CA → intermediate identity CA + intermediate permissions CA)

system_arch/qos/README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This README describes how we've approached QoS in this reference architecture. F
1818
- [DataFlowLibrary::Heartbeat profile](#dataflowlibraryheartbeat-profile)
1919
- [DataFlowLibrary::SecureLog profile](#dataflowlibrarysecurelog-profile)
2020
- [Application-specific QoS: NonSecureAppsQos.xml and SecureAppsQos.xml](#application-specific-qos-nonsecureappsqosxml-and-secureappsqosxml)
21+
- [External security snippets: SecureExternalAppsQos.xml](#external-security-snippets-secureexternalappsqosxml)
2122
- [XML QoS Best Practices](#xml-qos-best-practices)
2223

2324
## QoS Profile Configuration
@@ -177,16 +178,56 @@ Both files contain only 1 QoS library: ***DpQosLib***. This QoS library contains
177178

178179
[NonSecureAppsQos.xml](./NonSecureAppsQos.xml) contains one profile for each DomainParticipant. For the simplified demonstration, each profile inherits from *SystemLibrary::DefaultParticipant* in [Qos.xml](./Qos.xml). No additional configuration is applied for any given DomainParticipant.
179180

180-
[SecureAppsQos.xml](./SecureAppsQos.xml) also defines one profile for each DomainParticipant in a similar way to that of **NonSecureAppsQos.xml**, but with security configuration added.
181+
[SecureAppsQos.xml](./SecureAppsQos.xml) defines secure profiles for the demo DomainParticipants and services in a similar way to **NonSecureAppsQos.xml**, but with security configuration added.
181182

182183
[SecureAppsQos.xml](./SecureAppsQos.xml) defines a QoS snippet - *LanCommonSecurityConfig* defines common configuration to enable security for local domains (LAN connections). It references common permissions CA, identity CA, and governance files.
183184

184185
[SecureAppsQos.xml](./SecureAppsQos.xml) defines a QoS snippet - *WanCommonSecurityConfig* defines common configuration to enable security for remote domains (WAN connections). It references common permissions CA, identity CA, and governance files.
185186

187+
## External security snippets: [SecureExternalAppsQos.xml](SecureExternalAppsQos.xml)
188+
189+
[SecureExternalAppsQos.xml](./SecureExternalAppsQos.xml) provides standalone QoS snippets for participants that are **not** part of the demo applications themselves. Unlike the profiles in [SecureAppsQos.xml](./SecureAppsQos.xml), these are independent, Security-specific configurations meant to plug external observers into the secured system.
190+
191+
The file currently defines one snippet:
192+
193+
- **`SecureExternalAppsQosLib::SecureSystemObserver`** — a reusable [QoS Snippet](https://community.rti.com/best-practices/qos-profile-inheritance-and-composition-guidance#h.wr6u1ebybeff) that encapsulates the complete DDS Security property set for a read-only System Observer:
194+
- Composes `BuiltinQosSnippetLib::Feature.Security.Enable` to activate the Security Plugins
195+
- Permissions CA and Identity CA trust anchors
196+
- Operational domain governance
197+
- SystemObserver identity certificate and private key
198+
- SystemObserver signed permissions document
199+
- Secure logging disabled (`mode_mask=BUILTIN`, `verbosity=SILENT`) because the observer has no publish permissions
200+
201+
### Usage
202+
203+
Compose the snippet into any DomainParticipant QoS using the `<base_name>` element:
204+
205+
```xml
206+
<domain_participant_qos>
207+
<base_name>
208+
<element>SecureExternalAppsQosLib::SecureSystemObserver</element>
209+
</base_name>
210+
</domain_participant_qos>
211+
```
212+
213+
### Connext Studio (Spy source)
214+
215+
This configuration is ideal for use with **RTI Connext Studio**. To observe the secured domain with the Spy data source:
216+
217+
1. From the repository root, generate resolved QoS files with absolute security-artifact paths:
218+
219+
```bash
220+
python3 system_arch/security/setup_security.py --generate-resolved-qos
221+
```
222+
223+
2. In Connext Studio, add a Spy Source and configure the source with the configuration under
224+
`SecureExternalAppsQosLib::SecureSystemObserver` snippet from the `system_arch/security/resolved_qos/SecureExternalAppsQos.xml`.
225+
3. Spy will join the secured Operational Domain as a read-only observer — able to subscribe to all Topics without publish permissions.
226+
186227
## XML QoS Best Practices
187228

188229
>**Best Practice:** Compose your QoS profiles with [QoS Snippets](https://community.rti.com/best-practices/qos-profile-inheritance-and-composition-guidance#h.wr6u1ebybeff). Snippets provide easier readability and result in more maintainable QoS for increasingly complex systems by reducing repetitive configuration.
189230
>
190231
>**Best Practice:** Inherit from [Built-in QoS Profiles](https://community.rti.com/static/documentation/connext-dds/7.7.0/doc/manuals/connext_dds_professional/users_manual/users_manual/Built_in_QoS_Profiles.htm). Builtin profiles provide starting points to frequently used and tuned QoS combinations.
191232
192-
Please take a look at the comments inside the profiles in [Qos.xml](./Qos.xml), [NonSecureAppsQos.xml](./NonSecureAppsQos.xml), and [SecureAppsQos.xml](./SecureAppsQos.xml) for further details on each QoS policy and more **best practices** related to QoS configuration.
233+
Please take a look at the comments inside the profiles and snippets in [Qos.xml](./Qos.xml), [NonSecureAppsQos.xml](./NonSecureAppsQos.xml), [SecureAppsQos.xml](./SecureAppsQos.xml), and [SecureExternalAppsQos.xml](./SecureExternalAppsQos.xml) for further details on each QoS policy and more **best practices** related to QoS configuration.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- (c) 2024 Copyright, Real-Time Innovations, Inc. (RTI) All rights reserved.
3+
4+
RTI grants Licensee a license to use, modify, compile, and create derivative
5+
works of the software solely for use with RTI Connext DDS. Licensee may
6+
redistribute copies of the software provided that all such copies are
7+
subject to this license. The software is provided "as is", with no warranty
8+
of any type, including any warranty for fitness for any purpose. RTI is
9+
under no obligation to maintain or support the software. RTI shall not be
10+
liable for any incidental or consequential damages arising out of the use or
11+
inability to use the software. -->
12+
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.7.0/rti_dds_profiles.xsd" version="7.7.0">
13+
14+
<configuration_variables>
15+
<value>
16+
<element>
17+
<!-- Path to security folder to find artifacts.
18+
This path is relative to the "current working directory" when
19+
running the application (i.e. from the `modules` folder).
20+
It can be overridden by setting the RTI_SECURITY_ARTIFACTS_DIR
21+
environment variable.
22+
-->
23+
<name>RTI_SECURITY_ARTIFACTS_DIR</name>
24+
<value>../../system_arch/security</value>
25+
</element>
26+
</value>
27+
</configuration_variables>
28+
29+
<qos_library name="SecureExternalAppsQosLib">
30+
<!-- Snippet: SecureSystemObserver
31+
Provides full DDS Security configuration for a read-only System Observer.
32+
This is not a runnable profile on its own; compose it into a
33+
DomainParticipant QoS (e.g. in Connext Studio's Spy source) via:
34+
<base_name>
35+
<element>SecureExternalAppsQosLib::SecureSystemObserver</element>
36+
</base_name>
37+
-->
38+
<qos_profile name="SecureSystemObserver">
39+
<?rti-qos_snippet?>
40+
<domain_participant_qos>
41+
<base_name>
42+
<element>BuiltinQosSnippetLib::Feature.Security.Enable</element>
43+
</base_name>
44+
<property>
45+
<value>
46+
<!-- Permissions CA: validates S/MIME signatures on governance and permissions XMLs.
47+
Points to the intermediate Permissions CA that signs these documents. -->
48+
<element>
49+
<name>dds.sec.access.permissions_ca</name>
50+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/ca/TrustedPermissionsCa/certs/TrustedRootCa/TrustedPermissionsCa.crt</value>
51+
</element>
52+
<!-- Identity CA: validates identity certificate chains.
53+
Points to the intermediate Identity CA that signs participant certs. -->
54+
<element>
55+
<name>dds.sec.auth.identity_ca</name>
56+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/ca/TrustedIdentityCa/certs/TrustedRootCa/TrustedIdentityCa.crt</value>
57+
</element>
58+
<element>
59+
<name>dds.sec.access.governance</name>
60+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/domain_scope/OperationalDomain/governance/OperationalDomain/signed/TrustedPermissionsCa/OperationalDomain.p7s</value>
61+
</element>
62+
<element>
63+
<name>dds.sec.auth.identity_certificate</name>
64+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/identity/operating-room/SystemObserver/SystemObserver/certs/TrustedIdentityCa/SystemObserver.chain.pem</value>
65+
</element>
66+
<element>
67+
<name>dds.sec.auth.private_key</name>
68+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/identity/operating-room/SystemObserver/SystemObserver/private/SystemObserver.key</value>
69+
</element>
70+
<element>
71+
<name>dds.sec.access.permissions</name>
72+
<value>file:$(RTI_SECURITY_ARTIFACTS_DIR)/domain_scope/OperationalDomain/permissions/SystemObserver/signed/TrustedPermissionsCa/SystemObserver.p7s</value>
73+
</element>
74+
<!-- Read-only observer: it has no publish permissions, so it must not
75+
emit to the distributed secure-log topic. Disable secure logging. -->
76+
<element>
77+
<name>com.rti.serv.secure.logging.mode_mask</name>
78+
<value>BUILTIN</value>
79+
</element>
80+
<element>
81+
<name>com.rti.serv.secure.logging.verbosity</name>
82+
<value>SILENT</value>
83+
</element>
84+
</value>
85+
</property>
86+
</domain_participant_qos>
87+
</qos_profile>
88+
</qos_library>
89+
90+
</dds>

system_arch/security/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ identity/**/certs/
3131
# Scaffold hash sidecar
3232
# ---------------------------------------------------------------------------
3333
.scaffold-hash
34+
35+
# ---------------------------------------------------------------------------
36+
# Resolved QoS files (generated by --generate-resolved-qos)
37+
# ---------------------------------------------------------------------------
38+
resolved_qos/

system_arch/security/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Certificates expiring within 30 days are flagged as warnings. Use `--warn-days N
4747
| `--status` | Report certificate expiry status and exit |
4848
| `--warn-days N` | Days-to-expiry warning threshold for `--status` (default: 30) |
4949
| `--connext-version X.Y.Z` | Override auto-detected Connext version |
50+
| `--generate-resolved-qos` | Generate resolved QoS XML files in `system_arch/security/resolved_qos/` with absolute security artifact paths; skips existing files unless `--force` is set |
5051

5152
## Directory Layout
5253

@@ -82,7 +83,7 @@ system_arch/security/
8283
- **CA hierarchy:** A self-signed root CA (`TrustedRootCa`) issues two intermediate CAs — one for identity certificates (`TrustedIdentityCa`) and one for permissions/governance signing (`TrustedPermissionsCa`).
8384
- **Chain files:** Identity certificates include a `.chain.pem` containing both the leaf cert and its issuing CA cert, as required by the RTI Security Plugins.
8485
- **Signed XML:** Governance and permissions XML files are S/MIME-signed by the appropriate intermediate CA. The signed `.p7s` files are what Connext loads at runtime.
85-
- **Per-participant permissions:** Each participant has its own permissions document specifying the exact topics it may publish/subscribe to, with a default `DENY` rule.
86+
- **Per-participant permissions:** Each participant has its own permissions document specifying the exact topics it may publish/subscribe to, with a default `DENY` rule. For example, the `SystemObserver` participant grants `subscribe` on any topic and no `publish` rule at all — a least-privilege, read-only observer that can watch the full data flow but can never write to the bus.
8687
- **PSK passphrases:** Pre-Shared Key seed files (`.psk`) are generated per domain scope and stored alongside the governance/permissions artifacts (e.g. `domain_scope/TeleopWanDomain/TeleopWanDomain.psk`). The file format is `<id>:<seed>` where `<id>` is an integer in [0, 254]. Participants load the passphrase via the `dds.sec.crypto.rtps_psk_secret_passphrase` property.
8788

8889
## Good Practices for DDS Security

system_arch/security/dds_security.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ def generate_root_ca(
438438
) -> Path:
439439
"""(b) Generate a root CA private key and self-signed certificate."""
440440
if out_cert.is_file() and not force:
441-
log.info("Root CA cert exists, skipping: %s", out_cert)
441+
log.warning("Root CA cert already exists, skipping: %s — remove the file or use --force to regenerate", out_cert)
442442
return out_cert
443443
generate_key(key_path)
444444
return self_sign(key_path, cnf, out_cert, days=days)
@@ -473,7 +473,7 @@ def generate_intermediate_ca(
473473
) -> Path:
474474
"""(d-f) Generate an intermediate CA: private key, CSR, and signed certificate."""
475475
if out_cert.is_file() and not force:
476-
log.info("Intermediate CA cert exists, skipping: %s", out_cert)
476+
log.warning("Intermediate CA cert already exists, skipping: %s — remove the file or use --force to regenerate", out_cert)
477477
return out_cert
478478
generate_key(key_path)
479479
csr = out_cert.with_suffix(".csr")
@@ -528,7 +528,7 @@ def generate_identity(
528528
intermediate (not directly the root).
529529
"""
530530
if out_cert.is_file() and not force:
531-
log.info("Identity cert exists, skipping: %s", out_cert)
531+
log.warning("Identity cert already exists, skipping: %s — remove the file or use --force to regenerate", out_cert)
532532
return out_cert
533533
generate_key(key_path)
534534
csr = out_cert.with_suffix(".csr")
@@ -571,7 +571,7 @@ def generate_expired_identity(
571571
creation time.
572572
"""
573573
if out_cert.is_file() and not force:
574-
log.info("Expired identity cert exists, skipping: %s", out_cert)
574+
log.warning("Expired identity cert already exists, skipping: %s — remove the file or use --force to regenerate", out_cert)
575575
return out_cert
576576
generate_key(key_path)
577577
csr = out_cert.with_suffix(".csr")
@@ -634,7 +634,7 @@ def sign_governance(
634634
) -> Path:
635635
"""(m) Sign a governance XML with S/MIME v3.2."""
636636
if out_p7s.is_file() and not force:
637-
log.info("Signed governance exists, skipping: %s", out_p7s)
637+
log.warning("Signed governance already exists, skipping: %s — remove the file or use --force to regenerate", out_p7s)
638638
return out_p7s
639639
return sign_xml(key_path, cert_path, xml_path, out_p7s)
640640

@@ -654,7 +654,7 @@ def sign_permissions(
654654
) -> Path:
655655
"""(o) Sign a permissions XML with S/MIME v3.2."""
656656
if out_p7s.is_file() and not force:
657-
log.info("Signed permissions exists, skipping: %s", out_p7s)
657+
log.warning("Signed permissions already exists, skipping: %s — remove the file or use --force to regenerate", out_p7s)
658658
return out_p7s
659659
return sign_xml(key_path, cert_path, xml_path, out_p7s)
660660

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- (c) 2024 Copyright, Real-Time Innovations, Inc. (RTI) All rights reserved.
3+
4+
RTI grants Licensee a license to use, modify, compile, and create derivative
5+
works of the software solely for use with RTI Connext DDS. Licensee may
6+
redistribute copies of the software provided that all such copies are
7+
subject to this license. The software is provided "as is", with no warranty
8+
of any type, including any warranty for fitness for any purpose. RTI is
9+
under no obligation to maintain or support the software. RTI shall not be
10+
liable for any incidental or consequential damages arising out of the use or
11+
inability to use the software. -->
12+
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
13+
xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.7.0/dds_security_permissions.xsd">
14+
<permissions>
15+
<grant name="SystemObserverParticipant">
16+
<subject_name>/C=US/ST=CA/O=Company Name/emailAddress=systemobserver@company_name.com/CN=SystemObserver</subject_name>
17+
<validity>
18+
<!-- Format is CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] in GMT -->
19+
<not_before>2024-06-01T13:00:00</not_before>
20+
<not_after>2037-06-01T13:00:00</not_after>
21+
</validity>
22+
<!-- Read-only observer: may subscribe to ANY topic on ANY partition,
23+
and may NOT publish anything. The default DENY rule blocks all
24+
publication (no <publish> rule is granted). -->
25+
<allow_rule>
26+
<domains>
27+
<id>0</id>
28+
</domains>
29+
<partitions>
30+
<partition>*</partition>
31+
</partitions>
32+
<subscribe>
33+
<partitions>
34+
<partition>*</partition>
35+
</partitions>
36+
<topics>
37+
<topic>*</topic>
38+
</topics>
39+
</subscribe>
40+
</allow_rule>
41+
<default>DENY</default>
42+
</grant>
43+
</permissions>
44+
</dds>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[ req ]
2+
prompt = no
3+
distinguished_name = req_distinguished_name
4+
5+
[ req_distinguished_name ]
6+
countryName = US
7+
stateOrProvinceName = CA
8+
organizationName = Company Name
9+
emailAddress = systemobserver@company_name.com
10+
commonName = SystemObserver
11+
12+
[ usr_cert ]
13+
# End-entity certificate extensions
14+
basicConstraints = critical, CA:false
15+
subjectKeyIdentifier = hash
16+
authorityKeyIdentifier = keyid,issuer
17+
keyUsage = critical, digitalSignature, keyEncipherment

0 commit comments

Comments
 (0)