Security Overview
SLOzy implements defense-in-depth security with multiple layers of protection.
Authentication
JWT-based authentication is enforced via internal/middleware/auth.go:
type AuthMiddleware struct {
authService *auth.AuthenticationService
}
func (m *AuthMiddleware) Authenticate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
// Validate Bearer token format
parts := strings.SplitN(authHeader, " ", 2)
token := parts[1]
claims, err := m.authService.ValidateToken(token)
// Add user context to request
ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
// ...
})
}Tokens are signed using HS256 and include user_id, organization_id, email, and name claims. The JWT_SECRET must be at least 32 characters and kept confidential.
RBAC (Role-Based Access Control)
Authorization is handled by internal/middleware/rbac.go and internal/rbac/service.go:
func (m *RBACMiddleware) RequirePermission(permission string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
// Check user has permission via check_permission() DB function
hasPermission, err := m.rbacService.CheckPermission(ctx, userID, organizationID, permission)
if !hasPermission {
http.Error(w, "Permission denied", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}Four default roles are defined:
- admin — Full access (read, write, delete, manage users/teams/organizations)
- manager — Team and SLO management (read, write, manage teams, view analytics)
- developer — SLO and metrics access (read, write, view analytics)
- viewer — Read-only access (read, view analytics)
Rate Limiting
Token bucket rate limiter (internal/middleware/security.go) with configurable global and per-IP limits:
type RateLimiter struct {
visitors map[string]*Visitor
rate int // tokens per interval
burst int // bucket size
window time.Duration // interval duration
}- Global: 1000 requests/minute, burst 50
- Per-IP: 100 requests/minute, burst 10
- Whitelist/blacklist support for specific IPs
- Cleanup goroutine removes stale visitors every minute
Security Headers
Applied by SecurityHeadersMiddleware() (internal/middleware/security.go:197):
| Header | Value |
|---|---|
X-Frame-Options | DENY |
X-Content-Type-Options | nosniff |
X-XSS-Protection | 1; mode=block |
Referrer-Policy | strict-origin-when-cross-origin |
Permissions-Policy | default-src 'self' |
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload |
Content-Security-Policy | default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; ... |
Cross-Origin-Opener-Policy | same-origin |
Cross-Origin-Embedder-Policy | require-corp |
CORS Middleware
Configurable CORS middleware (internal/middleware/security.go:218):
func CORSMiddleware(allowedOrigins []string) gin.HandlerFunc {
// Validates origin against allowed list
// Sets Access-Control-Allow-Origin, Methods, Headers, Credentials
// Handles OPTIONS preflight requests
}Default allowed origins: http://localhost:3000, http://localhost:8080. Override via CORS_ORIGINS environment variable for production.
Prometheus TLS
Connections to Prometheus data sources use TLS with InsecureSkipVerify: false by default (internal/prometheus/client.go). Configure trusted CA certificates and TLS settings per data source for secure metric collection.