glauth exposes each group as cn=<Group> under ou=users, so a case-insensitive (cn=x) search matched both the user and the group (2 entries -> the shared ZB.MOM.WW.Auth.Ldap 'exactly one entry' rule failed the bind). Renamed the 4 colliding testers (readonly/writetune/alarmack/gwreader) + the 2 siblings for consistency: opc-readonly/opc-writeop/opc-writetune/opc-writeconfig/opc-alarmack and gw-viewer. Verified gw-viewer logs into the MxGateway dashboard as Viewer. multi-role/admin/designer/etc. were never affected (no case-collision).
Shared dev GLAuth (dc=zb,dc=local)
One GLAuth directory that all three sister apps use for
dev/test auth — OtOpcUa, MxAccessGateway, ScadaBridge. It runs as a single container on the
shared Docker host 10.100.0.35:3893 (plaintext LDAP). This is the app-neutral source of
truth; each app just points its …Ldap:Server at 10.100.0.35.
Scope: dev/test only. Production uses real corporate AD. See
../../docs/plans/2026-06-04-shared-glauth-standardization-design.md.
Directory layout
Group families are partitioned into non-overlapping gid ranges so the three apps coexist — each app maps only its own family and ignores the rest.
| Family | Used by | Groups (gidnumber) |
|---|---|---|
SCADA-* (55xx) |
ScadaBridge roles (DB-mapped) | Admins 5501, Designers 5502, Deploy-All 5503, Deploy-SiteA 5504, Viewers 5505 |
| OPC-perm (560x) | OtOpcUa + MxGateway OPC-UA write model | ReadOnly 5601, WriteOperate 5602, WriteTune 5603, WriteConfigure 5604, AlarmAck 5605 |
Gw* (561x) |
MxGateway dashboard (config-mapped) | GwAdmin 5610, GwReader 5611 |
OtOpcUa-* (57xx) |
OtOpcUa AdminUI (DB-mapped) | Admins 5701, Designers 5702, Viewers 5703 |
Users (all password password except serviceaccount → serviceaccount123):
serviceaccount(cn=serviceaccount,dc=zb,dc=local) — the single bind account every app uses for search-then-bind. Has asearch *capability.multi-role— member of every group → all roles in all three apps (canonical cross-app login).admin—SCADA-Admins+GwAdmin+OtOpcUa-Admins→ Administrator everywhere.- Per-role testers:
designer/deployer/site-deployer(ScadaBridge);gw-viewer(MxGateway Viewer);otdesigner/otviewer(OtOpcUa);opc-readonly/opc-writeop/opc-writetune/opc-writeconfig/opc-alarmack(OPC perms).
Naming rule: a tester username must not case-collide with a group name. GLAuth exposes each group as
cn=<Group>underou=users, so a case-insensitive(cn=x)search would match both the user and the group (two entries → the shared lib's "exactly one entry" rule fails the bind). That's why the OPC/Gw testers areopc-*/gw-viewer, notreadonly/gwreader.
Deploy on 10.100.0.35
Access note: deploying needs working SSH/Docker access to
10.100.0.35. If your key is not authorized there, hand this folder to whoever administers the box and have them run the samedocker compose up -d. The artifact is self-contained.
# From this repo root. Copy the FILES into the dest (not the dir) so a re-deploy
# doesn't nest them at ~/zb-glauth/glauth/ (scp -r of a dir into an existing dir).
ssh dohertj2@10.100.0.35 'mkdir -p ~/zb-glauth'
scp infra/glauth/config.toml infra/glauth/docker-compose.yml dohertj2@10.100.0.35:~/zb-glauth/
ssh dohertj2@10.100.0.35 'cd ~/zb-glauth && docker compose up -d --force-recreate && docker ps --filter name=zb-shared-glauth'
Verify
Bind as the service account and confirm multi-role spans all four families:
ldapsearch -x -H ldap://10.100.0.35:3893 \
-D cn=serviceaccount,dc=zb,dc=local -w serviceaccount123 \
-b dc=zb,dc=local "(cn=multi-role)" memberOf
# → result: 0 Success. memberOf comes back as group DNs (e.g. ou=SCADA-Admins,ou=groups,dc=zb,dc=local)
# spanning all four families: SCADA-*, ReadOnly/Write*/AlarmAck, GwAdmin/GwReader, OtOpcUa-*.
# (The shared ZB.MOM.WW.Auth.Ldap lib strips each to its bare RDN, e.g. "SCADA-Admins", at login.)
Confirm a user authenticates with password (a bad password returns result: 49; this user lacks
the search capability, so a successful bind shows result: 50 Insufficient access on the search):
ldapsearch -x -H ldap://10.100.0.35:3893 \
-D cn=multi-role,dc=zb,dc=local -w password \
-b dc=zb,dc=local "(cn=multi-role)" cn
No ldapsearch locally? Run it from a throwaway container:
docker run --rm alpine:3.20 sh -c 'apk add -q openldap-clients >/dev/null 2>&1 && \
ldapsearch -x -H ldap://10.100.0.35:3893 -D cn=serviceaccount,dc=zb,dc=local -w serviceaccount123 \
-b dc=zb,dc=local "(cn=multi-role)" memberOf'
Editing the directory
GLAuth uses the config datastore (this config.toml, mounted read-only). To add/change a user
or group, edit config.toml and recreate the container — a plain restart keeps the stale
file (single-file Docker bind-mount inode trap):
docker compose up -d --force-recreate
Group gidnumbers and user uidnumbers must stay unique across the whole file; keep new
groups inside the per-app range (55xx / 56xx / 57xx) so the families don't collide.