100 lines
2.5 KiB
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
|
|
}
|