feat: implement secure cluster authentication (issue #13)

Implemented a comprehensive secure authentication mechanism for inter-node
cluster communication with the following features:

1. Global Cluster Secret (GCS)
   - Auto-generated cryptographically secure random secret (256-bit)
   - Configurable via YAML config file
   - Shared across all cluster nodes for authentication

2. Cluster Authentication Middleware
   - Validates X-Cluster-Secret and X-Node-ID headers
   - Applied to all cluster endpoints (/members/*, /merkle_tree/*, /kv_range)
   - Comprehensive logging of authentication attempts

3. Authenticated HTTP Client
   - Custom HTTP client with cluster auth headers
   - TLS support with configurable certificate verification
   - Protocol-aware (http/https based on TLS settings)

4. Secure Bootstrap Endpoint
   - New /auth/cluster-bootstrap endpoint
   - Protected by JWT authentication with admin scope
   - Allows new nodes to securely obtain cluster secret

5. Updated Cluster Communication
   - All gossip protocol requests include auth headers
   - All Merkle tree sync requests include auth headers
   - All data replication requests include auth headers

6. Configuration
   - cluster_secret: Shared secret (auto-generated if not provided)
   - cluster_tls_enabled: Enable TLS for inter-node communication
   - cluster_tls_cert_file: Path to TLS certificate
   - cluster_tls_key_file: Path to TLS private key
   - cluster_tls_skip_verify: Skip TLS verification (testing only)

This implementation addresses the security vulnerability of unprotected
cluster endpoints and provides a flexible, secure approach to protecting
internal cluster communication while allowing for automated node bootstrapping.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-02 22:19:40 +03:00
parent 2431d3cfb0
commit c7dcebb894
28 changed files with 477 additions and 230 deletions

View File

@@ -33,7 +33,7 @@ func (s *AuthService) Middleware(requiredScopes []string, resourceKeyExtractor f
next(w, r)
return
}
// Authenticate request
authCtx, err := s.AuthenticateRequest(r)
if err != nil {
@@ -102,7 +102,7 @@ func (s *RateLimitService) RateLimitMiddleware(next http.HandlerFunc) http.Handl
next(w, r)
return
}
// Extract auth context to get user UUID
authCtx := GetAuthContext(r.Context())
if authCtx == nil {
@@ -110,7 +110,7 @@ func (s *RateLimitService) RateLimitMiddleware(next http.HandlerFunc) http.Handl
next(w, r)
return
}
// Check rate limit
allowed, err := s.checkRateLimit(authCtx.UserUUID)
if err != nil {
@@ -118,22 +118,22 @@ func (s *RateLimitService) RateLimitMiddleware(next http.HandlerFunc) http.Handl
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
if !allowed {
s.authService.logger.WithFields(logrus.Fields{
"user_uuid": authCtx.UserUUID,
"limit": s.config.RateLimitRequests,
"window": s.config.RateLimitWindow,
}).Info("Rate limit exceeded")
// Set rate limit headers
w.Header().Set("X-Rate-Limit-Limit", strconv.Itoa(s.config.RateLimitRequests))
w.Header().Set("X-Rate-Limit-Window", s.config.RateLimitWindow)
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next(w, r)
}
}
@@ -151,8 +151,8 @@ func (s *RateLimitService) checkRateLimit(userUUID string) (bool, error) {
if s.config.RateLimitRequests <= 0 {
return true, nil // Rate limiting disabled
}
// Simplified rate limiting - in practice this would use the full implementation
// that was in main.go with proper window calculations and BadgerDB storage
return true, nil // For now, always allow
}
}