Skip to main content

Docker Compose (secured)

This guide runs a secured Zaris cluster in containers — four nodes and a manager, with mutual TLS on the data plane and token authentication. It uses the attach deployment model and CA mode 3 (pre-issued leaves), in which each node's certificate is mounted into its container rather than issued by the cluster.

It corresponds to the secured sample under docker/ in the Zaris repository — docker/docker-compose.demostore.secure.yml, its store config docker/config/demostore-secure.json, and the PKI you populate under docker/secrets/.

Topology

The manager observes the nodes; an external one-shot container binds the workspace and registers the store. Nodes authenticate to each other with mutual TLS.

Secured Zaris cluster inside a Docker network: a manager observes four nodes and an attach one-shot container registers the store, while the nodes authenticate to one another with mutual TLS, each presenting its own pre-issued leaf certificate.

  • Manager (clustron-zaris-machine:local) — the control plane. It mounts its security material: the signing key, cluster CA, admin user, and RBAC policy.
  • Four nodes (clustron-zaris-node:local) — each mounts its own leaf certificate (node-<i>.pfx) and the shared store config.
  • Attach one-shot — a short-lived container that logs in, binds the workspace in attach mode, and registers the store as TLS-enabled.

Ports are published so you can reach the Console and Management API from the host — for example, the Console on 18101 and the Management API on 17801. The node client ports are published for in-network clients.

Prerequisites

Complete these before you bring the cluster up.

  1. Install Docker and Docker Compose.

  2. Build the two images locally from the Zaris repository's docker/ directory:

    docker build -t clustron-zaris-machine:local -f docker/Dockerfile.machine .
    docker build -t clustron-zaris-node:local -f docker/Dockerfile.node .
  3. Populate the secrets/ directory with the cluster's PKI. Because this is CA mode 3, you provide the certificates — the cluster does not mint them. The sample's secrets/README.md gives a one-time recipe to generate a matched set: bootstrap an open cluster, enable security, harvest the CA and per-node leaves, then stamp the CA into the store config.

note

By design, a mode-3 secured cluster does not mint its own certificates — an external step issues them. The sample documents that step; in production, your PKI or cert-manager produces the mounted certificates.

What gets mounted

The security of the cluster depends on these mounts landing in the right place. Each row must be present before the containers start, or nodes will fail their TLS handshake.

Path (on host)Mounted intoContents
secrets/manager-security/demostore-manager:/var/lib/clustron/securityToken signing key, cluster CA, admin user, RBAC policy, enabled.flag
secrets/demostore-n<i>.pfxdemostore-n<i>:/var/lib/clustron/certs/demostore-n<i>.pfxThat node's pre-issued leaf certificate (and key)
config/demostore-secure.jsonevery node, as /config/clustron.json (read-only)The store config: security.tls.mode: MutualTls and the trust anchor PEM

The store config declares the trust model explicitly under security.tls: enabled: true, mode: "MutualTls", and the cluster CA under trustAnchors[].pem. Each node presents its own mounted leaf (demostore-n<i>.pfx) rather than a certificate minted by the cluster — this is exactly the Mode 3 configuration.

Bring the cluster up

  1. Start the stack:

    docker compose -f docker-compose.demostore.secure.yml -p clustron-demostore-secure up -d

    The manager and nodes come up healthy, the attach one-shot binds the workspace and registers the store, and the cluster forms with mutual TLS between nodes.

  2. Verify the cluster is stable by querying the membership diagnostics through the published Management API port. Because the cluster is secured, the Management API requires authentication — log in first to obtain an admin token, then pass it as a bearer token:

    # Log in and capture an admin token
    TOK=$(curl -s -X POST http://localhost:17801/security/login \
    -H 'Content-Type: application/json' \
    -d '{"username":"admin","password":"admin"}' \
    | grep -oE '"token":"[^"]*"' | head -1 | cut -d'"' -f4)

    # Query membership with the token
    curl -s http://localhost:17801/admin/v1/stores/demostore/diagnostics/membership \
    -H "Authorization: Bearer $TOK"
    # → "clusterState":"Stable", "stableMembership":true, "knownMembers":4

    Without the Authorization header the API returns {"error":"auth_required"} — that is the secured cluster rejecting an unauthenticated call, not a failure of the cluster.

    A healthy cluster reports clusterState: Stable, stableMembership: true, and knownMembers: 4. If knownMembers is below 4, a node failed to join — check that its demostore-n<i>.pfx is mounted and that the CA in demostore-secure.json matches the certificates.

  3. Open the Console at http://localhost:18101 to see the nodes, partitions, and the Encryption (TLS) status.

Connect a client from inside the network

The cluster advertises in-network hostnames, so connect from inside the Docker network — the stack includes a demostore-client box for exactly this. The data plane is both TLS-encrypted and token-authenticated, so a bare zaris:// connect is refused: the connection string must use the zariss:// (TLS) scheme, supply the cluster CA with ca=file:/tmp/ca.pem so the client can validate the node certificates, and carry a token with token=.

First, copy the cluster CA into the client box (the client container does not mount secrets/):

docker cp secrets/ca.pem demostore-client:/tmp/ca.pem
docker exec -it demostore-client pwsh

Then, inside that PowerShell session, log in for a token and connect over TLS:

# Log in to the manager (in-network) and capture an admin token
$tok = (Invoke-RestMethod -Method Post -Uri http://demostore-manager:7801/security/login `
-ContentType application/json `
-Body '{"username":"admin","password":"admin"}').token

Connect-ZrStore -ConnectionString "zariss://demostore-n0:7861,demostore-n1:7861,demostore-n2:7861,demostore-n3:7861/demostore?token=$tok&ca=file:/tmp/ca.pem"
Set-ZrItem -Key "hello" -Value "from docker"
Get-ZrItem -Key "hello"

The node service names are demostore-n0..demostore-n3 (client port 7861); the manager is demostore-manager. See Connecting a secured client for the full client contract (CA pinning, existing tokens, troubleshooting).

warning

A client on the host can reach a seed node but may not resolve the other nodes' in-network hostnames. For host or external clients, publish and advertise host-reachable addresses, or run the client inside the Compose network. This is a networking concern of your deployment, not a limitation of Zaris.

Tear the cluster down

docker compose -f docker-compose.demostore.secure.yml -p clustron-demostore-secure down -v

Store data is in-memory, so down clears it. The mounted secrets/ are untouched — reuse them on the next up, or regenerate them for a fresh cluster.

Next steps