Skip to content

Bring your own NATS

By default pfCentral runs its own NATS broker and sets it up on first start. In external mode it connects to an operator-mode NATS broker that you run, such as a self-hosted cluster or Synadia Cloud. Use it when:

  • your team already operates NATS and wants one broker;
  • you need several pfCentral replicas for high availability (Kubernetes);
  • agents should connect to a broker in a different place from the web server.

If none of these apply, keep the embedded broker. It needs no setup.

New to NATS terms? Read NATS for firewall admins first.

Embedded External
pfCentral creates the operator, You create one account and its keys.
accounts, keys and TLS on first start. pfCentral creates nothing on the broker.
agents ──▶ pfCentral :24222 agents ──▶ your broker ◀── pfCentral (×N)
│ ▲
└──────┘ auth callout to pfCentral
  • pfCentral connects with three creds files and signs device passes with a delegated signing key you give it.
  • Its JetStream data (the TELEMETRY stream, the device-state and device-presence buckets) lives in its own account.
  • It has no access to $SYS, so offline detection relies on heartbeats only: a cleanly stopped agent shows offline after about 15 seconds instead of at once.
  • The device flow is the same. Agents enroll over HTTPS, then connect to the broker; the broker asks pfCentral to approve each connection.

You need one regular (not system) account for this pfCentral install.

  1. Create the account with JetStream enabled (memory and disk storage, streams and consumers).

  2. Add a scoped signing key for agents. Register it as a user scope with role agent and this template:

    Subjects
    Publish allowpfsense.{{tag(device_id)}}.>, $SRV.>, pf.presence.{{tag(device_id)}}, _INBOX.>
    Subscribe allow_INBOX.>, pfsense.{{tag(device_id)}}.>, $SRV.>

    pfCentral tags each device pass with device_id:<uuid>, so {{tag(device_id)}} fences each device to its own subjects. Agents must publish to _INBOX.> because they reply to the server’s requests in the same account.

  3. Create three users and their creds files:

    UserPurposeAuth user?
    apppfCentral’s own connectionYes
    calloutthe authorization callout serviceYes
    sentinelthe shared login agents knock withNo
  4. Turn on the authorization callout for the account, with the callout user’s and app user’s public keys, and allow only this account. The app and callout users must be listed as auth users so their own connections skip the callout. The sentinel must not be, so agent connections trigger it.

  5. Give pfCentral the three creds files and the seed (.nk) of the signing key from step 2.

With nsc on a self-hosted broker this is roughly nsc add account, nsc edit account --js-mem-storage -1 --js-disk-storage -1 …, nsc edit account --sk generate with a scope, nsc add user three times, nsc generate creds, and pushing the account to your resolver. On Synadia Cloud you do the same steps in the control plane.

VariableValue
PFCENTRAL_NATS_MODEexternal
PFCENTRAL_NATS_URLBroker URL, tls://nats.example.com:4222
PFCENTRAL_NATS_CREDS_FILEapp creds
PFCENTRAL_NATS_CALLOUT_CREDS_FILEcallout creds
PFCENTRAL_NATS_SENTINEL_CREDS_FILEsentinel creds
PFCENTRAL_NATS_SIGNING_KEY_PATHagent signing key seed
PFCENTRAL_NATS_RESOURCE_PREFIXOptional. Prefix for stream and bucket names if two installs share one account. Prefer one account per install.

pfCentral stops at start if a file is missing, if the creds belong to different accounts, or if one has expired. The first start registers the account in the database; later starts reuse it.

Agents get the sentinel creds when they enroll, as in embedded mode. Point their NATS URL at your broker.

Register a new scoped key on the account, update PFCENTRAL_NATS_SIGNING_KEY_PATH, and restart pfCentral. Passes signed with the old key expire within an hour; remove the old key from the account after that.