deb-bootc-compose/internal/security/authentication.go
2025-08-18 23:32:51 -07:00

354 lines
10 KiB
Go

package security
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
// AuthProvider defines the interface for authentication providers
type AuthProvider interface {
Authenticate(ctx context.Context, credentials interface{}) (*AuthResult, error)
ValidateToken(token string) (*AuthResult, error)
GetUserInfo(token string) (*UserInfo, error)
}
// AuthResult represents the result of an authentication attempt
type AuthResult struct {
Success bool
User *UserInfo
Token string
ExpiresAt time.Time
Permissions []string
Metadata map[string]interface{}
}
// UserInfo represents authenticated user information
type UserInfo struct {
Username string
Email string
FullName string
Groups []string
Permissions []string
Metadata map[string]interface{}
}
// AuthConfig represents authentication configuration
type AuthConfig struct {
Enabled bool `yaml:"enabled"`
Provider string `yaml:"provider"`
Kerberos KerberosConfig `yaml:"kerberos"`
OIDC OIDCConfig `yaml:"oidc"`
APIKey APIKeyConfig `yaml:"api_key"`
SSL SSLConfig `yaml:"ssl"`
RBAC RBACConfig `yaml:"rbac"`
Audit AuditConfig `yaml:"audit"`
Custom map[string]interface{} `yaml:"custom"`
}
// KerberosConfig represents Kerberos authentication configuration
type KerberosConfig struct {
Enabled bool `yaml:"enabled"`
Realm string `yaml:"realm"`
KeytabPath string `yaml:"keytab_path"`
ServiceName string `yaml:"service_name"`
Debug bool `yaml:"debug"`
}
// OIDCConfig represents OpenID Connect configuration
type OIDCConfig struct {
Enabled bool `yaml:"enabled"`
IssuerURL string `yaml:"issuer_url"`
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
RedirectURL string `yaml:"redirect_url"`
Scopes string `yaml:"scopes"`
TokenEndpoint string `yaml:"token_endpoint"`
UserInfoURL string `yaml:"userinfo_url"`
JWKSURL string `yaml:"jwks_url"`
}
// APIKeyConfig represents API key authentication configuration
type APIKeyConfig struct {
Enabled bool `yaml:"enabled"`
HeaderName string `yaml:"header_name"`
QueryParam string `yaml:"query_param"`
SecretPath string `yaml:"secret_path"`
Algorithm string `yaml:"algorithm"`
Expiration string `yaml:"expiration"`
}
// SSLConfig represents SSL/TLS configuration
type SSLConfig struct {
Enabled bool `yaml:"enabled"`
CertFile string `yaml:"cert_file"`
KeyFile string `yaml:"key_file"`
CAFile string `yaml:"ca_file"`
MinVersion string `yaml:"min_version"`
MaxVersion string `yaml:"max_version"`
CipherSuites []string `yaml:"cipher_suites"`
}
// RBACConfig represents role-based access control configuration
type RBACConfig struct {
Enabled bool `yaml:"enabled"`
Roles map[string]RoleConfig `yaml:"roles"`
Policies map[string]PolicyConfig `yaml:"policies"`
DefaultRole string `yaml:"default_role"`
}
// RoleConfig represents a role configuration
type RoleConfig struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Permissions []string `yaml:"permissions"`
Inherits []string `yaml:"inherits"`
}
// PolicyConfig represents a policy configuration
type PolicyConfig struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Effect string `yaml:"effect"` // allow, deny
Resources []string `yaml:"resources"`
Actions []string `yaml:"actions"`
Conditions map[string]interface{} `yaml:"conditions"`
}
// AuditConfig represents audit logging configuration
type AuditConfig struct {
Enabled bool `yaml:"enabled"`
LogFile string `yaml:"log_file"`
LogLevel string `yaml:"log_level"`
MaxSize int `yaml:"max_size"`
MaxBackups int `yaml:"max_backups"`
MaxAge int `yaml:"max_age"`
}
// AuthManager manages authentication and authorization
type AuthManager struct {
config *AuthConfig
providers map[string]AuthProvider
rbac *RBACManager
audit *AuditLogger
logger *logrus.Logger
}
// NewAuthManager creates a new authentication manager
func NewAuthManager(config *AuthConfig) (*AuthManager, error) {
am := &AuthManager{
config: config,
providers: make(map[string]AuthProvider),
logger: logrus.New(),
}
// Initialize RBAC if enabled
if config.RBAC.Enabled {
rbac, err := NewRBACManager(&config.RBAC)
if err != nil {
return nil, fmt.Errorf("failed to initialize RBAC: %w", err)
}
am.rbac = rbac
}
// Initialize audit logging if enabled
if config.Audit.Enabled {
audit, err := NewAuditLogger(&config.Audit)
if err != nil {
return nil, fmt.Errorf("failed to initialize audit logging: %w", err)
}
am.audit = audit
}
// Initialize authentication providers
if err := am.initializeProviders(); err != nil {
return nil, fmt.Errorf("failed to initialize authentication providers: %w", err)
}
return am, nil
}
// initializeProviders initializes all configured authentication providers
func (am *AuthManager) initializeProviders() error {
// Initialize Kerberos provider if enabled
if am.config.Kerberos.Enabled {
kerberos, err := NewKerberosProvider(&am.config.Kerberos)
if err != nil {
return fmt.Errorf("failed to initialize Kerberos provider: %w", err)
}
am.providers["kerberos"] = kerberos
}
// Initialize OIDC provider if enabled
if am.config.OIDC.Enabled {
oidc, err := NewOIDCProvider(&am.config.OIDC)
if err != nil {
return fmt.Errorf("failed to initialize OIDC provider: %w", err)
}
am.providers["oidc"] = oidc
}
// Initialize API key provider if enabled
if am.config.APIKey.Enabled {
apikey, err := NewAPIKeyProvider(&am.config.APIKey)
if err != nil {
return fmt.Errorf("failed to initialize API key provider: %w", err)
}
am.providers["apikey"] = apikey
}
return nil
}
// Authenticate authenticates a user using the specified provider
func (am *AuthManager) Authenticate(ctx context.Context, provider string, credentials interface{}) (*AuthResult, error) {
// Get the specified provider
authProvider, exists := am.providers[provider]
if !exists {
return nil, fmt.Errorf("authentication provider %s not found", provider)
}
// Attempt authentication
result, err := authProvider.Authenticate(ctx, credentials)
if err != nil {
am.logger.Errorf("Authentication failed for provider %s: %v", provider, err)
return nil, err
}
// Log successful authentication
if am.audit != nil {
am.audit.LogAuthEvent("authentication_success", result.User.Username, provider, nil)
}
return result, nil
}
// ValidateToken validates an authentication token
func (am *AuthManager) ValidateToken(token string) (*AuthResult, error) {
// Try all providers to validate the token
for providerName, provider := range am.providers {
if result, err := provider.ValidateToken(token); err == nil {
// Log token validation
if am.audit != nil {
am.audit.LogAuthEvent("token_validation", result.User.Username, providerName, nil)
}
return result, nil
}
}
return nil, fmt.Errorf("invalid or expired token")
}
// Authorize checks if a user has permission to perform an action
func (am *AuthManager) Authorize(user *UserInfo, resource, action string) bool {
if am.rbac == nil {
// If RBAC is disabled, allow all actions
return true
}
// Check authorization using RBAC
authorized := am.rbac.Authorize(user, resource, action)
// Log authorization attempt
if am.audit != nil {
metadata := map[string]interface{}{
"resource": resource,
"action": action,
"result": authorized,
}
am.audit.LogAuthEvent("authorization_check", user.Username, "rbac", metadata)
}
return authorized
}
// GetTLSConfig returns TLS configuration for secure connections
func (am *AuthManager) GetTLSConfig() (*tls.Config, error) {
if !am.config.SSL.Enabled {
return nil, nil
}
// Load certificate and key
cert, err := tls.LoadX509KeyPair(am.config.SSL.CertFile, am.config.SSL.KeyFile)
if err != nil {
return nil, fmt.Errorf("failed to load TLS certificate: %w", err)
}
// Load CA certificate if specified
var caPool *x509.CertPool
if am.config.SSL.CAFile != "" {
caData, err := os.ReadFile(am.config.SSL.CAFile)
if err != nil {
return nil, fmt.Errorf("failed to read CA file: %w", err)
}
caPool = x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caData) {
return nil, fmt.Errorf("failed to parse CA certificate")
}
}
// Create TLS config
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
}
// Set minimum version if specified
if am.config.SSL.MinVersion != "" {
switch strings.ToLower(am.config.SSL.MinVersion) {
case "tls1.0":
tlsConfig.MinVersion = tls.VersionTLS10
case "tls1.1":
tlsConfig.MinVersion = tls.VersionTLS11
case "tls1.2":
tlsConfig.MinVersion = tls.VersionTLS12
case "tls1.3":
tlsConfig.MinVersion = tls.VersionTLS13
}
}
// Set maximum version if specified
if am.config.SSL.MaxVersion != "" {
switch strings.ToLower(am.config.SSL.MaxVersion) {
case "tls1.0":
tlsConfig.MaxVersion = tls.VersionTLS10
case "tls1.1":
tlsConfig.MaxVersion = tls.VersionTLS11
case "tls1.2":
tlsConfig.MaxVersion = tls.VersionTLS12
case "tls1.3":
tlsConfig.MaxVersion = tls.VersionTLS13
}
}
return tlsConfig, nil
}
// GetHTTPClient returns an HTTP client with proper authentication
func (am *AuthManager) GetHTTPClient() *http.Client {
client := &http.Client{
Timeout: 30 * time.Second,
}
// Add TLS configuration if enabled
if tlsConfig, err := am.GetTLSConfig(); err == nil && tlsConfig != nil {
client.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
return client
}