Skip to main content

Sync Agent — hybrid AD & DB connector

The NexusID Sync Agent is a small, self-hosted service (Windows / Linux / macOS, written in Rust) that connects egress↔ingress to the broker and synchronizes your on-prem systems — like Azure AD Connect, but provider-agnostic. It runs where your directory or database lives and makes only outbound connections, so nothing dials into your network.

Two modes

ModeWhat it does
ADDrains the broker's provisioning queue into Active Directory over LDAP: create (disabled), enable (start date), disable (leaver), attribute update, and password writeback (unicodePwd over LDAPS). Binds with an AD service account (simple bind, or Kerberos/GSSAPI).
DBBi-directional field sync between a database table and the broker. Outbound pushes mapped rows to the ingest webhook (driving Joiner/Mover/Leaver). Writeback applies server-side changes back to the DB. PostgreSQL / MySQL / SQLite.

How it talks to the broker

  • AD mode uses the agent contract: GET /agent/ad/operations claims a batch (auth header X-Agent-Token), the agent executes each against AD, and POST /agent/ad/operations/{id}/complete reports the result. Pair this with a Directory Integration that has Provision from HR sources enabled (see DataSheets → Provisioning to AD).
  • DB mode posts mapped rows to an ingest webhook (e.g. /api/hr-sync/db-agent/webhook) and can poll a change-feed for writeback.

Security

  • No inbound exposure — outbound-only to the broker and to your directory/DB.
  • Encrypted credentials — the AD service-account password and DB connection string use env:VAR or enc:<…> (AES-256-GCM, vault key NEXUS_AGENT_KEY); never required in plaintext. Broker-issued passwords are decrypted with the shared SECRET_ENCRYPTION_KEY.
  • Tamper-evident log — every action is appended to a local hash-chained event log (hash = SHA256(seq ‖ ts ‖ event ‖ detail ‖ prev_hash)). nexus-agent verify-log re-walks the chain and prints the record count and any break, so on-box run history can't be silently edited.

Active Directory account & permissions

On a domain-joined Windows host there are two distinct identities:

  1. The Windows account the agent service runs as — its logon identity (LocalSystem, a domain user, or a gMSA).
  2. The AD account the agent binds to the directory with — the identity that actually performs the create / enable / disable / password-write operations.

How they relate depends on the bind mode (set in [ad] of config.toml):

ModeConfigIdentity used against AD
Simple bind (default build)use_kerberos = false, bind_dn + bind_passwordA dedicated AD service account (a normal domain user, e.g. CN=svc-nexus,OU=Service Accounts,…). The service-logon account is irrelevant to AD; all writes happen as the bind account. The password is stored encrypted (enc: / env:), never plaintext.
Kerberos / integrated (--features kerberos build)use_kerberos = true, no passwordThe Kerberos identity the service runs as — ideally a gMSA (group Managed Service Account), so AD auto-manages and rotates the password and nothing is stored.

This is not an Azure-style "service principal." In AD the equivalent is a service account (domain user) or a gMSA. No SPN is required for the agent — LDAPS needs none, and Kerberos mode only needs the run-as account to have a valid ticket.

Least-privilege delegation (not Domain Admin)

Grant the binding account only the rights it needs, scoped to the OU(s) it manages — never at the domain root:

Agent operationRequired right on the OU
Find users (search by mail / UPN)Read user properties
CREATE_ACCOUNTCreate User objects
Password writeback (unicodePwd, over LDAPS)Reset Password (extended right)
Enable / disableWrite userAccountControl
Attribute updatesWrite the mapped attributes (givenName, sn, displayName, mail, title, department, telephoneNumber, …)

Quickest setup: in Active Directory Users & Computers, right-click the target OU → Delegate Control → grant "Create, delete, and manage user accounts" and "Reset user passwords and force password change at next logon" (or use dsacls for exact attribute rights).

Recommendation (domain-joined)

  • Best: build with --features kerberos, run the Windows service under a gMSA, and delegate the OU rights above to that gMSA — integrated auth, no stored password, automatic rotation.
  • Simpler (stock binary): a dedicated domain user service account with a strong password (enc: / env:), the same OU delegation, and LDAPS on 636 (mandatory for unicodePwd writeback).

Either way, two non-domain credentials remain separate: the broker X-Agent-Token (AD_AGENT_TOKEN) and the SECRET_ENCRYPTION_KEY used to decrypt broker-issued initial passwords.

Quick start

Prebuilt binaries for Linux (glibc + static musl), Windows, and macOS (Apple Silicon + Intel) are attached to each GitHub release — verify against the .sha256. Or build from source:

# 1. Build (portable; add --features kerberos for GSSAPI)
cargo build --release # -> target/release/nexus-agent

# 2. Keys & secrets
nexus-agent gen-key # NEXUS_AGENT_KEY (vault) and the broker key
NEXUS_AGENT_KEY=… nexus-agent encrypt-secret --value 'svc-account-pw' # -> enc:… (paste into config)

# 3. Configure (see config.example.toml) and run
nexus-agent run --config /etc/nexus-agent/config.toml
nexus-agent status --config /etc/nexus-agent/config.toml # mode + audit integrity + counts

Run as a service

  • Linux: dist/nexus-agent.service (systemd) + secrets in /etc/nexus-agent/agent.env.
  • macOS: dist/com.nexusid.agent.plist (launchd).
  • Windows: register with sc.exe or NSSM; the binary handles graceful stop.

Run in Docker

A multi-arch image (linux/amd64 + linux/arm64) is published to GHCR on each release. Replace {version} with the release you want (e.g. 0.1.1 — see the releases):

docker pull ghcr.io/adroitts/nexusid-agent:{version}

docker run -d --name nexus-agent --restart unless-stopped \
-v "$PWD/config.toml:/etc/nexus-agent/config.toml:ro" \
-v nexus-agent-data:/var/lib/nexus-agent \
-e NEXUS_AGENT_KEY -e AD_AGENT_TOKEN -e SECRET_ENCRYPTION_KEY \
ghcr.io/adroitts/nexusid-agent:{version}

Why pin {version} instead of :latest? A sync agent writes to your directory, so the running build should be deliberate and reproducible:

  • Reproducible & auditable:0.1.1 is immutable; everyone (and every host) runs the exact same code, and your change log shows precisely when the agent version changed.
  • Controlled rollout — upgrade by bumping the pinned tag (and rolling back is just re-pinning the previous one), rather than silently picking up a new build on the next docker pull/restart.
  • :latest drifts — two hosts pulling :latest a week apart can run different code; fine for a quick local try, risky for something with write access to AD.

Mount your config.toml read-only at /etc/nexus-agent/config.toml, point audit_log at /var/lib/nexus-agent/audit.jsonl (a named volume) so the hash-chained log persists, and supply env:/enc: secrets via -e (the image runs as a non-root user).

Configuration sketch

[agent]
id = "nexus-agent-dc01"
mode = "ad" # or "db"
poll_interval_secs = 30
audit_log = "/var/lib/nexus-agent/audit.jsonl"

[server]
base_url = "https://idp.example.com"
agent_token = "env:NEXUS_AGENT_TOKEN"
secret_key = "env:SECRET_ENCRYPTION_KEY"

[ad]
url = "ldaps://dc01.corp.example.com:636"
bind_dn = "CN=svc-nexus,OU=Service Accounts,DC=corp,DC=example,DC=com"
bind_password = "enc:AES-256-GCM|local|…"
base_dn = "DC=corp,DC=example,DC=com"
password_writeback = true

Seeing agents in the console

Once an agent is running, Admin → Sync Agents lists every connected agent with a live status dot (online / offline / error), its mode (AD/DB), host, version, and the last connected, last heartbeat, and last terminated timestamps. Click View on a row for the per-agent connection logCONNECTED / TERMINATED / DISCONNECTED (stale) events, newest first.

How it works: the agent posts POST /agent/heartbeat each cycle (authenticated with X-Agent-Token), which flips it ONLINE and stamps lastConnectedAt on a fresh connect; on graceful shutdown it posts POST /agent/disconnect (logged as TERMINATED). If heartbeats stop, a broker sweep (agent.stale-after-secs, default 180s) marks the agent OFFLINE and logs a stale DISCONNECTED. An agent that names a Directory Integration also lights up the Online Agents tile on that page.

Source

The agent source is published at github.com/adroitts/nexusid-agent — see its README.md for the full build/feature matrix (default, --features kerberos, --no-default-features).