Topic · Cookbook

Security Plugin-Chain Cookbook

Sechs konkrete Rezepte: PKI-Trust-Anker mit selbst-signierter Root-CA, Permissions-XML für Multi-Topic-Setups, AES-GCM vs. AES-GMAC, AES-NI Hardware-Acceleration, Multi-Sink Audit-Logging, Pen-Test-Smoke gegen die Plugin-Chain. Spec-Anker und Plugin-Trait-Surface in der Security Plugin Reference.

6 Rezepte · 5 Plugin-Stufen Crates: 7 (siehe Crates-Map) Spec: DDS-Security 1.2

Vorab — Plugin-Chain aktivieren

DDS-Security ist opt-in. Im Default-Build ist die security-Feature aus; der Hot-Path läuft ohne Crypto-Overhead. Aktivierung passiert auf zwei Ebenen:

  • Build-Side: cargo build -p zerodds-dcps --features security baut die Plugin-Chain mit.
  • Runtime-Side: DomainParticipantQos bekommt einen PropertyQosPolicy-Block mit Cert/Permissions-Pfaden.

Reference statt Rezept? Die Security Plugin Reference dokumentiert alle 5 Trait-Surfaces (Authentication, AccessControl, Cryptographic, Logging, DataTagging) plus die 7 Implementations-Crates.

1 · PKI-Trust-Anker mit selbst-signierter Root-CA

Wann: Closed-Environment-Setups ohne externe CA (Lab, kleine Edge-Deployments). Ein selbst-signiertes Root-Cert dient als Trust-Anker, alle Participant-Certs hängen drunter.

# 1) Root-CA erzeugen (einmalig, gut sichern)
openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca-cert.pem \
    -sha256 -days 3650 -nodes \
    -subj "/CN=ZeroDDS Root CA/O=Acme/C=DE"

# 2) Participant-Cert ausstellen (pro Host/Service)
openssl req -newkey rsa:2048 -keyout part-key.pem -out part.csr \
    -nodes -subj "/CN=robot-arm-7/O=Acme/C=DE"
openssl x509 -req -in part.csr -CA ca-cert.pem -CAkey ca-key.pem \
    -CAcreateserial -out part-cert.pem -days 365 -sha256

ZeroDDS-Side (PropertyQosPolicy auf Participant):

let participant_qos = DomainParticipantQos {
    property: PropertyQosPolicy::new()
        .with("dds.sec.auth.identity_ca", "file:/etc/zerodds/ca-cert.pem")
        .with("dds.sec.auth.identity_certificate", "file:/etc/zerodds/part-cert.pem")
        .with("dds.sec.auth.private_key", "file:/etc/zerodds/part-key.pem"),
    ..Default::default()
};

Tieferes Material: PKI Governance & Cert-Lifecycle für CRL, Cert-Rotation, HSM-Integration.

2 · Permissions-XML für Multi-Topic-Setups

Wann: Mehrere Topics, mehrere Identities, klare Read/Write-Trennung (z.B. Telemetry: nur Robot darf publizieren, alle dürfen lesen; Setpoint: nur Operator darf publizieren).

<permissions>
  <grant name="robot_grant">
    <subject_name>CN=robot-arm-7,O=Acme,C=DE</subject_name>
    <validity>
      <not_before>2026-01-01T00:00:00</not_before>
      <not_after>2027-01-01T00:00:00</not_after>
    </validity>
    <allow_rule>
      <domains><id>0</id></domains>
      <publish><topics><topic>Telemetry</topic></topics></publish>
      <subscribe><topics><topic>Setpoint</topic></topics></subscribe>
    </allow_rule>
    <default>DENY</default>
  </grant>
</permissions>

CMS-Signatur (Pflicht):

# Permissions-XML mit der CA signieren
openssl cms -sign -in permissions.xml -text \
    -out permissions.p7s -outform PEM \
    -signer ca-cert.pem -inkey ca-key.pem -nocerts

ZeroDDS-Side: Property dds.sec.access.permissions = file:/etc/zerodds/permissions.p7s, plus dds.sec.access.governance für Domain-Level-Policies.

Pattern-Match: <topic>customer-*/odom</topic> erlaubt Glob — siehe AccessControl-Plugin Reference.

3 · AES-GCM vs. AES-GMAC — wann was

AES-GCM: Confidentiality + Integrity. Sample-Payload wird verschlüsselt. Höchstes Schutz-Niveau, aber höchster CPU-Aufwand.

AES-GMAC: Nur Integrity (MAC). Sample-Payload bleibt cleartext. Schützt vor Tampering, aber nicht vor Lauschern. ~3× schneller als GCM.

Konfig (Governance-XML, Topic-Level):

<topic_rule>
  <topic_expression>Telemetry</topic_expression>
  <enable_discovery_protection>true</enable_discovery_protection>
  <enable_liveliness_protection>true</enable_liveliness_protection>
  <enable_read_access_control>true</enable_read_access_control>
  <enable_write_access_control>true</enable_write_access_control>
  <metadata_protection_kind>SIGN</metadata_protection_kind>     <!-- GMAC -->
  <data_protection_kind>ENCRYPT</data_protection_kind>          <!-- GCM -->
</topic_rule>

Trade-Off: ENCRYPT auf hochfrequenten Topics kann den Hot-Path zum Bottleneck machen. Profile mit otel-tracing bevor Production.

4 · AES-NI Hardware-Acceleration

Wann: CPU-bound mit AES-GCM. AES-NI ist seit Intel Westmere (2010) + AMD Bulldozer (2011) im Mainstream — ist auf jedem aktuellen Server da.

ZeroDDS-Build:

# Default: AES-NI wird automatisch detected (cpuid)
cargo build -p zerodds-dcps --features security --release

# Explizit forcen (z.B. Cross-Compile):
RUSTFLAGS="-C target-feature=+aes,+pclmulqdq" cargo build ...

Verifikation: zerodds-security-crypto loggt beim Init die gewählte Backend-Variante (Software vs. AES-NI). Faktor 5-10× Speed-up bei AES-GCM 256.

ARM: ARMv8-Crypto-Extensions analog; +aes,+pmull als target-feature.

Code-Pfad: crates/security-crypto/src/aes_gcm_hw.rs

5 · Multi-Sink Audit-Logging

Wann: Compliance-Pflicht für Audit-Trail (CRA, ISO 27001). Du brauchst stderr für interaktive Inspection + JSONL für den Log-Aggregator + syslog für SIEM.

use zerodds_dcps::qos::DomainParticipantQos;
use zerodds_qos::PropertyQosPolicy;

// DDS-Security spec-style logger wireup: the participant carries dds.sec.log.*
// properties; the runtime builds a fan-out logger from them on create.
let qos = DomainParticipantQos {
    property: PropertyQosPolicy::new()
        .with("dds.sec.log.plugin", "stderr,jsonl")                  // fan out to both
        .with("dds.sec.log.level", "Notice")                        // min level
        .with("dds.sec.log.jsonl.path", "/var/log/zerodds-audit.jsonl"),
    ..Default::default()
};

▶ Runnable example: rust-security-plugin-chain

JSONL-Format: ein Event pro Zeile, alle DDS-Security-Events folgen §9.6.2.1.1: { "ts": "...", "severity": "...", "plugin_class": "AccessControl", "msg": "...", "subject": "...", "topic": "..." }.

SIEM-Integration: syslog-Facility LOG_AUTH; SIEM-Rule auf plugin_class=AccessControl AND severity≥Warning für Permission-Violations.

6 · Pen-Test-Smoke gegen die Plugin-Chain

Wann: Vor jedem Production-Release. Smoke-Test ob die Chain wirklich blockt was sie blocken soll.

Test-Matrix:

  • Authentication: Participant ohne Cert versucht zu joinen → muss abgelehnt werden (SECURITY_AUTHENTICATION_FAILED-Log-Event).
  • AccessControl: Identity X (kein Permissions-Grant für Topic Setpoint) versucht zu publizieren → DataWriter-Creation muss NotAllowedByAccessControl werfen.
  • Cryptographic: mit Wireshark + zerodds-DDS-Dissector mitsniffen — Sample-Payload muss verschlüsselt sein (kein Cleartext-IDL).
  • Logging: jeder Block-Event muss im Audit-Trail mit Severity ≥ Warning sein.
  • DataTagging: Reader mit level=PUBLIC-Filter darf level=SECRET-Sample nicht sehen.

Tools: zerodds-conformance hat einen Security-Conformance-Test-Suite; zerodds-chaos kann Crypto-Tamper-Faults injizieren.

Topic · Cookbook

Security Plugin-Chain Cookbook

Six concrete recipes: a PKI trust anchor with a self-signed root CA, permissions XML for multi-topic setups, AES-GCM vs. AES-GMAC, AES-NI hardware acceleration, multi-sink audit logging, pen-test smoke against the plugin chain. Spec anchors and the plugin trait surface are in the Security Plugin Reference.

6 recipes · 5 plugin stages Crates: 7 (see the crates map) Spec: DDS-Security 1.2

First — enabling the plugin chain

DDS-Security is opt-in. In the default build the security feature is off; the hot path runs without crypto overhead. Enabling happens on two levels:

  • Build side: cargo build -p zerodds-dcps --features security builds in the plugin chain.
  • Runtime side: DomainParticipantQos gets a PropertyQosPolicy block with cert/permissions paths.

Reference instead of recipe? The Security Plugin Reference documents all 5 trait surfaces (Authentication, AccessControl, Cryptographic, Logging, DataTagging) plus the 7 implementation crates.

1 · PKI trust anchor with a self-signed root CA

When: closed-environment setups without an external CA (lab, small edge deployments). A self-signed root cert serves as the trust anchor; all participant certs hang below it.

# 1) create the root CA (once, keep it safe)
openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca-cert.pem \
    -sha256 -days 3650 -nodes \
    -subj "/CN=ZeroDDS Root CA/O=Acme/C=DE"

# 2) issue a participant cert (per host/service)
openssl req -newkey rsa:2048 -keyout part-key.pem -out part.csr \
    -nodes -subj "/CN=robot-arm-7/O=Acme/C=DE"
openssl x509 -req -in part.csr -CA ca-cert.pem -CAkey ca-key.pem \
    -CAcreateserial -out part-cert.pem -days 365 -sha256

ZeroDDS side (PropertyQosPolicy on the participant):

let participant_qos = DomainParticipantQos {
    property: PropertyQosPolicy::new()
        .with("dds.sec.auth.identity_ca", "file:/etc/zerodds/ca-cert.pem")
        .with("dds.sec.auth.identity_certificate", "file:/etc/zerodds/part-cert.pem")
        .with("dds.sec.auth.private_key", "file:/etc/zerodds/part-key.pem"),
    ..Default::default()
};

Deeper material: PKI Governance & cert lifecycle for CRL, cert rotation, HSM integration.

2 · Permissions XML for multi-topic setups

When: multiple topics, multiple identities, a clear read/write split (e.g. Telemetry: only the robot may publish, everyone may read; Setpoint: only the operator may publish).

<permissions>
  <grant name="robot_grant">
    <subject_name>CN=robot-arm-7,O=Acme,C=DE</subject_name>
    <validity>
      <not_before>2026-01-01T00:00:00</not_before>
      <not_after>2027-01-01T00:00:00</not_after>
    </validity>
    <allow_rule>
      <domains><id>0</id></domains>
      <publish><topics><topic>Telemetry</topic></topics></publish>
      <subscribe><topics><topic>Setpoint</topic></topics></subscribe>
    </allow_rule>
    <default>DENY</default>
  </grant>
</permissions>

CMS signature (mandatory):

# sign the permissions XML with the CA
openssl cms -sign -in permissions.xml -text \
    -out permissions.p7s -outform PEM \
    -signer ca-cert.pem -inkey ca-key.pem -nocerts

ZeroDDS side: property dds.sec.access.permissions = file:/etc/zerodds/permissions.p7s, plus dds.sec.access.governance for domain-level policies.

Pattern match: <topic>customer-*/odom</topic> allows globs — see the AccessControl plugin reference.

3 · AES-GCM vs. AES-GMAC — when which

AES-GCM: confidentiality + integrity. The sample payload is encrypted. Highest protection level, but highest CPU cost.

AES-GMAC: integrity only (MAC). The sample payload stays cleartext. Protects against tampering, but not against eavesdroppers. ~3× faster than GCM.

Config (governance XML, topic level):

<topic_rule>
  <topic_expression>Telemetry</topic_expression>
  <enable_discovery_protection>true</enable_discovery_protection>
  <enable_liveliness_protection>true</enable_liveliness_protection>
  <enable_read_access_control>true</enable_read_access_control>
  <enable_write_access_control>true</enable_write_access_control>
  <metadata_protection_kind>SIGN</metadata_protection_kind>     <!-- GMAC -->
  <data_protection_kind>ENCRYPT</data_protection_kind>          <!-- GCM -->
</topic_rule>

Trade-off: ENCRYPT on high-frequency topics can make the hot path a bottleneck. Profile with otel tracing before production.

4 · AES-NI hardware acceleration

When: CPU-bound with AES-GCM. AES-NI has been mainstream since Intel Westmere (2010) + AMD Bulldozer (2011) — it's on every current server.

ZeroDDS build:

# default: AES-NI is auto-detected (cpuid)
cargo build -p zerodds-dcps --features security --release

# force explicitly (e.g. cross-compile):
RUSTFLAGS="-C target-feature=+aes,+pclmulqdq" cargo build ...

Verification: zerodds-security-crypto logs the chosen backend variant (software vs. AES-NI) at init. 5-10× speed-up for AES-GCM 256.

ARM: ARMv8 crypto extensions analogously; +aes,+pmull as the target feature.

Code path: crates/security-crypto/src/aes_gcm_hw.rs

5 · Multi-sink audit logging

When: compliance obligation for an audit trail (CRA, ISO 27001). You need stderr for interactive inspection + JSONL for the log aggregator + syslog for SIEM.

use zerodds_dcps::qos::DomainParticipantQos;
use zerodds_qos::PropertyQosPolicy;

// DDS-Security spec-style logger wireup: the participant carries dds.sec.log.*
// properties; the runtime builds a fan-out logger from them on create.
let qos = DomainParticipantQos {
    property: PropertyQosPolicy::new()
        .with("dds.sec.log.plugin", "stderr,jsonl")                  // fan out to both
        .with("dds.sec.log.level", "Notice")                        // min level
        .with("dds.sec.log.jsonl.path", "/var/log/zerodds-audit.jsonl"),
    ..Default::default()
};

▶ Runnable example: rust-security-plugin-chain

JSONL format: one event per line, all DDS-Security events follow §9.6.2.1.1: { "ts": "...", "severity": "...", "plugin_class": "AccessControl", "msg": "...", "subject": "...", "topic": "..." }.

SIEM integration: syslog facility LOG_AUTH; a SIEM rule on plugin_class=AccessControl AND severity≥Warning for permission violations.

6 · Pen-test smoke against the plugin chain

When: before every production release. A smoke test of whether the chain really blocks what it should block.

Test matrix:

  • Authentication: a participant without a cert tries to join → must be rejected (SECURITY_AUTHENTICATION_FAILED log event).
  • AccessControl: identity X (no permissions grant for topic Setpoint) tries to publish → DataWriter creation must throw NotAllowedByAccessControl.
  • Cryptographic: sniff along with Wireshark + the zerodds DDS dissector — the sample payload must be encrypted (no cleartext IDL).
  • Logging: every block event must be in the audit trail at severity ≥ Warning.
  • DataTagging: a reader with a level=PUBLIC filter must not see a level=SECRET sample.

Tools: zerodds-conformance has a security conformance test suite; zerodds-chaos can inject crypto tamper faults.