QrRapido/Configuration/DockerSecretsConfigurationProvider.cs
Ricardo Carneiro 6d4a8904f2 feat: Docker Secrets para credenciais sensíveis
- Criado DockerSecretsConfigurationProvider para ler secrets de /run/secrets/
- Removidas credenciais sensíveis do appsettings.Production.json
- Adicionado indicador visual no rodapé (✓/✗) para verificar se secrets foram carregados
- Atualizado deploy.yml para usar Docker Secrets no Swarm
- Criado script create-docker-secrets.sh para gerenciar secrets
- Criado template secrets.env.template para facilitar configuração
- Documentação completa em DOCKER_SECRETS_SETUP.md

Secrets gerenciados:
- stripe_secret_key
- stripe_webhook_secret
- mongodb_connection_string
- google_client_id / google_client_secret
- microsoft_client_id / microsoft_client_secret

IMPORTANTE: Após este deploy, é necessário criar os secrets no Swarm
e recriar o service. Consulte DOCKER_SECRETS_SETUP.md.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 21:39:20 -03:00

112 lines
3.9 KiB
C#

using Microsoft.Extensions.Configuration;
namespace QRRapidoApp.Configuration
{
/// <summary>
/// Configuration provider that reads Docker Secrets from /run/secrets/
/// Secrets are mounted as files in Swarm mode.
/// </summary>
public class DockerSecretsConfigurationProvider : ConfigurationProvider
{
private readonly string _secretsPath;
private readonly Dictionary<string, string> _secretKeyMappings;
public DockerSecretsConfigurationProvider(string secretsPath, Dictionary<string, string> secretKeyMappings)
{
_secretsPath = secretsPath;
_secretKeyMappings = secretKeyMappings;
}
public override void Load()
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (!Directory.Exists(_secretsPath))
{
return;
}
var secretsLoaded = 0;
foreach (var mapping in _secretKeyMappings)
{
var secretFileName = mapping.Key;
var configKey = mapping.Value;
var secretFilePath = Path.Combine(_secretsPath, secretFileName);
if (File.Exists(secretFilePath))
{
try
{
var secretValue = File.ReadAllText(secretFilePath).Trim();
if (!string.IsNullOrEmpty(secretValue))
{
Data[configKey] = secretValue;
secretsLoaded++;
}
}
catch (Exception)
{
// Silently ignore read errors - will fall back to other config sources
}
}
}
// Set indicator that secrets were loaded from Docker
if (secretsLoaded > 0)
{
Data["App:SecretsLoaded"] = "true";
}
}
}
public class DockerSecretsConfigurationSource : IConfigurationSource
{
public string SecretsPath { get; set; } = "/run/secrets";
public Dictionary<string, string> SecretKeyMappings { get; set; } = new();
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new DockerSecretsConfigurationProvider(SecretsPath, SecretKeyMappings);
}
}
public static class DockerSecretsConfigurationExtensions
{
/// <summary>
/// Adds Docker Secrets as a configuration source.
/// Maps secret file names to configuration keys.
/// </summary>
public static IConfigurationBuilder AddDockerSecrets(
this IConfigurationBuilder builder,
Action<DockerSecretsConfigurationSource>? configure = null)
{
var source = new DockerSecretsConfigurationSource
{
// Default mappings for QRRapido secrets
SecretKeyMappings = new Dictionary<string, string>
{
// Stripe
["stripe_secret_key"] = "Stripe:SecretKey",
["stripe_webhook_secret"] = "Stripe:WebhookSecret",
// MongoDB
["mongodb_connection_string"] = "ConnectionStrings:MongoDB",
// OAuth - Google
["google_client_id"] = "Authentication:Google:ClientId",
["google_client_secret"] = "Authentication:Google:ClientSecret",
// OAuth - Microsoft
["microsoft_client_id"] = "Authentication:Microsoft:ClientId",
["microsoft_client_secret"] = "Authentication:Microsoft:ClientSecret",
}
};
configure?.Invoke(source);
builder.Add(source);
return builder;
}
}
}