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