Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/generator/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ <h3 style="margin: 1.5rem 0 1rem 0; color: #4caf50;">HashiCorp Vault Configurati

<div class="form-group">
<label for="vaultPath">Secrets Path</label>
<input type="text" id="vaultPath" name="vaultPath" placeholder="electrostore" value="secret/electrostore">
<input type="text" id="vaultPath" name="vaultPath" placeholder="electrostore" value="electrostore">
<small class="help-text">Path in Vault where secrets are stored</small>
</div>

Expand Down
2 changes: 1 addition & 1 deletion docs/generator/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
48 changes: 28 additions & 20 deletions docs/generator/js/generators.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 += `
Expand All @@ -52,7 +52,7 @@ services:`;
} else {
compose += `
ports:
- "\${API_PORT:-${config.apiPort}}:80"`;
- "\${API_PORT:-${config.apiPort}}:8080"`;
}

compose += `
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 ""

Expand All @@ -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}"

Expand All @@ -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 ""
Expand Down
92 changes: 67 additions & 25 deletions electrostoreAPI/Extensions/VaultConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:<key>}}
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:<key>}} 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<string, string?>
{
{ 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:<key>}} 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<string, string?>
{
{ 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
Expand Down
14 changes: 7 additions & 7 deletions electrostoreAPI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>())
{
var authMethod = new TokenAuthMethodInfo(builder.Configuration.GetSection("Vault:Token").Value);
var vaultConfig = new VaultClientSettings(builder.Configuration.GetSection("Vault:Addr").Value, authMethod);
builder.Services.AddSingleton<IVaultClient>(new VaultClient(vaultConfig));
builder.Configuration.AddVaultConfiguration();
}

var jwtSettings = builder.Configuration.GetSection("Jwt").Get<JwtSettings>() ?? new JwtSettings
{
Expand Down Expand Up @@ -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<bool>())
{
var authMethod = new TokenAuthMethodInfo(builder.Configuration.GetSection("Vault:Token").Value);
var vaultConfig = new VaultClientSettings(builder.Configuration.GetSection("Vault:Addr").Value, authMethod);
builder.Services.AddSingleton<IVaultClient>(new VaultClient(vaultConfig));
builder.Configuration.AddVaultConfiguration();
}
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(builder.Configuration.GetConnectionString("DefaultConnection"),
new MySqlServerVersion(new Version(11, 4, 7))
Expand Down
28 changes: 14 additions & 14 deletions electrostoreAPI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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
}
}
}
}
Loading