fix(docker+tests): restore rig LDAP login via redundant local GLAuth pair + FallbackServers

The rig pointed at the shared 10.100.0.35 GLAuth whose serviceaccount password
was rotated (SEC-36), so central login had been failing ('Authentication service
is misconfigured') and a TEMP DisableLogin workaround was pending. Central nodes
now point at the local redundant pair (scadaproj/infra/glauth-redundant,
host.docker.internal:3893 + FallbackServers :3894), where the dev bind password
is correct — live-gated on the redeployed rig: login OK, primary-kill failover,
sticky preference (bind-count proven), walk-back on backup-kill.

AuthFlowTests factory bound as cn=admin for search-then-bind, but the current
directory grants the search capability only to serviceaccount (admin searches
return 50 Insufficient access) — stale since the GLAuth config evolved; the test
had been skipping on the closed port and failed once anything answered :3893.
Now binds as serviceaccount; AuthFlowTests 5/5 against the pair.
This commit is contained in:
Joseph Doherty
2026-08-13 08:49:05 -04:00
parent d28824d967
commit b71fbae36e
4 changed files with 18 additions and 9 deletions
@@ -29,8 +29,10 @@
}, },
"Security": { "Security": {
"Ldap": { "Ldap": {
"Server": "10.100.0.35", "Server": "host.docker.internal",
"Port": 3893, "Port": 3893,
"FallbackServers": [ "host.docker.internal:3894" ],
"_comment_FallbackServers": "Redundant local GLAuth pair (scadaproj/infra/glauth-redundant): zb-ldap-primary :3893 + zb-ldap-backup :3894 on the Docker host. Replaces the shared 10.100.0.35 GLAuth, whose serviceaccount password was rotated (SEC-36) away from the rig's dev bind creds. Also live-exercises Auth 0.2.x FallbackServers sticky failover.",
"Transport": "None", "Transport": "None",
"AllowInsecure": true, "AllowInsecure": true,
"SearchBase": "dc=zb,dc=local", "SearchBase": "dc=zb,dc=local",
@@ -29,8 +29,10 @@
}, },
"Security": { "Security": {
"Ldap": { "Ldap": {
"Server": "10.100.0.35", "Server": "host.docker.internal",
"Port": 3893, "Port": 3893,
"FallbackServers": [ "host.docker.internal:3894" ],
"_comment_FallbackServers": "Redundant local GLAuth pair (scadaproj/infra/glauth-redundant): zb-ldap-primary :3893 + zb-ldap-backup :3894 on the Docker host. Replaces the shared 10.100.0.35 GLAuth, whose serviceaccount password was rotated (SEC-36) away from the rig's dev bind creds. Also live-exercises Auth 0.2.x FallbackServers sticky failover.",
"Transport": "None", "Transport": "None",
"AllowInsecure": true, "AllowInsecure": true,
"SearchBase": "dc=zb,dc=local", "SearchBase": "dc=zb,dc=local",
@@ -106,8 +106,10 @@ public class AuthFlowTests : IClassFixture<ScadaBridgeWebApplicationFactory>
[Fact] [Fact]
public async Task LoginEndpoint_WithValidLdapCredentials_SetsCookieAndRedirects() public async Task LoginEndpoint_WithValidLdapCredentials_SetsCookieAndRedirects()
{ {
// Requires GLAuth test LDAP server: docker compose -f infra/docker-compose.yml up -d glauth // Requires a local GLAuth on localhost:3893 — the redundant test pair:
// GLAuth runs on localhost:3893, baseDN dc=zb,dc=local, all passwords "password" // cd ~/Desktop/scadaproj/infra/glauth-redundant && docker compose up -d
// (baseDN dc=zb,dc=local, user passwords "password"; the old infra/docker-compose.yml
// glauth service no longer exists — LDAP moved out of that stack.)
if (!await IsLdapAvailableAsync()) if (!await IsLdapAvailableAsync())
{ {
// Skip gracefully if GLAuth not running — not a test failure // Skip gracefully if GLAuth not running — not a test failure
@@ -54,11 +54,14 @@ public class ScadaBridgeWebApplicationFactory : WebApplicationFactory<Program>
["ScadaBridge__Security__Ldap__Transport"] = "None", ["ScadaBridge__Security__Ldap__Transport"] = "None",
["ScadaBridge__Security__Ldap__AllowInsecure"] = "true", ["ScadaBridge__Security__Ldap__AllowInsecure"] = "true",
["ScadaBridge__Security__Ldap__SearchBase"] = "dc=zb,dc=local", ["ScadaBridge__Security__Ldap__SearchBase"] = "dc=zb,dc=local",
// GLAuth places users at cn=<name>,ou=<group>,ou=users,dc=... — a service // Search-then-bind needs an account holding GLAuth's `search` capability, which
// account is configured to enable the shared service's search-then-bind: // the current directory grants ONLY to `serviceaccount` (binding as `admin`
// resolve the user's real DN by (UserNameAttribute=<name>) lookup, then bind it. // succeeds but its searches return `50 Insufficient access`, i.e. auth reads as
["ScadaBridge__Security__Ldap__ServiceAccountDn"] = "cn=admin,ou=SCADA-Admins,ou=users,dc=zb,dc=local", // "service unavailable"). Matches the local redundant pair
["ScadaBridge__Security__Ldap__ServiceAccountPassword"] = "password", // (scadaproj/infra/glauth-redundant), whose serviceaccount password is the
// well-known dev value — the shared 10.100.0.35 instance's is rotated (SEC-36).
["ScadaBridge__Security__Ldap__ServiceAccountDn"] = "cn=serviceaccount,dc=zb,dc=local",
["ScadaBridge__Security__Ldap__ServiceAccountPassword"] = "serviceaccount123",
}; };
foreach (var (key, value) in envVars) foreach (var (key, value) in envVars)