Critical fixes: - Add SanitizeConnectionString() in storage/db_connect.go to remove passwords - Add SecureEnvVar() in api/factory.go to clear env vars after reading (JWT_SECRET) - Clear DB password from config after connection Logging improvements: - Enhance logging/sanitize.go with patterns for: - PostgreSQL connection strings - Generic connection string passwords - HTTP Authorization headers - Private keys CLI security: - Add --security-audit flag to api-server for security checks: - Config file permissions - Exposed environment variables - Running as root - API key file permissions - Add warning when --api-key flag used (process list exposure) Files changed: - internal/storage/db_connect.go - internal/api/factory.go - internal/logging/sanitize.go - internal/auth/flags.go - cmd/api-server/main.go
126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/jfraeys/fetch_ml/internal/fileutil"
|
|
)
|
|
|
|
// Flags holds authentication-related command line flags
|
|
type Flags struct {
|
|
APIKey string
|
|
APIKeyFile string
|
|
ConfigFile string
|
|
EnableAuth bool
|
|
ShowHelp bool
|
|
}
|
|
|
|
// ParseAuthFlags parses authentication command line flags
|
|
func ParseAuthFlags() *Flags {
|
|
flags := &Flags{}
|
|
|
|
flag.StringVar(&flags.APIKey, "api-key", "", "API key for authentication")
|
|
flag.StringVar(&flags.APIKeyFile, "api-key-file", "", "Path to file containing API key")
|
|
flag.StringVar(&flags.ConfigFile, "config", "", "Configuration file path")
|
|
flag.BoolVar(&flags.EnableAuth, "enable-auth", false, "Enable authentication")
|
|
flag.BoolVar(&flags.ShowHelp, "auth-help", false, "Show authentication help")
|
|
|
|
// Custom help flag that doesn't exit
|
|
flag.Usage = func() {}
|
|
|
|
flag.Parse()
|
|
|
|
return flags
|
|
}
|
|
|
|
// GetAPIKeyFromSources gets API key from multiple sources in priority order
|
|
func GetAPIKeyFromSources(flags *Flags) string {
|
|
// 1. Command line flag (highest priority)
|
|
if flags.APIKey != "" {
|
|
// Warn about process list exposure
|
|
log.Println("WARNING: --api-key exposes credential in process list. Use --api-key-file for better security.")
|
|
return flags.APIKey
|
|
}
|
|
|
|
// 2. Explicit file flag
|
|
if flags.APIKeyFile != "" {
|
|
contents, readErr := os.ReadFile(flags.APIKeyFile)
|
|
if readErr == nil {
|
|
return strings.TrimSpace(string(contents))
|
|
}
|
|
log.Printf("Warning: Could not read API key file %s: %v", flags.APIKeyFile, readErr)
|
|
}
|
|
|
|
// 3. Environment variable
|
|
if envKey := os.Getenv("FETCH_ML_API_KEY"); envKey != "" {
|
|
return envKey
|
|
}
|
|
|
|
// 4. File-based key (for automated scripts)
|
|
if fileKey := os.Getenv("FETCH_ML_API_KEY_FILE"); fileKey != "" {
|
|
content, err := fileutil.SecureFileRead(fileKey)
|
|
if err == nil {
|
|
return strings.TrimSpace(string(content))
|
|
}
|
|
log.Printf("Warning: Could not read API key file %s: %v", fileKey, err)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// ValidateFlags validates parsed authentication flags
|
|
func ValidateFlags(flags *Flags) error {
|
|
if flags.ShowHelp {
|
|
PrintAuthHelp()
|
|
os.Exit(0)
|
|
}
|
|
|
|
if flags.APIKeyFile != "" {
|
|
if _, err := os.Stat(flags.APIKeyFile); err != nil {
|
|
return fmt.Errorf("api key file not found: %s", flags.APIKeyFile)
|
|
}
|
|
if err := CheckConfigFilePermissions(flags.APIKeyFile); err != nil {
|
|
log.Printf("Warning: %v", err)
|
|
}
|
|
}
|
|
|
|
// If config file is specified, check if it exists
|
|
if flags.ConfigFile != "" {
|
|
if _, err := os.Stat(flags.ConfigFile); err != nil {
|
|
return fmt.Errorf("config file not found: %s", flags.ConfigFile)
|
|
}
|
|
|
|
// Check file permissions
|
|
if err := CheckConfigFilePermissions(flags.ConfigFile); err != nil {
|
|
log.Printf("Warning: %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PrintAuthHelp prints authentication-specific help
|
|
func PrintAuthHelp() {
|
|
fmt.Println("Authentication Options:")
|
|
fmt.Println(" --api-key <key> API key for authentication")
|
|
fmt.Println(" --api-key-file <path> Read API key from file")
|
|
fmt.Println(" --config <file> Configuration file path")
|
|
fmt.Println(" --enable-auth Enable authentication (if disabled)")
|
|
fmt.Println(" --auth-help Show this help")
|
|
fmt.Println()
|
|
fmt.Println("Environment Variables:")
|
|
fmt.Println(" FETCH_ML_API_KEY API key for authentication")
|
|
fmt.Println(" FETCH_ML_API_KEY_FILE File containing API key")
|
|
fmt.Println(" FETCH_ML_ENV Environment (development/production)")
|
|
fmt.Println(" FETCH_ML_ALLOW_INSECURE_AUTH Allow insecure auth (dev only)")
|
|
fmt.Println()
|
|
fmt.Println("Security Notes:")
|
|
fmt.Println(" - API keys in command line may be visible in process lists")
|
|
fmt.Println(" - Environment variables are preferred for automated scripts")
|
|
fmt.Println(" - File-based keys should have restricted permissions (600)")
|
|
fmt.Println(" - Authentication is mandatory in production environments")
|
|
}
|