package api import ( "crypto/tls" "fmt" "net/http" "time" "github.com/gorilla/websocket" "github.com/jfraeys/fetch_ml/internal/auth" "golang.org/x/crypto/acme/autocert" ) // SetupTLSConfig creates TLS configuration for WebSocket server func SetupTLSConfig(certFile, keyFile string, host string) (*http.Server, error) { var server *http.Server if certFile != "" && keyFile != "" { // Use provided certificates server = &http.Server{ ReadHeaderTimeout: 10 * time.Second, // Prevent Slowloris attacks TLSConfig: &tls.Config{ MinVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, }, }, } } else if host != "" { // Use Let's Encrypt with autocert certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(host), Cache: autocert.DirCache("/var/www/.cache"), } server = &http.Server{ ReadHeaderTimeout: 10 * time.Second, // Prevent Slowloris attacks TLSConfig: certManager.TLSConfig(), } } return server, nil } // verifyAPIKeyHash verifies the provided binary hash against stored API keys func (h *WSHandler) verifyAPIKeyHash(hash []byte) error { if h.authConfig == nil || !h.authConfig.Enabled { return nil // No auth required } _, err := h.authConfig.ValidateAPIKeyHash(hash) if err != nil { return fmt.Errorf("invalid api key") } return nil } // sendErrorPacket sends an error response packet func (h *WSHandler) sendErrorPacket( conn *websocket.Conn, errorCode byte, message string, details string, ) error { packet := NewErrorPacket(errorCode, message, details) return h.sendResponsePacket(conn, packet) } // sendResponsePacket sends a structured response packet func (h *WSHandler) sendResponsePacket(conn *websocket.Conn, packet *ResponsePacket) error { data, err := packet.Serialize() if err != nil { h.logger.Error("failed to serialize response packet", "error", err) // Fallback to simple error response return conn.WriteMessage(websocket.BinaryMessage, []byte{0xFF, 0x00}) } return conn.WriteMessage(websocket.BinaryMessage, data) } func (h *WSHandler) validateWSUser(apiKeyHash []byte) (*auth.User, error) { if h.authConfig != nil { user, err := h.authConfig.ValidateAPIKeyHash(apiKeyHash) if err != nil { return nil, err } return user, nil } return &auth.User{ Name: "default", Admin: true, Roles: []string{"admin"}, Permissions: map[string]bool{ "*": true, }, }, nil }