From 4d7ff74236ed72085d37dccf826fc844335528e1 Mon Sep 17 00:00:00 2001 From: vampi62 <104321401+vampi62@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:13:08 +0100 Subject: [PATCH] Refactor Vault config, generator and API fixes --- docs/generator/index.html | 2 +- docs/generator/js/config.js | 2 +- docs/generator/js/generators.js | 48 ++++++---- .../VaultConfigurationExtensions.cs | 92 ++++++++++++++----- electrostoreAPI/Program.cs | 14 +-- .../Properties/launchSettings.json | 28 +++--- 6 files changed, 118 insertions(+), 68 deletions(-) diff --git a/docs/generator/index.html b/docs/generator/index.html index c5e5328..7bc964b 100644 --- a/docs/generator/index.html +++ b/docs/generator/index.html @@ -384,7 +384,7 @@

HashiCorp Vault Configurati
- + Path in Vault where secrets are stored
diff --git a/docs/generator/js/config.js b/docs/generator/js/config.js index 6de0725..8ae24bd 100644 --- a/docs/generator/js/config.js +++ b/docs/generator/js/config.js @@ -176,7 +176,7 @@ function collectConfig(formData) { // Generate random password function generateRandomPassword(length) { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*_+-|;,.<>?'; + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#%^&*_+-|;,.<>?'; let password = ''; for (let i = 0; i < length; i++) { password += chars.charAt(Math.floor(Math.random() * chars.length)); diff --git a/docs/generator/js/generators.js b/docs/generator/js/generators.js index f32d14a..6a530a2 100644 --- a/docs/generator/js/generators.js +++ b/docs/generator/js/generators.js @@ -38,7 +38,7 @@ services:`; - "traefik.enable=true" - "traefik.http.routers.api.rule=${apiRule}" - "traefik.http.routers.api.entrypoints=${entrypoint}" - - "traefik.http.services.api.loadbalancer.server.port=80"`; + - "traefik.http.services.api.loadbalancer.server.port=8080"`; if (config.traefik.middlewares) { compose += ` @@ -52,7 +52,7 @@ services:`; } else { compose += ` ports: - - "\${API_PORT:-${config.apiPort}}:80"`; + - "\${API_PORT:-${config.apiPort}}:8080"`; } compose += ` @@ -234,6 +234,7 @@ services:`; - "3900:3900" - "3901:3901" - "3902:3902" + - "3903:3903" volumes: - ./config/garage/garage.toml:/etc/garage.toml:ro - garage-data:/data @@ -284,12 +285,10 @@ function generateAppsettings(config) { let connectionString; if (config.useMariaDB) { - connectionString = `Server=mariadb;Port=3306;Database=${config.mariadb.database};User=${config.mariadb.user};Password= - ${config.useVault ? '{{vault:mariadb_password}}' : config.mariadb.password};`; + connectionString = `Server=mariadb;Port=3306;Database=${config.mariadb.database};User=${config.mariadb.user};Password=${config.useVault ? '{{vault:mariadb_password}}' : config.mariadb.password};`; } else { const db = config.mariadbExternal; - connectionString = `Server=${db.host};Port=${db.port};Database=${db.database};User=${db.user};Password= - ${config.useVault ? '{{vault:mariadb_password}}' : db.password};`; + connectionString = `Server=${db.host};Port=${db.port};Database=${db.database};User=${db.user};Password=${config.useVault ? '{{vault:mariadb_password}}' : db.password};`; } settings.ConnectionStrings = { @@ -317,7 +316,7 @@ function generateAppsettings(config) { if (config.useS3) { settings.S3 = { "Enable": true, - "Endpoint": "http://garage:3900", + "Endpoint": "garage:3900", "AccessKey": config.useVault ? "{{vault:s3_access_key}}" : config.s3.accessKey, "SecretKey": config.useVault ? "{{vault:s3_secret_key}}" : config.s3.secretKey, "Bucket": config.s3.bucket, @@ -485,23 +484,31 @@ docker exec vault vault secrets enable -version=2 -path=${config.vault.mountPoin echo "Storing secrets in Vault..." `; - script += `docker exec vault vault kv put ${config.vault.mountPoint}/${config.vault.path} mariadb_password=" - ${config.useMariaDB ? config.mariadb.password : config.mariadbExternal.password}" + script += `docker exec vault vault kv put ${config.vault.mountPoint}/${config.vault.path} mariadb_password="${config.useMariaDB ? config.mariadb.password : config.mariadbExternal.password}" `; - script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} mqtt_password=" - ${config.useMQTT ? config.mqtt.password : config.mqttExternal.password} + script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} mqtt_password="${config.useMQTT ? config.mqtt.password : config.mqttExternal.password}" `; if (config.enableSMTP && config.smtp) { - script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} smtp_password=" - ${config.smtp.password}" + script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} smtp_password="${config.smtp.password}" `; } - script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} jwt_key=" - ${config.jwt.key}" + script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} jwt_key="${config.jwt.key}" +`; + + if (config.oauthProviders.length > 0) { + config.oauthProviders.forEach(provider => { + const name = toSnakeCase(provider.displayName); + script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} oauth_${name}_client_id="${provider.clientId}" +`; + script += `docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} oauth_${name}_client_secret="${provider.clientSecret}" +`; + }); + } +` echo "Vault configuration completed" echo "" @@ -519,13 +526,14 @@ echo "Waiting for Garage to start (10 seconds)..." sleep 10 echo "Configuring Garage cluster..." -docker exec electrostore-garage /garage status || true -docker exec electrostore-garage /garage layout assign -z dc1 -c 1 \$(docker exec electrostore-garage garage status | grep -oP '[0-9a-f]{16}' | head -n 1) || true +GARAGE_NODE_ID=$(docker exec electrostore-garage /garage status | grep -oP '[0-9a-f]{16}' | head -n 1) +GARAGE_CAPACITY=5000000000 # 5GB, adjust as needed +docker exec electrostore-garage /garage layout assign $GARAGE_NODE_ID -z dc1 --capacity $GARAGE_CAPACITY $ $(docker exec electrostore-garage garage status | grep -oP '[0-9a-f]{16}' | head -n 1) || true docker exec electrostore-garage /garage layout apply --version 1 echo "Creating S3 access keys..." -docker exec electrostore-garage /garage key import --name electrostore-key ${config.s3.accessKey} ${config.s3.secretKey} +docker exec electrostore-garage /garage key import -n electrostore-key --yes ${config.s3.accessKey} ${config.s3.secretKey} GARAGE_ACCESS_KEY="${config.s3.accessKey}" GARAGE_SECRET_KEY="${config.s3.secretKey}" @@ -542,12 +550,12 @@ docker exec electrostore-garage /garage bucket allow --read --write ${config.s3. if (config.useVault) { script += ` echo "Storing S3 keys in Vault..." -docker exec vault vault kv patch ${config.vault.path} s3_access_key="\$GARAGE_ACCESS_KEY" s3_secret_key="\$GARAGE_SECRET_KEY" +docker exec vault vault kv patch ${config.vault.mountPoint}/${config.vault.path} s3_access_key="\$GARAGE_ACCESS_KEY" s3_secret_key="\$GARAGE_SECRET_KEY" `; } script += ` -rm /tmp/garage_keys.txt +#rm /tmp/garage_keys.txt echo "Garage configuration completed" echo "" diff --git a/electrostoreAPI/Extensions/VaultConfigurationExtensions.cs b/electrostoreAPI/Extensions/VaultConfigurationExtensions.cs index c20cf87..35921cb 100644 --- a/electrostoreAPI/Extensions/VaultConfigurationExtensions.cs +++ b/electrostoreAPI/Extensions/VaultConfigurationExtensions.cs @@ -4,6 +4,22 @@ namespace electrostore.Extensions; +interface vaultConfiguration +{ + public string Addr { get; set; } + public string Token { get; set; } + public string Path { get; set; } + public string MountPoint { get; set; } +} + +class VaultConfigurationImpl : vaultConfiguration +{ + public required string Addr { get; set; } + public required string Token { get; set; } + public required string Path { get; set; } + public required string MountPoint { get; set; } +} + public static class VaultConfigurationExtensions { public static IConfigurationBuilder AddVaultConfiguration(this IConfigurationBuilder builder) @@ -21,36 +37,62 @@ public static IConfigurationBuilder AddVaultConfiguration(this IConfigurationBui // in all config sections, replace values with vault secrets if they are in the format {{vault:}} foreach (var section in tempConfig.GetChildren()) { - foreach (var child in section.GetChildren()) + SearchInConfigBranch(builder, vaultClient, + new VaultConfigurationImpl { - if (child.Value != null && child.Value.Contains("{{vault:") && child.Value.Contains("}}")) - { - Console.WriteLine($"Checking key: {child.Key} with value: {child.Value}"); - // for all occurrences of {{vault:}} in the value, replace with the corresponding vault secret - var newValue = child.Value; - int startIndex = 0; - while (true) - { - int vaultStart = newValue.IndexOf("{{vault:", startIndex); - if (vaultStart == -1) break; - int vaultEnd = newValue.IndexOf("}}", vaultStart); - if (vaultEnd == -1) break; - var vaultKey = newValue.Substring(vaultStart + 8, vaultEnd - vaultStart - 8); - var secretValue = GetVaultSecret(vaultClient, vaultPath, vaultMountPoint, vaultKey); - newValue = string.Concat(newValue.AsSpan(0, vaultStart), secretValue, newValue.AsSpan(vaultEnd + 2)); - startIndex = vaultStart + secretValue.Length; - } - // update the configuration with the new value - builder.AddInMemoryCollection(new Dictionary - { - { child.Path, newValue } - }); - } - } + Addr = vaultAddr, + Token = vaultToken, + Path = vaultPath, + MountPoint = vaultMountPoint + }, section); } } return builder; } + + private static void SearchInConfigBranch(IConfigurationBuilder builder, VaultClient vaultClient, vaultConfiguration vaultConfig, IConfigurationSection section) + { + foreach (var child in section.GetChildren()) + { + // if the section has a value, check if it contains a vault reference and replace it with the secret value + if (child.Value != null) { + FindSecretInConfigValue(builder, vaultClient, vaultConfig, child); + } + else + { + // if the section has no value, it might be a branch, so we need to search in its children + SearchInConfigBranch(builder, vaultClient, vaultConfig, child); + } + } + } + + private static void FindSecretInConfigValue(IConfigurationBuilder builder, VaultClient vaultClient, vaultConfiguration vaultConfig, IConfigurationSection section) + { + if (section.Value != null && section.Value.Contains("{{vault:") && section.Value.Contains("}}")) + { + Console.WriteLine($"Checking key: {section.Key} with value: {section.Value}"); + // for all occurrences of {{vault:}} in the value, replace with the corresponding vault secret + var newValue = section.Value; + int startIndex = 0; + while (true) + { + int vaultStart = newValue.IndexOf("{{vault:", startIndex); + if (vaultStart == -1) break; + int vaultEnd = newValue.IndexOf("}}", vaultStart); + if (vaultEnd == -1) break; + var vaultKey = newValue.Substring(vaultStart + 8, vaultEnd - vaultStart - 8); + var secretValue = GetVaultSecret(vaultClient, vaultConfig.Path, vaultConfig.MountPoint, vaultKey); + newValue = string.Concat(newValue.AsSpan(0, vaultStart), secretValue, newValue.AsSpan(vaultEnd + 2)); + startIndex = vaultStart + secretValue.Length; + } + builder.AddInMemoryCollection(new Dictionary + { + { section.Path, newValue } + }); + Console.WriteLine($"Updated key: {section.Key} with new value: {newValue}"); + } + } + private static string GetVaultSecret(VaultClient vaultClient, string path, string mountPoint, string key) { try diff --git a/electrostoreAPI/Program.cs b/electrostoreAPI/Program.cs index 49a12c9..d318cb4 100644 --- a/electrostoreAPI/Program.cs +++ b/electrostoreAPI/Program.cs @@ -65,6 +65,13 @@ public static void Main(string[] args) { builder.Configuration.AddJsonFile("config/appsettings.Development.json", optional: true, reloadOnChange: true); } + if (builder.Configuration.GetSection("Vault:Enable").Get()) + { + var authMethod = new TokenAuthMethodInfo(builder.Configuration.GetSection("Vault:Token").Value); + var vaultConfig = new VaultClientSettings(builder.Configuration.GetSection("Vault:Addr").Value, authMethod); + builder.Services.AddSingleton(new VaultClient(vaultConfig)); + builder.Configuration.AddVaultConfiguration(); + } var jwtSettings = builder.Configuration.GetSection("Jwt").Get() ?? new JwtSettings { @@ -253,13 +260,6 @@ private static void AddAuthentication(WebApplicationBuilder builder, byte[] key) private static void AddScopes(WebApplicationBuilder builder) { - if (builder.Configuration.GetSection("Vault:Enable").Get()) - { - var authMethod = new TokenAuthMethodInfo(builder.Configuration.GetSection("Vault:Token").Value); - var vaultConfig = new VaultClientSettings(builder.Configuration.GetSection("Vault:Addr").Value, authMethod); - builder.Services.AddSingleton(new VaultClient(vaultConfig)); - builder.Configuration.AddVaultConfiguration(); - } builder.Services.AddDbContext(options => options.UseMySql(builder.Configuration.GetConnectionString("DefaultConnection"), new MySqlServerVersion(new Version(11, 4, 7)) diff --git a/electrostoreAPI/Properties/launchSettings.json b/electrostoreAPI/Properties/launchSettings.json index 31a52bc..ad2151e 100644 --- a/electrostoreAPI/Properties/launchSettings.json +++ b/electrostoreAPI/Properties/launchSettings.json @@ -1,23 +1,14 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61108", - "sslPort": 0 - } - }, +{ "profiles": { "http": { "commandName": "Project", - "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "http://localhost:5234", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5234" }, "IIS Express": { "commandName": "IISExpress", @@ -27,5 +18,14 @@ "ASPNETCORE_ENVIRONMENT": "Development" } } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61108", + "sslPort": 0 + } } -} +} \ No newline at end of file