docs(tracking): close NEXT-07, file NEXT-08/09/10, record the runner token reset
NEXT-07 is struck: windev was redeployed from origin/main (a346d51) and the service is
healthy, and the root cause the row predicted is confirmed -- the 2026-06-25 build's
Auth.ApiKeys 0.1.2.0 supports auth-DB schema 2 while the database sits at schema 3, which
is the current shared-lib version, so deploying forward was the fix rather than touching
the DB. The original text stays for the triage record.
Three findings surfaced by that work, each deliberately left for the next cycle rather
than patched in passing:
- NEXT-08: the shared GLAuth offers no TLS, so SEC-06 makes GatewayConfiguration.md's
"deployed hosts must set Ldaps or StartTls" unsatisfiable for anything genuinely
labelled Production. windev's relabel to Staging is honest for a dev rig but defers
the posture question rather than answering it.
- NEXT-09: Directory.Build.props:29 quotes a path ending in a backslash, so the SHA-stamp
git invocation is malformed on Windows and ContinueOnError stamps git's stderr into
InformationalVersion -- a Windows binary cannot be correlated to a commit, which is what
TST-11 exists to guarantee.
- NEXT-10: glauth.md's pre-provisioned-user table contradicts both the directory and its
own dashboard section, and was the root cause of the NEXT-06 fixture drift. Reconciling
it sweeps the OPC-UA group taxonomy, so it is scoped out here on purpose.
The TST-30 runner work is hygiene, not closure: runner-1 now mounts its registration token
from a 0600 file like runner-2, but both still share one instance-scope token that was
world-readable for months and is provably still live. Gitea 1.26.4 cannot rotate it from
the CLI or API, so the UI reset is recorded as a pending operator action with its
follow-through (refresh the token file, shred the token-bearing compose backups).
This commit is contained in:
@@ -185,3 +185,4 @@ Sequence these together rather than piecemeal — several are one change set spa
|
||||
| 2026-08-07 | **TST-30 → fully `Done`** — the operator half (register a second CI runner) executed live on `10.100.0.35` per `docs/runbooks/TST-30-second-ci-runner.md` option (a). A second **instance-level** `act_runner` container, `gitea-runner-2` (runner id 5, capacity 2, labels `ubuntu-latest`/`ubuntu-22.04`), was added to the `/opt/gitea` compose stack with the same `container.network: traefik` setting as the original, its registration token mounted from a `0600` file rather than inlined in compose. The existing `gitea-runner` (id 1, capacity 4) was **not** touched — total capacity went 4 → 6 purely by addition, so the change reverts by removing one container. **Concurrency verified live** (the finding's acceptance test): HEAD `a346d51` was pushed to two scratch branches, `scratch/tst30-a` (run 661) and `scratch/tst30-b` (run 662), while an unrelated run 660 was already in flight; at 13:07:53Z jobs from **three** runs were `in_progress` at once — run 660 `portable` plus run 662 `portable`/`java` on runner 1, and run 661 `portable`/`java` on `gitea-runner-2` — a state the pre-change single-runner topology could not produce. Runner attribution came from `GET /repos/dohertj2/mxaccessgw/actions/runs/{id}/jobs`, which exposes `runner_id`/`runner_name` per job, and was cross-checked against `docker logs gitea-runner-2` (`task 1145 …`, `task 1146 …` pickups at 13:05:22–23Z). **`gitea:3000` resolution holds on the new instance** — the property option (c) was rejected for losing: run 661's `portable` job (task 1145, scheduled on `gitea-runner-2`) logged `git remote add origin http://gitea:3000/dohertj2/mxaccessgw` then a successful `fetch … From http://gitea:3000/dohertj2/mxaccessgw`, and its job container's `/workspace/dohertj2/mxaccessgw` was confirmed checked out at `a346d514dd24e775640e5667aa7cd8e561fec68a`. **Runbook fact corrected:** its checklist expected `GET /repos/dohertj2/mxaccessgw/actions/runners` to show ≥2, but that endpoint still returns `total_count: 0` — correctly, since both runners are instance-scoped, which is exactly what this finding documented; the working check is `GET /api/v1/admin/actions/runners` (lists ids 1, 4 = an unrelated local macOS runner, and 5). A dated "Executed 2026-08-07" note recording this was added to the top of the runbook rather than rewriting its history. No source, workflow, or `docs/GatewayTesting.md` change was needed — the no-cancel reality and the `run-windev-ci.sh` bypass are unaffected by runner count, as that prose already stated. Scratch branches deleted after verification. Tracking: both registers' TST-30 rows, the pending-operator-actions list in `90-candidate-findings-next-cycle.md`, and this row. |
|
||||
| 2026-08-07 | **CLI-39 publish executed** — the release the version train was cut for is now live on `gitea.dohertylan.com`. Published from HEAD `a346d51` via `scripts/pack-clients.ps1`: `nuget` `ZB.MOM.WW.MxGateway.Client` **0.2.0** + `ZB.MOM.WW.MxGateway.Contracts` **0.2.0**, `pypi` `zb-mom-ww-mxaccess-gateway-client` **0.2.0**, `cargo` `zb-mom-ww-mxgateway-client` **0.2.0**, `maven` `com.zb.mom.ww.mxgateway:zb-mom-ww-mxgateway-client` **0.2.1** (the Java exception recorded in the CLI-39 row above — 0.2.0 was already taken by the pre-conformance build). All five coordinates re-queried through `GET /api/v1/packages/dohertj2/{type}/{name}/{version}` after the push and confirmed `200`. Go ships no artifact: module tag `clients/go/v0.2.0` was created and pushed at `a346d51` by `scripts/tag-go-module.ps1` (its CLI-21 version-match guard passed against `ClientVersion = "0.2.0"`), and `go list -m …@v0.2.0` resolves. Full suites ran pre-push with no failures: 128 .NET, 164 Python, 107 Rust, 79 Java. **Deviation found and fixed:** the first `cargo publish` returned `401` because `CARGO_REGISTRIES_DOHERTJ2_GITEA_TOKEN` held the raw token — cargo forwards that value as the `Authorization` header verbatim and Gitea's cargo registry requires `Bearer <token>` there (confirmed both ways against `/api/v1/user`: bare → 401, `Bearer`-prefixed → 200), unlike the other feeds' username/token basic-auth pairs. The publish succeeded on retry with the prefix exported shell-locally; the permanent fix was then applied to the operator's `~/.zshenv` (outside this repo; prior file backed up `0600` as `~/.zshenv.bak-cli39`) and re-verified via a `Bearer`-authed `GET /api/v1/user` → `200`. The requirement is now documented in `docs/ClientPackaging.md`'s Rust section, and that doc's "Versioning" section carries the published-coordinates record so the next bump starts from the real registry state rather than a re-check. No source, proto, script, or test change — publish execution plus docs. Tracking: `docs/ClientPackaging.md` and this row (CLI-39's register rows in both this file and `50-clients.md` were already plain `Done` with no publish-pending annotation to clear). |
|
||||
| 2026-08-07 | **SEC-36 → fully `Done`** — the operator half (rotate the dev LDAP service-account credential) executed live per `docs/runbooks/SEC-36-ldap-credential-rotation.md`. A new password was generated, the `serviceaccount` `passsha256` in `scadaproj/infra/glauth/config.toml` replaced, and the shared GLAuth recreated on `10.100.0.35`, so the value this repo's git history permanently discloses (live in the directory since 2026-06-04; not reproduced here) **no longer binds** `dc=zb,dc=local` — the load-bearing half of the finding. The new value lives only in the three channels the design named: the GLAuth `passsha256` (committed in `scadaproj`, commit `aada53b`), the NSSM environment on `10.100.0.48`, and each dev box's user-secrets (set on this Mac); it is in no file of this repo. The retired plaintext was also scrubbed from the `scadaproj` glauth comments (`config.toml`, `docker-compose.yml`, `README.md`) and from the docker host's live `docker-compose.yml`; the host's `*.bak-sec36` rollback copies deliberately keep it. **Three runbook facts corrected** in a dated block at its top: (1) step 3's `cd ~/Desktop/scadaproj/infra/glauth` on the docker host is stale — the stack runs from `/home/dohertj2/zb-glauth` (container `zb-shared-glauth`), fed by the `scp` deploy in `scadaproj/infra/glauth/README.md`; (2) `wonder-app-vd03` is out of scope **on documentary evidence**, not merely unchecked — its gateway binds the ScadaBridge/ScadaLink local GLAuth under `dc=scadalink`/`dc=scadabridge`, a different directory that never held this credential (and the host is unreachable from the dev network), so no env var was staged there; (3) the "3-fail / 10-minute per-IP lockout" caution is inert for this instance — `config.toml:14` sets `LimitFailedBinds = false`. **One Done criterion is met with a caveat:** the new value is staged on windev (10th `AppEnvironmentExtra` entry on the `MxAccessGw` NSSM service) but the runbook's primary dashboard `/login` check **could not run** — windev's gateway is crash-looping on an unrelated pre-existing fault (deployed Server binary of 2026-06-25 predates the 2026-07-15 auth-DB migration, so it opens a schema-3 DB it supports only at schema 2 and aborts; ~10k Hosting-failed events/day since at least 08-06). Verified instead by the equivalent primitive: a direct `ldapsearch` bind as `cn=serviceaccount,dc=zb,dc=local` with the new value against `10.100.0.35:3893` succeeded and returned the `multi-role` entry — the same search bind the dashboard performs. The deferred check is recorded in the runbook with its completion path. **Two new next-cycle candidate findings** filed in `90-candidate-findings-next-cycle.md`: NEXT-06 (`DashboardLdapLiveTests` fixture drift — its only positive test binds `admin`/`admin123` while the directory's `admin` password is the standard dev one, and fixture user `readonly` does not exist in the GLAuth config, so the suite carries **no positive-proof coverage** of the service-account bind and could not have substituted for the dashboard check) and NEXT-07 (the windev stale deployment / crash loop above). Tracking: both registers' SEC-36 rows, the pending-operator-actions list in `90-candidate-findings-next-cycle.md`, and this row. |
|
||||
| 2026-08-07 | **NEXT-07 resolved and SEC-36's deferred dashboard check discharged** — the two loose ends the SEC-36 row above left open. windev (`10.100.0.48`) was repaired by a fresh portable framework-dependent publish of `origin/main` (`a346d51`) built in a clean clone at `C:\build\mxgw-redeploy`, deployed to `C:\publish\mxaccessgw\Server-20260807` with the `MxAccessGw` NSSM service repointed; the service holds a stable PID with `5120`/`5130` listening, a worker spawned, the Galaxy snapshot restored (129 objects / 56,731 attributes) and a clean event log. The predicted root cause is confirmed: the deployed 2026-06-25 build carried `ZB.MOM.WW.Auth.ApiKeys` 0.1.2.0 (auth-DB schema 2) against a database stamped at schema 3 on 2026-07-15 by an ephemeral run of newer code — schema 3 is the **current** shared-lib version (`SqliteAuthSchema.CurrentVersion=3` in Auth 0.1.5), so redeploying forward was the fix and the DB was left untouched; `gateway-auth.db.bak-next07` (+`-wal`/`-shm`) and the previous `Server` publish directory stand as rollback. Two side effects: the old deploy's `appsettings.json` carried the LDAP bind password in **plaintext on disk**, while the new one keeps the repo's `${secret:ldap/mxgateway/bind}` token with the NSSM environment supplying the value; and the redeploy tripped the SEC-06 `Ldap:Transport=None` production hard-stop (`GatewayOptionsValidator.cs:178`), resolved by relabelling `DOTNET_ENVIRONMENT` to `Staging` (that entry only — the other nine preserved byte-identical), which matches what windev actually is: a host running `Dashboard:DisableLogin=true` against a GLAuth with no TLS. SEC-06 is untouched for real production hosts, and the posture gap that relabelling defers is filed as NEXT-08. With windev live, **SEC-36's deferred check ran for real**: a foreground run of the new build with `Dashboard:DisableLogin=false` as a process-env-only override served `GET /login` (200 + antiforgery token), accepted `POST /auth/login` as `multi-role`/`password` (302 to `/` + `MxGatewayDashboard` cookie), rendered the admin nav on the authenticated `GET /`, and redirected the anonymous control to `/login?ReturnUrl=%2F` — proving the rotated credential through the real `DashboardAuthenticator` search bind on the deployed host, not just by `ldapsearch`. Also executed host-side: runner-1 on `10.100.0.35` moved to the same `0600` token-file mount as runner-2 (identity preserved), leaving the shared instance registration token — proven still live — as a **pending operator UI reset**. Three new candidate findings filed: NEXT-08 (GLAuth has no TLS, so SEC-06 makes `docs/GatewayConfiguration.md`'s "deployed hosts must set `Ldaps` or `StartTls`" unsatisfiable), NEXT-09 (`src/Directory.Build.props:29` stamps git's error text into `InformationalVersion` on Windows), NEXT-10 (`glauth.md`'s pre-provisioned-user table contradicts the directory and the rest of its own file — the root cause of NEXT-06, deliberately deferred because reconciling it sweeps the OPC-UA group taxonomy). Tracking: `90-candidate-findings-next-cycle.md` (NEXT-07 struck, NEXT-08/09/10 added, operator-action list), `docs/runbooks/SEC-36-ldap-credential-rotation.md` Correction 3, `docs/runbooks/TST-30-second-ci-runner.md`, `docs/GatewayConfiguration.md` SEC-35 section. |
|
||||
|
||||
Reference in New Issue
Block a user