infra(docker-env2)+infra(docker): init:true + secrets posture parity for env2; regen-proto EXIT trap (arch-review 2026-08-19 P8-2 + P8-3 + P8-4)
This commit is contained in:
@@ -20,20 +20,27 @@
|
|||||||
"MinNrOfMembers": 1
|
"MinNrOfMembers": 1
|
||||||
},
|
},
|
||||||
"Database": {
|
"Database": {
|
||||||
"ConfigurationDb": "Server=scadabridge-mssql,1433;Database=ScadaBridgeConfig2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true",
|
"_comment": "ConfigurationDb/MachineDataDb (which carry the dev SQL password) are supplied as ScadaBridge__Database__* whole-key env overrides in docker-env2/docker-compose.yml (dev-only-insecure, mirroring docker/docker-compose.yml and the ApiKeyPepper convention). The same dev password already ships committed for the sibling scadabridge-mssql container (infra/docker-compose.yml MSSQL_SA_PASSWORD) — this is consolidation, not new exposure. Env overrides layer over JSON before the ${secret:} expander runs, so the dev cluster boots with no KEK/secret store. Real/prod config uses ${secret:} tokens in src/.../appsettings.Central.json (T4)."
|
||||||
"MachineDataDb": "Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
|
||||||
},
|
},
|
||||||
"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 — mirrors docker/central-node-a/appsettings.Central.json. Replaces the shared 10.100.0.35 GLAuth, whose serviceaccount password was rotated (SEC-36) away from the rig's dev bind creds; env2 hit this directly (every login failing) before this fix.",
|
||||||
"Transport": "None",
|
"Transport": "None",
|
||||||
"AllowInsecure": true,
|
"AllowInsecure": true,
|
||||||
"SearchBase": "dc=zb,dc=local",
|
"SearchBase": "dc=zb,dc=local",
|
||||||
"ServiceAccountDn": "cn=serviceaccount,dc=zb,dc=local",
|
"ServiceAccountDn": "cn=serviceaccount,dc=zb,dc=local",
|
||||||
"ServiceAccountPassword": "serviceaccount123"
|
"_comment_ServiceAccountPassword": "Supplied as ScadaBridge__Security__Ldap__ServiceAccountPassword env override in docker-env2/docker-compose.yml (dev-only-insecure)."
|
||||||
},
|
},
|
||||||
"JwtSigningKey": "scadabridge-env2-dev-jwt-signing-key-must-be-at-least-32-characters-long",
|
"Auth": {
|
||||||
|
"DisableLogin": false,
|
||||||
|
"User": "multi-role"
|
||||||
|
},
|
||||||
|
"_comment_JwtSigningKey": "Supplied as ScadaBridge__Security__JwtSigningKey env override in docker-env2/docker-compose.yml (dev-only-insecure).",
|
||||||
"JwtExpiryMinutes": 15,
|
"JwtExpiryMinutes": 15,
|
||||||
"IdleTimeoutMinutes": 30,
|
"IdleTimeoutMinutes": 30,
|
||||||
"RequireHttpsCookie": false,
|
"RequireHttpsCookie": false,
|
||||||
|
|||||||
@@ -20,20 +20,27 @@
|
|||||||
"MinNrOfMembers": 1
|
"MinNrOfMembers": 1
|
||||||
},
|
},
|
||||||
"Database": {
|
"Database": {
|
||||||
"ConfigurationDb": "Server=scadabridge-mssql,1433;Database=ScadaBridgeConfig2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true",
|
"_comment": "ConfigurationDb/MachineDataDb (which carry the dev SQL password) are supplied as ScadaBridge__Database__* whole-key env overrides in docker-env2/docker-compose.yml (dev-only-insecure, mirroring docker/docker-compose.yml and the ApiKeyPepper convention). The same dev password already ships committed for the sibling scadabridge-mssql container (infra/docker-compose.yml MSSQL_SA_PASSWORD) — this is consolidation, not new exposure. Env overrides layer over JSON before the ${secret:} expander runs, so the dev cluster boots with no KEK/secret store. Real/prod config uses ${secret:} tokens in src/.../appsettings.Central.json (T4)."
|
||||||
"MachineDataDb": "Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
|
||||||
},
|
},
|
||||||
"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 — mirrors docker/central-node-b/appsettings.Central.json. Replaces the shared 10.100.0.35 GLAuth, whose serviceaccount password was rotated (SEC-36) away from the rig's dev bind creds; env2 hit this directly (every login failing) before this fix.",
|
||||||
"Transport": "None",
|
"Transport": "None",
|
||||||
"AllowInsecure": true,
|
"AllowInsecure": true,
|
||||||
"SearchBase": "dc=zb,dc=local",
|
"SearchBase": "dc=zb,dc=local",
|
||||||
"ServiceAccountDn": "cn=serviceaccount,dc=zb,dc=local",
|
"ServiceAccountDn": "cn=serviceaccount,dc=zb,dc=local",
|
||||||
"ServiceAccountPassword": "serviceaccount123"
|
"_comment_ServiceAccountPassword": "Supplied as ScadaBridge__Security__Ldap__ServiceAccountPassword env override in docker-env2/docker-compose.yml (dev-only-insecure)."
|
||||||
},
|
},
|
||||||
"JwtSigningKey": "scadabridge-env2-dev-jwt-signing-key-must-be-at-least-32-characters-long",
|
"Auth": {
|
||||||
|
"DisableLogin": false,
|
||||||
|
"User": "multi-role"
|
||||||
|
},
|
||||||
|
"_comment_JwtSigningKey": "Supplied as ScadaBridge__Security__JwtSigningKey env override in docker-env2/docker-compose.yml (dev-only-insecure).",
|
||||||
"JwtExpiryMinutes": 15,
|
"JwtExpiryMinutes": 15,
|
||||||
"IdleTimeoutMinutes": 30,
|
"IdleTimeoutMinutes": 30,
|
||||||
"RequireHttpsCookie": false,
|
"RequireHttpsCookie": false,
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ x-secrets-store-env: &secrets-store-env
|
|||||||
services:
|
services:
|
||||||
central-a:
|
central-a:
|
||||||
image: scadabridge:latest
|
image: scadabridge:latest
|
||||||
|
# An init process (tini) as PID 1, so a crashed dotnet process actually dies
|
||||||
|
# instead of wedging the container at 100% CPU with the restart policy never
|
||||||
|
# firing — mirrors docker/docker-compose.yml's init: true (ScadaBridge#34).
|
||||||
|
init: true
|
||||||
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
||||||
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
||||||
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
||||||
@@ -27,6 +31,25 @@ services:
|
|||||||
# pepper per the "different per environment" guidance; real deployments inject a
|
# pepper per the "different per environment" guidance; real deployments inject a
|
||||||
# true secret out-of-band, never from source control. Both Central nodes share it.
|
# true secret out-of-band, never from source control. Both Central nodes share it.
|
||||||
ScadaBridge__InboundApi__ApiKeyPepper: "dev-only-insecure-pepper-env2-cluster-0001"
|
ScadaBridge__InboundApi__ApiKeyPepper: "dev-only-insecure-pepper-env2-cluster-0001"
|
||||||
|
# DEV-ONLY local-cluster secrets — NOT real secrets. Relocated out of the mounted
|
||||||
|
# appsettings.Central.json (mirrors docker/docker-compose.yml's G-4 T5 secrets
|
||||||
|
# cleanup) so that app-config file carries no plaintext credentials. Env overrides
|
||||||
|
# layer over JSON BEFORE the ${secret:} pre-host expander runs. The SQL password is
|
||||||
|
# the same value already committed for the sibling scadabridge-mssql container
|
||||||
|
# (infra/docker-compose.yml MSSQL_SA_PASSWORD) and for docker/'s central nodes —
|
||||||
|
# consolidation, not new exposure; env2's Config2/MachineData2 databases live on
|
||||||
|
# that SAME shared mssql container. Real deployments use ${secret:} tokens +
|
||||||
|
# a seeded store.
|
||||||
|
ScadaBridge__Database__ConfigurationDb: "Server=scadabridge-mssql,1433;Database=ScadaBridgeConfig2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
||||||
|
ScadaBridge__Database__MachineDataDb: "Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
||||||
|
# LDAP bind credential for the redundant local GLAuth pair (scadaproj/infra/glauth-redundant,
|
||||||
|
# host.docker.internal:3893/:3894 — see central-node-a/appsettings.Central.json). Same
|
||||||
|
# value docker/'s central nodes use for the SAME local pair; the shared 10.100.0.35
|
||||||
|
# GLAuth's serviceaccount password was rotated (SEC-36) and is no longer valid.
|
||||||
|
ScadaBridge__Security__Ldap__ServiceAccountPassword: "serviceaccount123"
|
||||||
|
# DEV-ONLY local-cluster secret — NOT a real secret. Distinct from docker/'s JWT key
|
||||||
|
# so sessions cannot cross environments (see docker-env2/README.md).
|
||||||
|
ScadaBridge__Security__JwtSigningKey: "scadabridge-env2-dev-jwt-signing-key-must-be-at-least-32-characters-long"
|
||||||
# DEV-ONLY gRPC control-plane preshared key for site-x — NOT a real secret.
|
# DEV-ONLY gRPC control-plane preshared key for site-x — NOT a real secret.
|
||||||
# Must match ScadaBridge:Communication:GrpcPsk in site-x-node-*/appsettings.Site.json.
|
# Must match ScadaBridge:Communication:GrpcPsk in site-x-node-*/appsettings.Site.json.
|
||||||
# Production seeds SB-GRPC-PSK-<siteId> into the secret store instead.
|
# Production seeds SB-GRPC-PSK-<siteId> into the secret store instead.
|
||||||
@@ -47,6 +70,7 @@ services:
|
|||||||
|
|
||||||
central-b:
|
central-b:
|
||||||
image: scadabridge:latest
|
image: scadabridge:latest
|
||||||
|
init: true # PID-1 crash wedge, ScadaBridge#34 — see central-a
|
||||||
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
||||||
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
||||||
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
||||||
@@ -63,6 +87,25 @@ services:
|
|||||||
# pepper per the "different per environment" guidance; real deployments inject a
|
# pepper per the "different per environment" guidance; real deployments inject a
|
||||||
# true secret out-of-band, never from source control. Both Central nodes share it.
|
# true secret out-of-band, never from source control. Both Central nodes share it.
|
||||||
ScadaBridge__InboundApi__ApiKeyPepper: "dev-only-insecure-pepper-env2-cluster-0001"
|
ScadaBridge__InboundApi__ApiKeyPepper: "dev-only-insecure-pepper-env2-cluster-0001"
|
||||||
|
# DEV-ONLY local-cluster secrets — NOT real secrets. Relocated out of the mounted
|
||||||
|
# appsettings.Central.json (mirrors docker/docker-compose.yml's G-4 T5 secrets
|
||||||
|
# cleanup) so that app-config file carries no plaintext credentials. Env overrides
|
||||||
|
# layer over JSON BEFORE the ${secret:} pre-host expander runs. The SQL password is
|
||||||
|
# the same value already committed for the sibling scadabridge-mssql container
|
||||||
|
# (infra/docker-compose.yml MSSQL_SA_PASSWORD) and for docker/'s central nodes —
|
||||||
|
# consolidation, not new exposure; env2's Config2/MachineData2 databases live on
|
||||||
|
# that SAME shared mssql container. Real deployments use ${secret:} tokens +
|
||||||
|
# a seeded store.
|
||||||
|
ScadaBridge__Database__ConfigurationDb: "Server=scadabridge-mssql,1433;Database=ScadaBridgeConfig2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
||||||
|
ScadaBridge__Database__MachineDataDb: "Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData2;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true"
|
||||||
|
# LDAP bind credential for the redundant local GLAuth pair (scadaproj/infra/glauth-redundant,
|
||||||
|
# host.docker.internal:3893/:3894 — see central-node-b/appsettings.Central.json). Same
|
||||||
|
# value docker/'s central nodes use for the SAME local pair; the shared 10.100.0.35
|
||||||
|
# GLAuth's serviceaccount password was rotated (SEC-36) and is no longer valid.
|
||||||
|
ScadaBridge__Security__Ldap__ServiceAccountPassword: "serviceaccount123"
|
||||||
|
# DEV-ONLY local-cluster secret — NOT a real secret. Distinct from docker/'s JWT key
|
||||||
|
# so sessions cannot cross environments (see docker-env2/README.md).
|
||||||
|
ScadaBridge__Security__JwtSigningKey: "scadabridge-env2-dev-jwt-signing-key-must-be-at-least-32-characters-long"
|
||||||
# DEV-ONLY gRPC control-plane preshared key for site-x — NOT a real secret.
|
# DEV-ONLY gRPC control-plane preshared key for site-x — NOT a real secret.
|
||||||
# Must match ScadaBridge:Communication:GrpcPsk in site-x-node-*/appsettings.Site.json.
|
# Must match ScadaBridge:Communication:GrpcPsk in site-x-node-*/appsettings.Site.json.
|
||||||
# Production seeds SB-GRPC-PSK-<siteId> into the secret store instead.
|
# Production seeds SB-GRPC-PSK-<siteId> into the secret store instead.
|
||||||
@@ -83,6 +126,7 @@ services:
|
|||||||
|
|
||||||
site-x-a:
|
site-x-a:
|
||||||
image: scadabridge:latest
|
image: scadabridge:latest
|
||||||
|
init: true # PID-1 crash wedge, ScadaBridge#34 — see central-a
|
||||||
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
||||||
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
||||||
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
||||||
@@ -111,6 +155,7 @@ services:
|
|||||||
|
|
||||||
site-x-b:
|
site-x-b:
|
||||||
image: scadabridge:latest
|
image: scadabridge:latest
|
||||||
|
init: true # PID-1 crash wedge, ScadaBridge#34 — see central-a
|
||||||
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
# CoordinatedShutdown needs cluster-leave (15s budget) + cluster-exiting +
|
||||||
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
# actor-system-terminate + Serilog flush; the 10s SIGTERM default SIGKILLed
|
||||||
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
# mid-drain, turning every redeploy into the crash path (review 01 [Medium]).
|
||||||
|
|||||||
+20
-8
@@ -50,12 +50,25 @@ if [[ ! -f "$CSPROJ" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Backup so we can always restore the comment state on failure. Leaving the
|
# Backup so we can always restore the comment state, however this script ends.
|
||||||
# csproj with an active Protobuf item is the one outcome that breaks Docker, so
|
# Leaving the csproj with an active Protobuf item is the one outcome that breaks
|
||||||
# every exit path restores this copy.
|
# Docker, so every exit path restores this copy — success, a failed `dotnet
|
||||||
|
# build` (ERR), or Ctrl-C (INT/TERM) mid-build. An EXIT trap covers all of
|
||||||
|
# those (bash runs it on any shell termination, signals included), and the
|
||||||
|
# guard on $BACKUP's existence makes it idempotent: the normal success path
|
||||||
|
# below restores explicitly for prompt feedback, so by the time the trap fires
|
||||||
|
# on a clean exit $BACKUP is already gone and it's a no-op instead of a second
|
||||||
|
# restore/rm.
|
||||||
BACKUP="$(mktemp)"
|
BACKUP="$(mktemp)"
|
||||||
cp "$CSPROJ" "$BACKUP"
|
cp "$CSPROJ" "$BACKUP"
|
||||||
trap 'cp "$BACKUP" "$CSPROJ"; rm -f "$BACKUP"; echo "Restored csproj from backup."' ERR
|
restore_csproj() {
|
||||||
|
if [[ -f "$BACKUP" ]]; then
|
||||||
|
cp "$BACKUP" "$CSPROJ"
|
||||||
|
rm -f "$BACKUP"
|
||||||
|
echo "Restored csproj from backup."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap restore_csproj EXIT
|
||||||
|
|
||||||
# 1. Inject an ItemGroup holding just the selected protos, immediately before
|
# 1. Inject an ItemGroup holding just the selected protos, immediately before
|
||||||
# the closing </Project>. The documented commented-out block is left alone.
|
# the closing </Project>. The documented commented-out block is left alone.
|
||||||
@@ -117,10 +130,9 @@ if [[ "$TARGET" == "sitecommand" || "$TARGET" == "all" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. Restore the backed-up csproj — i.e. drop the injected ItemGroup — so Docker
|
# 5. Restore the backed-up csproj — i.e. drop the injected ItemGroup — so Docker
|
||||||
# builds keep working.
|
# builds keep working. (Also covered by the EXIT trap above; calling it here
|
||||||
cp "$BACKUP" "$CSPROJ"
|
# too just gives the success path its "Restored..." feedback before "Done".)
|
||||||
rm -f "$BACKUP"
|
restore_csproj
|
||||||
trap - ERR
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Done. Review and commit:"
|
echo "Done. Review and commit:"
|
||||||
|
|||||||
Reference in New Issue
Block a user