feat(infra): Docker-compose configuration and environment-specific security guards for Beta deployment to Test environment #56

Merged
mjasin merged 12 commits from infra/beta-deploy-test into develop 2026-06-01 17:17:46 +00:00
Showing only changes of commit a672a868b4 - Show all commits
@@ -72,8 +72,28 @@ public static class DbInitializer
var adminPassword = configuration?["Nexus:AdminPassword"]
mjasin marked this conversation as resolved
Review

🔴 Blocking — Triple-layer fallback exposes hardcoded default credential in production

The current fallback chain Nexus:AdminPasswordNEXUS_ADMIN_PASSWORD (from IConfiguration) → Environment.GetEnvironmentVariable("NEXUS_ADMIN_PASSWORD")"Admin123!" is dangerous. If NEXUS_ADMIN_PASSWORD is not injected at container startup (e.g. operator error, mis-spelled var), the process will silently seed the admin account with "Admin123!" without any warning — a critical security regression in Test/Prod.

The Environment.GetEnvironmentVariable call is also redundant because IConfiguration in ASP.NET Core already reads environment variables. You only need two keys, and the final fallback must only be allowed in Development.

Suggested fix:

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var adminPassword = configuration?["Nexus:AdminPassword"]
                    ?? configuration?["NEXUS_ADMIN_PASSWORD"];

if (string.IsNullOrWhiteSpace(adminPassword))
{
    if (environment == "Development")
    {
        adminPassword = "Admin123!";
    }
    else
    {
        throw new InvalidOperationException(
            "NEXUS_ADMIN_PASSWORD must be set for non-Development environments.");
    }
}
🔴 **Blocking — Triple-layer fallback exposes hardcoded default credential in production** The current fallback chain `Nexus:AdminPassword` → `NEXUS_ADMIN_PASSWORD` (from `IConfiguration`) → `Environment.GetEnvironmentVariable("NEXUS_ADMIN_PASSWORD")` → `"Admin123!"` is dangerous. If `NEXUS_ADMIN_PASSWORD` is not injected at container startup (e.g. operator error, mis-spelled var), the process will silently seed the admin account with `"Admin123!"` without any warning — a critical security regression in Test/Prod. The `Environment.GetEnvironmentVariable` call is also redundant because `IConfiguration` in ASP.NET Core already reads environment variables. You only need **two** keys, and the final fallback must only be allowed in `Development`. **Suggested fix:** ```csharp var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"; var adminPassword = configuration?["Nexus:AdminPassword"] ?? configuration?["NEXUS_ADMIN_PASSWORD"]; if (string.IsNullOrWhiteSpace(adminPassword)) { if (environment == "Development") { adminPassword = "Admin123!"; } else { throw new InvalidOperationException( "NEXUS_ADMIN_PASSWORD must be set for non-Development environments."); } } ```
Review

🔴 Blocking — Still unresolved. Silent credential fallback in non-Development environments.

This code is identical to the original. The "Admin123!" default will still be reached silently in Test/Production if NEXUS_ADMIN_PASSWORD is absent (e.g. from a typo in the .env file or a missing Docker secret). The docker-compose.test.yml does enforce ${NEXUS_ADMIN_PASSWORD:?...} at the compose level, but this C# fallback provides a false safety net that can be triggered by non-compose deployments (e.g., direct kubectl apply).

Please add the environment check before applying the fallback:

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var adminPassword = configuration?["Nexus:AdminPassword"]
                    ?? configuration?["NEXUS_ADMIN_PASSWORD"];

if (string.IsNullOrWhiteSpace(adminPassword))
{
    if (environment == "Development")
    {
        adminPassword = "Admin123!";
    }
    else
    {
        throw new InvalidOperationException(
            "NEXUS_ADMIN_PASSWORD must be configured for non-Development environments. Aborting startup.");
    }
}

This also eliminates the redundant Environment.GetEnvironmentVariable call, since IConfiguration already reads env vars.

🔴 **Blocking — Still unresolved. Silent credential fallback in non-Development environments.** This code is identical to the original. The `"Admin123!"` default will still be reached silently in Test/Production if `NEXUS_ADMIN_PASSWORD` is absent (e.g. from a typo in the `.env` file or a missing Docker secret). The `docker-compose.test.yml` does enforce `${NEXUS_ADMIN_PASSWORD:?...}` at the compose level, but this C# fallback provides a false safety net that can be triggered by non-compose deployments (e.g., direct `kubectl apply`). Please add the environment check before applying the fallback: ```csharp var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"; var adminPassword = configuration?["Nexus:AdminPassword"] ?? configuration?["NEXUS_ADMIN_PASSWORD"]; if (string.IsNullOrWhiteSpace(adminPassword)) { if (environment == "Development") { adminPassword = "Admin123!"; } else { throw new InvalidOperationException( "NEXUS_ADMIN_PASSWORD must be configured for non-Development environments. Aborting startup."); } } ``` This also eliminates the redundant `Environment.GetEnvironmentVariable` call, since `IConfiguration` already reads env vars.
?? configuration?["NEXUS_ADMIN_PASSWORD"]
?? Environment.GetEnvironmentVariable("NEXUS_ADMIN_PASSWORD")
?? "Admin123!";
?? Environment.GetEnvironmentVariable("NEXUS_ADMIN_PASSWORD");
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? "Development";
var isDevelopment = string.Equals(env, "Development", StringComparison.OrdinalIgnoreCase);
if (string.IsNullOrEmpty(adminPassword))
{
if (!isDevelopment)
{
throw new InvalidOperationException(
"CRITICAL SECURITY ERROR: Admin password is NOT configured! " +
"In non-Development environments (e.g. Test/Production), the admin password must be explicitly set " +
"via configuration ('Nexus:AdminPassword' or 'NEXUS_ADMIN_PASSWORD') or environment variables. " +
"Seeding aborted to prevent insecure credentials fallback.");
}
Console.WriteLine("[Seeder] WARNING: Admin password is not set. Falling back to default weak password 'Admin123!' in Development environment.");
adminPassword = "Admin123!";
}
adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, adminPassword);
dbContext.Users.Add(adminUser);