- Enhanced secrets management with environment-aware defaults - Fixed RabbitMQ URL: no default credentials in production - Added getRabbitMQURL with environment-aware logic - Added ValidateRequiredSecrets to validate required secrets - Added RequiredSecretKeys listing production-required secrets - Added validation for RabbitMQ URL in production - All secrets properly managed via environment variables - No hardcoded secrets in production code
121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// SecretsProvider définit l'interface pour les fournisseurs de secrets (T0037)
|
|
type SecretsProvider interface {
|
|
GetSecret(name string) (string, error)
|
|
IsSecret(name string) bool
|
|
}
|
|
|
|
// EnvSecretsProvider récupère les secrets depuis les variables d'environnement (T0037)
|
|
type EnvSecretsProvider struct {
|
|
secretKeys map[string]bool
|
|
}
|
|
|
|
// NewEnvSecretsProvider crée un nouveau fournisseur de secrets depuis l'environnement
|
|
func NewEnvSecretsProvider(secretKeys []string) *EnvSecretsProvider {
|
|
keysMap := make(map[string]bool)
|
|
for _, key := range secretKeys {
|
|
keysMap[key] = true
|
|
}
|
|
return &EnvSecretsProvider{secretKeys: keysMap}
|
|
}
|
|
|
|
// GetSecret récupère un secret depuis les variables d'environnement (T0037)
|
|
func (p *EnvSecretsProvider) GetSecret(name string) (string, error) {
|
|
value := os.Getenv(name)
|
|
if value == "" {
|
|
return "", fmt.Errorf("secret %s not found", name)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// IsSecret vérifie si une clé est un secret (T0037)
|
|
func (p *EnvSecretsProvider) IsSecret(name string) bool {
|
|
return p.secretKeys[name]
|
|
}
|
|
|
|
// MaskSecret masque un secret pour l'affichage dans les logs (T0037)
|
|
// Masque les 4 premiers et 4 derniers caractères, remplace le reste par "****"
|
|
func MaskSecret(secret string) string {
|
|
if secret == "" {
|
|
return ""
|
|
}
|
|
if len(secret) <= 8 {
|
|
return "****"
|
|
}
|
|
return secret[:4] + "****" + secret[len(secret)-4:]
|
|
}
|
|
|
|
// MaskConfigValue masque une valeur si c'est un secret (T0037)
|
|
func MaskConfigValue(key, value string, provider SecretsProvider) string {
|
|
if provider != nil && provider.IsSecret(key) {
|
|
return MaskSecret(value)
|
|
}
|
|
return value
|
|
}
|
|
|
|
// DefaultSecretKeys retourne la liste des clés considérées comme secrets (T0037)
|
|
// MOD-P0-002: Always mask secrets in logs, even in DEBUG mode
|
|
// BE-SEC-014: Comprehensive list of all secret keys
|
|
func DefaultSecretKeys() []string {
|
|
return []string{
|
|
"JWT_SECRET",
|
|
"CHAT_JWT_SECRET",
|
|
"DB_PASSWORD",
|
|
"DATABASE_PASSWORD",
|
|
"DATABASE_URL", // Contains password in connection string
|
|
"REDIS_PASSWORD",
|
|
"REDIS_URL", // May contain password in connection string
|
|
"RABBITMQ_URL", // May contain password in connection string
|
|
"AWS_SECRET_ACCESS_KEY",
|
|
"AWS_ACCESS_KEY_ID",
|
|
"STRIPE_SECRET_KEY",
|
|
"STRIPE_WEBHOOK_SECRET",
|
|
"SMTP_PASSWORD",
|
|
"OAUTH_CLIENT_SECRET",
|
|
"SENTRY_DSN", // May contain sensitive information
|
|
}
|
|
}
|
|
|
|
// RequiredSecretKeys retourne la liste des secrets requis en production (BE-SEC-014)
|
|
func RequiredSecretKeys() []string {
|
|
return []string{
|
|
"JWT_SECRET",
|
|
"DATABASE_URL",
|
|
}
|
|
}
|
|
|
|
// ValidateRequiredSecrets valide que tous les secrets requis sont présents (BE-SEC-014)
|
|
func ValidateRequiredSecrets(env string) error {
|
|
requiredSecrets := RequiredSecretKeys()
|
|
var missingSecrets []string
|
|
|
|
for _, secretKey := range requiredSecrets {
|
|
value := os.Getenv(secretKey)
|
|
if value == "" {
|
|
missingSecrets = append(missingSecrets, secretKey)
|
|
}
|
|
}
|
|
|
|
if len(missingSecrets) > 0 {
|
|
return fmt.Errorf("required secrets are missing: %v", missingSecrets)
|
|
}
|
|
|
|
// BE-SEC-014: In production, also validate RabbitMQ URL if RabbitMQ is enabled
|
|
if env == "production" || env == "prod" {
|
|
rabbitMQEnable := os.Getenv("RABBITMQ_ENABLE")
|
|
if rabbitMQEnable != "false" {
|
|
rabbitMQURL := os.Getenv("RABBITMQ_URL")
|
|
if rabbitMQURL == "" {
|
|
return fmt.Errorf("RABBITMQ_URL is required in production when RabbitMQ is enabled")
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|