DN Rallypoint Additional Identities - rallytac/pub GitHub Wiki

Developer Note: Rallypoint Additional Identities and SNI

A Rallypoint normally presents a single TLS server identity—the certificate / key pair in rallypointd_conf.json. The additionalIdentities array lets a Rallypoint load extra named certificate/key pairs and select among them at TLS handshake time using Server Name Indication (SNI).

This is useful when:

  • Multiple logical domains (in the operational sense—France, Germany, tenant A, tenant B, and so on) share one Rallypoint IP/port or sit behind a load balancer.
  • Different clients or peer Rallypoints must authenticate against different server certificates (different CAs, subject DNs, or policy boundaries) while dialing the same host:port.
  • You want domain-specific server certs without running a separate rallypointd process per domain.

For general Rallypoint installation, meshing, and the full configuration schema, see Engage Rallypoints. For certificate stores and CA management, see Engage Security and Using ecstool.


How It Works

At startup, rallypointd loads:

  1. The primary identity from certificate (internally keyed as *—the default when no SNI is sent or no name matches).
  2. Each entry in additionalIdentities, keyed by its name (stored case-insensitively).

When a TLS client connects, OpenSSL invokes an SNI callback. If the client sent an SNI string that matches a loaded identity name, that identity's certificate is presented. Otherwise the primary certificate is used.

Outbound connections—Engage Engine clients and peer Rallypoints—supply the SNI string via the sni field on their Rallypoint connection object. The Rallypoint sets SSL_set_tlsext_host_name() with that value during the TLS ClientHello.

Important: SNI is sent in the clear in the TLS ClientHello. Treat sni as a certificate-selector label, not a secret. It does not replace mutual X.509 authentication or group encryption.

Startup behavior: If any additionalIdentities entry fails to load, the Rallypoint aborts at startup. Validate all referenced certs and keys before deploying.


Two Different "Domain" Concepts

Rallypoints use the word domain in two places. They are often configured together in multi-domain deployments, but they are not the same mechanism.

Concept Configuration Purpose
TLS identity selection additionalIdentities[].name + outbound sni Choose which server certificate the Rallypoint presents during TLS
Logical routing domain domainName, allowedDomains, blockedDomains, extraDomains Control which peer Rallypoints may connect when domain enforcement is enabled; advertise reachable domains in peer hello and mDNS
  • domainName empty ⇒ domain checks are off; any peer may connect regardless of the domain it declares.
  • domainName set ⇒ connecting peers must declare a domain in the Rallypoint hello handshake, and that domain must appear in allowedDomains (your own domainName is included automatically unless blocked).

additionalIdentities names and domainName values do not have to match, but using the same label (for example FRANCE) in both places is a common convention and makes operations easier to reason about.


Rallypoint Server Configuration

Each additionalIdentities element is a NamedIdentity:

  • name — label sent as TLS SNI by clients/peers that want this identity. Matching is case-insensitive (FRANCE, france, and France are equivalent).
  • certificate — SecurityCertificate object with certificate and key (PEM text, @/path/to/file, or @certstore://elementName).

Minimal Example

Primary cert for default connections; two extra identities for domain-specific certs:

{
   "id": "core-rp",
   "listenPort": 7443,

   "certificate": {
      "certificate": "@certstore://coreRpDefault",
      "key": "@certstore://coreRpDefault"
   },

   "tls": {
      "verifyPeers": true,
      "caCertificates": [ "@certstore://rtsCA" ]
   },

   "additionalIdentities": [
      {
         "name": "FRANCE",
         "certificate": {
            "certificate": "@certstore://franceRpSrv",
            "key": "@certstore://franceRpSrv"
         }
      },
      {
         "name": "GERMANY",
         "certificate": {
            "certificate": "@certstore://germanyRpSrv",
            "key": "@certstore://germanyRpSrv"
         }
      }
   ]
}

Clients that omit sni (or send a name that does not match) receive coreRpDefault. Clients or peers that send sni FRANCE receive franceRpSrv.

File-path references work the same way as the primary cert:

{
   "name": "france",
   "certificate": {
      "certificate": "@./france.cert",
      "key": "@./france.key"
   }
}

Multi-Domain Server with Logical Domain Enforcement

When peer Rallypoints also use domainName, combine TLS identities with domain policy on the same Rallypoint or across a mesh:

{
   "id": "core-rp",
   "domainName": "CORE",
   "allowedDomains": [ "FRANCE", "GERMANY" ],
   "extraDomains": [ "FRANCE", "GERMANY" ],

   "certificate": {
      "certificate": "@certstore://coreRpDefault",
      "key": "@certstore://coreRpDefault"
   },

   "additionalIdentities": [
      {
         "name": "FRANCE",
         "certificate": {
            "certificate": "@certstore://franceRpSrv",
            "key": "@certstore://franceRpSrv"
         }
      },
      {
         "name": "GERMANY",
         "certificate": {
            "certificate": "@certstore://germanyRpSrv",
            "key": "@certstore://germanyRpSrv"
         }
      }
   ]
}

A leaf Rallypoint in the France operational domain might be configured as:

{
   "id": "france-leaf",
   "domainName": "FRANCE",
   "isMeshLeaf": true,

   "certificate": {
      "certificate": "@certstore://franceLeafSrv",
      "key": "@certstore://franceLeafSrv"
   },

   "peeringConfigurationFileName": "/etc/rallypointd/peers.json"
}

The leaf's peers.json dials the core using SNI to select the France server cert on the shared core address (see Peering below).


Engage Engine Configuration

Engage Engine-powered applications (mobile apps, gateways, Engage Bridge Service, engage-cmd, and so on) attach to Rallypoints through the Rallypoint JSON object. Set sni on that object to request a non-default server identity.

Per-group rallypoints is the most common pattern—each group can use a different Rallypoint connection, including a different sni:

{
   "groups": [
      {
         "id": "{a1b2c3d4-e5f6-7890-abcd-ef1234567890}",
         "name": "France Ops",
         "type": 1,
         "cryptoPassword": "62022266D3E22F85B7E23E66EA6C639C0FBD96C37FEF40DD51A06E2119882F44",
         "rallypoints": [
            {
               "host": {
                  "address": "rp.example.com",
                  "port": 7443
               },
               "sni": "FRANCE",
               "verifyPeer": true,
               "caCertificates": [ "@certstore://franceCA" ],
               "certificate": "@certstore://myClientCert",
               "certificateKey": "@certstore://myClientCert"
            }
         ]
      },

      {
         "id": "{b2c3d4e5-f6a7-8901-bcde-f23456789012}",
         "name": "Germany Ops",
         "type": 1,
         "cryptoPassword": "62022266D3E22F85B7E23E66EA6C639C0FBD96C37FEF40DD51A06E2119882F44",
         "rallypoints": [
            {
               "host": {
                  "address": "rp.example.com",
                  "port": 7443
               },
               "sni": "GERMANY",
               "verifyPeer": true,
               "caCertificates": [ "@certstore://germanyCA" ],
               "certificate": "@certstore://myClientCert",
               "certificateKey": "@certstore://myClientCert"
            }
         ]
      }
   ]
}

Both groups dial the same rp.example.com:7443. TLS SNI selects which server certificate the Rallypoint presents. Each group's caCertificates should include the CA that signed the identity being selected—France clients trust franceCA, Germany clients trust germanyCA.

Rallypoint Cluster (Failover / Multi-RP)

When using rallypointCluster instead of a flat rallypoints array, each cluster member is a full Rallypoint object and may carry its own sni:

{
   "id": "{group-id}",
   "name": "France Ops with failover",
   "type": 1,
   "cryptoPassword": "...",
   "rallypointCluster": {
      "connectionStrategy": 0,
      "rolloverSecs": 10,
      "rallypoints": [
         {
            "host": { "address": "rp-primary.example.com", "port": 7443 },
            "sni": "FRANCE",
            "verifyPeer": true,
            "caCertificates": [ "@certstore://franceCA" ]
         },
         {
            "host": { "address": "rp-secondary.example.com", "port": 7443 },
            "sni": "FRANCE",
            "verifyPeer": true,
            "caCertificates": [ "@certstore://franceCA" ]
         }
      ]
   }
}

If rallypointCluster.rallypoints is non-empty, it takes precedence over a sibling rallypoints array on the same group.

Engage Bridge Service (EBS)

EBS group definitions use the same rallypoints / rallypointCluster shapes. A bridged raw group that reaches a domain-specific Rallypoint identity:

{
   "id": "france-bridge-group",
   "name": "France bridge leg",
   "type": 3,
   "cryptoPassword": "...",
   "rallypoints": [
      {
         "host": { "address": "rp.example.com", "port": 7443 },
         "sni": "FRANCE",
         "verifyPeer": true,
         "caCertificates": [ "@certstore://franceCA" ],
         "certificate": "@certstore://ebsClientCert",
         "certificateKey": "@certstore://ebsClientCert"
      }
   ]
}

See Engage Bridging Service for broader EBS configuration patterns.

WebSocket Rallypoint Connections

If the group uses protocol 1 (rppTlsWs—WebSocket over TLS), sni behaves the same way on the underlying TLS connection. The Rallypoint's WebSocket listener currently uses a single identity (websocket.certificate); additionalIdentities apply to the primary listenPort TLS listener.


Rallypoint Peering

Peer Rallypoints in a mesh dial each other using the peers.json format documented in Engage Rallypoints — Meshing. Add sni to a peer entry when the far-end Rallypoint exposes multiple server identities on the same host:port.

Single Peer, Domain-Specific SNI

france-leaf peers into core-rp at 172.16.10.10:7443 and requests the France server identity:

{
   "peers": [
      {
         "id": "core-rp",
         "enabled": true,
         "host": {
            "address": "172.16.10.10",
            "port": 7443
         },
         "sni": "FRANCE"
      }
   ]
}

On core-rp, additionalIdentities must include an entry with name FRANCE (or france—case does not matter). The peer link comes up with mutual TLS using core-rp's France server cert.

Switching Identities on the Same Peer

Changing sni in peers.json and reloading peering configuration (or waiting for the periodic file check) causes the Rallypoint to tear down and re-establish the peer connection with the new SNI. This is useful when testing or when repointing a leaf at a different logical domain on a shared core.

Example progression on germany-leaf:

  1. "sni": "FRANCE" ⇒ peer link uses France server cert on core-rp.
  2. Change to "sni": "GERMANY" ⇒ germany-leaf reconnects; core-rp presents the Germany identity.

Watch rallypointd logs for lines like:

adding peer connection [core-rp @ '172.16.10.10':7443] sni='FRANCE' with certificate
SSL_set_tlsext_host_name() succeeded for 172.16.10.10:7443 regarding sni of [FRANCE]

Mesh with Per-Domain Leaf Rallypoints

Typical layout: a core mesh RP (or cluster behind a load balancer) with additionalIdentities for each operational domain; leaf RPs per site/domain peer into the core.

                    +------------------+
                    |   Load Balancer  |
                    | rp.example.com   |
                    +--------+---------+
                             |
              +--------------+--------------+
              |         core-rp             |
              |  additionalIdentities:      |
              |    FRANCE, GERMANY, ...     |
              +--------------+--------------+
                    ^               ^
         sni=FRANCE |               | sni=GERMANY
                    |               |
            +-------+----+   +------+-------+
            | france-leaf|   | germany-leaf |
            | domainName |   | domainName   |
            |  FRANCE    |   |  GERMANY     |
            +------------+   +--------------+
                 ^                  ^
            local clients      local clients

Core rallypointd_conf.json (abbreviated):

{
   "id": "core-rp",
   "domainName": "CORE",
   "allowedDomains": [ "FRANCE", "GERMANY" ],

   "certificate": { "certificate": "@certstore://coreDefault", "key": "@certstore://coreDefault" },

   "additionalIdentities": [
      { "name": "FRANCE",  "certificate": { "certificate": "@certstore://franceSrv", "key": "@certstore://franceSrv" } },
      { "name": "GERMANY", "certificate": { "certificate": "@certstore://germanySrv", "key": "@certstore://germanySrv" } }
   ],

   "peeringConfigurationFileName": "/etc/rallypointd/peers.json"
}

France leaf peers.json:

{
   "peers": [
      {
         "id": "core-rp",
         "enabled": true,
         "host": { "address": "rp.example.com", "port": 7443 },
         "sni": "FRANCE"
      }
   ]
}

Germany leaf peers.json:

{
   "peers": [
      {
         "id": "core-rp",
         "enabled": true,
         "host": { "address": "rp.example.com", "port": 7443 },
         "sni": "GERMANY"
      }
   ]
}

Each leaf sets isMeshLeaf true and its own domainName so the core mesh forwards traffic correctly. See Connecting Into A Mesh.

Optional Per-Peer Client Certificate

Peer entries may also specify a certificate object (client cert for mutual TLS to that peer). sni is independent—it only selects the server identity on the far end:

{
   "id": "core-rp",
   "enabled": true,
   "host": { "address": "rp.example.com", "port": 7443 },
   "sni": "FRANCE",
   "certificate": {
      "certificate": "@certstore://franceLeafPeerCert",
      "key": "@certstore://franceLeafPeerCert"
   }
}

End-to-End Walkthrough

This walkthrough uses two Rallypoints on a private network: a core Rallypoint (core-rp at 172.16.10.10) with two additional identities, and a leaf Rallypoint (france-leaf at 172.16.20.5) that peers in with sni set.

1. Configure core-rp with additional identities

{
   "id": "core-rp",
   "listenPort": 7443,

   "certificate": {
      "certificate": "@certstore://rtsFactoryDefaultRpSrv",
      "key": "@certstore://rtsFactoryDefaultRpSrv"
   },

   "additionalIdentities": [
      {
         "name": "france",
         "certificate": { "certificate": "@./france.cert", "key": "@./france.key" }
      },
      {
         "name": "germany",
         "certificate": { "certificate": "@./germany.cert", "key": "@./germany.key" }
      }
   ]
}

2. Start core-rp

rallypointd -cfg:core-rp.json

Confirm log lines showing each identity loaded:

loaded identity certificate for '*': ...
loaded identity certificate for 'france': ...
loaded identity certificate for 'germany': ...

3. Configure france-leaf to peer with SNI

france-leaf.json points at peers.json:

{
   "id": "france-leaf",
   "listenPort": 7443,
   "peeringConfigurationFileName": "./peers.json"
}

peers.json:

{
   "peers": [
      {
         "id": "core-rp",
         "enabled": true,
         "host": { "address": "172.16.10.10", "port": 7443 },
         "sni": "FRANCE"
      }
   ]
}

4. Configure an Engage client for the same identity

An application group that connects as a client the same way:

"rallypoints": [
   {
      "host": { "address": "172.16.10.10", "port": 7443 },
      "sni": "FRANCE",
      "verifyPeer": true,
      "caCertificates": [ "@certstore://rtsCA" ]
   }
]

5. Verify

  • Peer link on france-leaf reaches connected state in the status report.
  • openssl s_client with explicit SNI shows the expected server cert:
openssl s_client -connect 172.16.10.10:7443 -servername FRANCE </dev/null 2>/dev/null | openssl x509 -noout -subject

Repeat with -servername GERMANY to see the other identity.


Certificate and Trust Planning

  • Primary vs additional CAs: France and Germany identities may be signed by different CAs. Client and peer caCertificates must trust the cert chain for the identity they select via sni.
  • Mutual TLS still applies: sni only picks the server cert. tls.verifyPeers on the Rallypoint still requires valid client certificates regardless of SNI.
  • Naming convention: Pick stable, operator-friendly name strings (FRANCE, TENANT_A, SITE_42). They are not required to be DNS hostnames.
  • Load balancers: TCP/TLS passthrough to listenPort preserves SNI end-to-end. TLS termination at the load balancer without forwarding SNI to the Rallypoint breaks identity selection—use passthrough or configure the LB to send the appropriate SNI to backends.

Troubleshooting

Symptom Likely cause
Rallypoint aborts at startup after adding identities Bad cert/key path, password-protected key without unlock, or PEM parse error in one additionalIdentities entry
TLS verify failure on client/peer despite correct address sni mismatch—client sent no SNI or wrong name, so server presented primary cert signed by a different CA than the client trusts
Peer connects but wrong cert in logs sni case differs from name—should still work (case-insensitive); check for typos or trailing whitespace
Peer rejected with domain error domainName enforcement on far-end RP; ensure peer declares an allowed domain in hello, separate from SNI
Identity works for clients but not peers Confirm sni is set in peers.json, not only in Engine group config

Enable debug logging and look for cb_SslSniCallback set SSL_CTX for ... on the server and SSL_set_tlsext_host_name() succeeded ... regarding sni of [...] on the client/peer side.


Related Documentation