diff --git a/docs/GatewayConfiguration.md b/docs/GatewayConfiguration.md index 2790940..278435c 100644 --- a/docs/GatewayConfiguration.md +++ b/docs/GatewayConfiguration.md @@ -249,7 +249,7 @@ dev/test GLAuth posture (`glauth.md`), not a production posture. | `MxGateway:Ldap:AllowInsecure` | `true` | Permits a plaintext bind. Must be `true` when `Transport` is `None`; set `false` (with `Ldaps`/`StartTls`) in production. | | `MxGateway:Ldap:SearchBase` | `dc=zb,dc=local` | Search base DN. | | `MxGateway:Ldap:ServiceAccountDn` | `cn=serviceaccount,dc=zb,dc=local` | Bind DN for the search account. | -| `MxGateway:Ldap:ServiceAccountPassword` | `${secret:ldap/mxgateway/bind}` | Search-account password. **Never a committed plaintext value (SEC-36):** the shared GLAuth bind credential is supplied out-of-band through one of three channels, all binding to this key. **(1) Encrypted secrets store (shipped default):** `appsettings.json` ships the reference `${secret:ldap/mxgateway/bind}`, which the pre-host `${secret:}` expander resolves at startup from the encrypted secrets store (the code-side design default is blank, so a missing/unresolved value fails closed rather than falling back to a leaked credential). Seed it once with `secret set ldap/mxgateway/bind ` (the store's master key must be present via `ZB_SECRETS_MASTER_KEY`); startup aborts with `SecretNotFoundException` if the secret is absent. **(2) Deployed hosts — env var:** override directly with `MxGateway__Ldap__ServiceAccountPassword` (double-underscore form) in the NSSM service environment — a plain literal there is used as-is and the store lookup is skipped. **(3) Dev boxes — user-secrets:** `dotnet user-secrets set "MxGateway:Ldap:ServiceAccountPassword" ` (the server carries `mxaccessgw-server`; user-secrets load automatically in the Development environment and live under the user profile, outside the tree). The value comes from the GLAuth source of truth `scadaproj/infra/glauth/`, never from a repo file. **Rotation:** because the credential was historically committed, rotating it in `scadaproj/infra/glauth/` (and redeploying the shared GLAuth on `10.100.0.35`) is required — see `docs/runbooks/SEC-36-ldap-credential-rotation.md` for the cutover order. A blank/unresolved value fails startup validation with a message naming the two supported channels. | +| `MxGateway:Ldap:ServiceAccountPassword` | `${secret:ldap/mxgateway/bind}` | Search-account password. **Never a committed plaintext value (SEC-36):** the shared GLAuth bind credential is supplied out-of-band through one of three channels, all binding to this key. **(1) Encrypted secrets store (shipped default):** `appsettings.json` ships the reference `${secret:ldap/mxgateway/bind}`, which the pre-host `${secret:}` expander resolves at startup from the encrypted secrets store (the code-side design default is blank, so a missing/unresolved value fails closed rather than falling back to a leaked credential). Seed it once with `secret set ldap/mxgateway/bind ` (the store's master key must be present via `ZB_SECRETS_MASTER_KEY`); startup aborts with `SecretNotFoundException` if the secret is absent. **(2) Deployed hosts — env var:** override directly with `MxGateway__Ldap__ServiceAccountPassword` (double-underscore form) in the NSSM service environment — a plain literal there is used as-is and the store lookup is skipped. **(3) Dev boxes — user-secrets:** `dotnet user-secrets set "MxGateway:Ldap:ServiceAccountPassword" ` (the server carries `mxaccessgw-server`; user-secrets load automatically in the Development environment and live under the user profile, outside the tree). The value comes from the GLAuth source of truth `scadaproj/infra/glauth/`, never from a repo file. **Rotation:** because the credential was historically committed, it was rotated in `scadaproj/infra/glauth/` (and the shared GLAuth on `10.100.0.35` redeployed) on 2026-08-07 (SEC-36) — see `docs/runbooks/SEC-36-ldap-credential-rotation.md` for the cutover procedure used, and for future rotations. A blank/unresolved value fails startup validation with a message naming the two supported channels. | | `MxGateway:Ldap:UserNameAttribute` | `cn` | LDAP attribute holding the login user name. | | `MxGateway:Ldap:DisplayNameAttribute` | `cn` | LDAP attribute holding the display name. | | `MxGateway:Ldap:GroupAttribute` | `memberOf` | LDAP attribute enumerating group membership (mapped to dashboard roles via `MxGateway:Dashboard:GroupToRole`). | diff --git a/docs/GatewayTesting.md b/docs/GatewayTesting.md index 9220c9b..9ecd8a7 100644 --- a/docs/GatewayTesting.md +++ b/docs/GatewayTesting.md @@ -455,20 +455,22 @@ runtime because the x86 Worker cannot build on Linux: ### Runner capacity is shared and finite -All CI runs on a co-located `gitea-runner` container on docker host `10.100.0.35` with -`maxParallel=1`, and that runner instance is **shared across repos** — it interleaves -`dohertj2/mxaccessgw` and `dohertj2/lmxopcua` jobs on the single slot rather than being -scoped to this repo (`GET /repos/dohertj2/mxaccessgw/actions/runners` returns -`total_count: 0`; the runner is registered at the instance level). Every job in a run -(`portable`, `java`, `windows-x86`) executes serially, so queue latency is additive within -a run and an active `lmxopcua` run blocks `mxaccessgw` entirely — expect ~20–30 minutes of -queue depth under cross-repo contention, not a stuck pipeline. This Gitea version (1.26) -also exposes **no run cancel or delete via the API** (`POST .../actions/runs/{id}/cancel` -returns 404, `DELETE .../actions/runs/{id}` returns 400), so a superseded or hung run cannot -be cleared and holds the slot until it finishes or times out. See -`docs/runbooks/TST-30-second-ci-runner.md` for the operator runbook that registers a second -runner to relieve this; until that lands, treat single-runner contention as expected, not a -CI outage. +CI runs on two co-located runner containers on docker host `10.100.0.35` — `gitea-runner` +(capacity 4) and `gitea-runner-2` (capacity 2, registered 2026-08-07 per +`docs/runbooks/TST-30-second-ci-runner.md`) — and both runner instances are **shared across +repos**: they interleave `dohertj2/mxaccessgw` and `dohertj2/lmxopcua` jobs across the +combined slots rather than being scoped to this repo (`GET +/repos/dohertj2/mxaccessgw/actions/runners` returns `total_count: 0`; both runners are +registered at the instance level). Every job in a run (`portable`, `java`, `windows-x86`) +still executes serially within that run, so queue latency is additive within a run, but an +active `lmxopcua` run no longer blocks `mxaccessgw` entirely the way a single shared slot +did — the two runners relieve cross-repo contention. This Gitea version (1.26) also exposes +**no run cancel or delete via the API** (`POST .../actions/runs/{id}/cancel` returns 404, +`DELETE .../actions/runs/{id}` returns 400), so a superseded or hung run cannot be cleared +and holds the slot until it finishes or times out — with two runners this means a single +wedged run can still hold slots, because the no-cancel reality is unchanged. See +`docs/runbooks/TST-30-second-ci-runner.md` for the operator runbook that registered the +second runner. When queue depth (or the missing-cancel reality) makes waiting impractical, verify a specific commit out of band instead of waiting behind the queue: run diff --git a/glauth.md b/glauth.md index 9dcfc78..ff415f0 100644 --- a/glauth.md +++ b/glauth.md @@ -40,8 +40,9 @@ gw-specific role. > it out-of-band (encrypted secrets store reference `${secret:ldap/mxgateway/bind}`, the > `MxGateway__Ldap__ServiceAccountPassword` env var on deployed hosts, or `dotnet user-secrets` > on dev boxes — see `docs/GatewayConfiguration.md`). The credential was historically committed -> to this repo (and remains recoverable from git history), so **rotation is required**; the -> operator runbook is `docs/runbooks/SEC-36-ldap-credential-rotation.md`. +> to this repo (and remains recoverable from git history); it **was rotated on 2026-08-07** +> (SEC-36, executed per `docs/runbooks/SEC-36-ldap-credential-rotation.md`) and the old +> committed value no longer binds. ## Pre-existing groups (LmxOpcUa role taxonomy)