docs(glauth): shared GLAuth standardization design (dev/test consolidation onto 10.100.0.35)

Approved design: consolidate OtOpcUa, MxAccessGateway, ScadaBridge dev/test auth
onto one shared GLAuth at 10.100.0.35:3893 (dc=zb,dc=local, plaintext). App-neutral
source of truth in scadaproj/infra/glauth/; merged directory with gid families
partitioned 55xx/56xx/57xx + multi-role/admin/serviceaccount; per-app Server
repoints; incremental rollout keeping old glauths until verified.
This commit is contained in:
Joseph Doherty
2026-06-04 15:26:32 -04:00
parent b0fe7b15ca
commit 106fb8b149
@@ -0,0 +1,140 @@
# Shared GLAuth Standardization — Design
> **Status:** Approved 2026-06-04. Next: `superpowers-extended-cc:writing-plans` → implementation plan.
> **Scope:** dev/test only. Production stays on real corporate AD (out of scope).
## Goal
Consolidate the three sister projects (OtOpcUa, MxAccessGateway, ScadaBridge) onto **one shared
GLAuth dev directory** running on the shared Docker host **`10.100.0.35:3893`**, replacing the
three separate LDAP setups in use today. This is the natural endpoint of the Auth-component
normalization: all three already use the shared `ZB.MOM.WW.Auth.Ldap` library (search-then-bind)
and already default to the same base DN `dc=zb,dc=local`.
## Decisions (locked during brainstorming)
| Decision | Choice |
|---|---|
| Environments | **Dev/test only** (prod → real AD, untouched) |
| Consolidation depth | **Full** — every dev instance points at 35 |
| Transport | **Plaintext** (`Transport=None`, `AllowInsecure=true`) — trusted lab subnet |
| Source of truth | **`scadaproj/infra/glauth/`** (app-neutral, next to the other shared `ZB.MOM.WW.*` components) — Approach A |
## Architecture
```
scadaproj/infra/glauth/ ← single source of truth (git)
├── config.toml (merged dc=zb,dc=local directory)
├── docker-compose.yml (one `glauth` service, :3893)
└── README.md
│ deploy on 10.100.0.35: docker compose up -d
GLAuth @ 10.100.0.35:3893 · datastore=config · baseDN dc=zb,dc=local · ldaps=false
▲ ▲
plaintext bind │ (None + AllowInsecure) │
┌──────────────┴───────────┐ ┌─────────┴─────────────────────┐
Mac / OrbStack │ windev (10.100.0.48)
• ScadaBridge :9000/:9100 │ • MxGateway (MxAccessGw svc)
• OtOpcUa docker-dev │ • OtOpcUa (OtOpcUa svc)
(un-stubbed)
```
- One `glauth` container on `10.100.0.35:3893`, `datastore=config`, `baseDN=dc=zb,dc=local`, ldaps disabled.
- Every dev consumer: `Server=10.100.0.35`, `Port=3893`, `Transport=None`, `AllowInsecure=true`, `SearchBase=dc=zb,dc=local`.
- **Retired:** the `scadabridge-ldap` container (ScadaBridge `infra/docker-compose.yml`) and the windev-local glauth (`C:\publish\glauth`).
- **Consequences:** windev gains a runtime dependency on 35 for *new* logins (existing cookie sessions unaffected); deploying to 35 needs working access (see Prerequisites).
## The merged directory
One `dc=zb,dc=local` directory; group families partitioned into **non-overlapping gid ranges** (today
both existing GLAuth files reuse 55015505 — the collision to fix). **Each app maps only its own family
and ignores the rest**, so the families coexist with zero conflict.
**Groups**
| 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 |
`SCADA-*` keeps its canonical 55xx numbers (already deployed). The OPC/`Gw` groups move off the old
55015505/5510 into 56xx to clear the clash.
**Users** (all password `password`; uid ranges 50xx ScadaBridge / 51xx MxGateway / 52xx OtOpcUa)
- **`serviceaccount`** (5999, `cn=serviceaccount,dc=zb,dc=local`, `search *` capability) — the *single*
bind account every app uses. Password `serviceaccount123`. ScadaBridge moves to it from `cn=admin`/`password`.
- **`multi-role`** (5005) — member of **every** group → all roles in all three apps (canonical cross-app QA login).
- **`admin`** (5001) — `SCADA-Admins` + `GwAdmin` + `OtOpcUa-Admins` → Administrator everywhere.
- Per-role testers: `designer`/`deployer`/`site-deployer` (ScadaBridge); `gwreader` (MxGateway Viewer);
`otdesigner`/`otviewer` (OtOpcUa); `readonly`/`writeop`/`writetune`/`writeconfig`/`alarmack` (OPC perms).
## Per-app config changes
Each consumer changes only its LDAP `Server` (+ a few keys). Shared service account
`cn=serviceaccount,dc=zb,dc=local` / `serviceaccount123`.
- **ScadaBridge** (`docker/` + `docker-env2/`, central-node-a & -b `appsettings.Central.json`):
`Ldap:Server` `scadabridge-ldap``10.100.0.35`; `ServiceAccountDn` `cn=admin``cn=serviceaccount`,
`ServiceAccountPassword``serviceaccount123`. Rest unchanged (`SCADA-*` DB mappings already seeded).
Retire the `ldap` service in `infra/docker-compose.yml`; sequenced-recreate central nodes.
- **OtOpcUa docker-dev** (`docker-dev/docker-compose.yml`, all host containers) — **the un-stub**:
drop `Security__Ldap__DevStubMode=true`; add `Server=10.100.0.35`, `Port=3893`, `Transport=None`,
`AllowInsecure=true`, `SearchBase=dc=zb,dc=local`, `ServiceAccountDn=cn=serviceaccount,…`,
`ServiceAccountPassword=serviceaccount123`. Seed OtOpcUa DB mappings
`OtOpcUa-Admins→Administrator`, `OtOpcUa-Designers→Designer`, `OtOpcUa-Viewers→Viewer` (system-wide).
- **MxGateway** (windev `C:\publish\mxaccessgw\Server\appsettings.json`): `Ldap:Server`
`localhost``10.100.0.35`; `SearchBase` `dc=lmxopcua``dc=zb,dc=local`; `ServiceAccountDn``…dc=zb,dc=local`.
`Transport=None`/`AllowInsecure=true` already migrated; `GroupToRole` (`GwAdmin`/`GwReader`) unchanged.
Restart `MxAccessGw` (+ dependent `OtOpcUa` svc).
- **OtOpcUa (windev service)**: locate its deployed overlay; repoint `Server``10.100.0.35`,
`SearchBase``dc=zb,dc=local`, service account, and switch dev transport `Ldaps``None`+`AllowInsecure`.
- **Then** stop/disable the windev-local `glauth` service.
## Rollout & rollback
Incremental; **the old glauths stay up until the very end**, so every step is reversible by pointing
`Server` back.
1. Stand up the shared glauth on 35 → verify via `ldapsearch` (bind `serviceaccount`; `multi-role`
`memberOf` spans all families). Nothing repointed yet.
2. Prove reachability from an OrbStack container to `10.100.0.35:3893` (the linchpin) before any app edit.
3. ScadaBridge `:9000` → recreate → browser-verify `multi-role` = 4 roles. Then `:9100`.
4. OtOpcUa docker-dev → un-stub + repoint + seed → recreate → verify.
5. windev MxGateway (backup appsettings) → restart → verify. Then windev OtOpcUa overlay.
6. Only once all green: stop/disable `scadabridge-ldap` + the windev-local glauth.
**Rollback** per consumer: revert the one-line `Server` change (git revert on the Mac; `.bak` restore on
windev) and recreate/restart. Remove the shared glauth = `docker compose down` on 35.
## Testing & verification
- **LDAP layer:** `ldapsearch` bind `serviceaccount`; confirm each test user + `multi-role`'s `memberOf`
across all four families; bind each user to confirm `password`.
- **Per-app browser (macbook Chrome):** ScadaBridge `:9000`/`:9100` `multi-role` → 4 roles (via
`/auth/token`); OtOpcUa `:9200` → seeded roles; MxGateway `10.100.0.48:5130` → Administrator; windev OtOpcUa → AdminUI.
- **Role-gating spot-checks:** `gwreader`→MxGateway Viewer-only; `designer`→ScadaBridge design-only;
`otviewer`→OtOpcUa read-only.
- **Negative:** wrong password rejected everywhere; a user in no family of an app → denied there.
## Prerequisites & open items (resolve in the plan)
1. **Access to `10.100.0.35`** — SSH from this Mac is currently refused (`Permission denied`/connection
reset) and the windev→35 jump is administratively prohibited. Either re-authorize this Mac's key on 35,
or the user runs the final `docker compose up -d`. Artifacts are portable either way.
2. **OtOpcUa group key shape** — confirm OtOpcUa maps on the **short RDN** (`OtOpcUa-Admins`) the shared
lib returns vs the full-DN its `LdapGroupRoleMapping` entity comment shows, before seeding.
3. **OrbStack→LAN reachability** — verify ScadaBridge/OtOpcUa containers can reach `10.100.0.35:3893`
early (likely fine; it's the linchpin). `log()` if any consumer can't reach 35 rather than silently failing.
4. **windev OtOpcUa config path** — discovery step (less is known about this deployment than MxGateway).
## Notes
- `scadaproj` is a plain-files umbrella that is *also* a local git repo; `infra/glauth/` lives here as the
canonical source. Per-app config edits land on a `feat/*` branch per repo (merge on the user's go).
windev edits are deployment-only with `.bak` backups (like the GroupToRole / LDAP-key migrations done
2026-06-04); repo templates optionally aligned.
- Related memory: `multi-role-cross-app-test-user`, `mxgateway-windev-deploy`,
`scadabridge-local-deploy-gotchas`, `auth-audit-normalization-in-progress`.