fetch_ml/internal/api/ws_tls_auth.go

100 lines
2.5 KiB
Go

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
}