Files
akanealw eec8c28fb3
Go Benchmark / Performance Regression Check (push) Has been cancelled
Cerberus Integration / Cerberus Security Stack Integration (push) Has been cancelled
Upload Coverage to Codecov / Backend Codecov Upload (push) Has been cancelled
Upload Coverage to Codecov / Frontend Codecov Upload (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (go) (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Has been cancelled
CrowdSec Integration / CrowdSec Bouncer Integration (push) Has been cancelled
Docker Build, Publish & Test / build-and-push (push) Has been cancelled
Quality Checks / Auth Route Protection Contract (push) Has been cancelled
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Has been cancelled
Quality Checks / Backend (Go) (push) Has been cancelled
Quality Checks / Frontend (React) (push) Has been cancelled
Rate Limit integration / Rate Limiting Integration (push) Has been cancelled
Security Scan (PR) / Trivy Binary Scan (push) Has been cancelled
Supply Chain Verification (PR) / Verify Supply Chain (push) Has been cancelled
WAF integration / Coraza WAF Integration (push) Has been cancelled
Docker Build, Publish & Test / Security Scan PR Image (push) Has been cancelled
Repo Health Check / Repo health (push) Has been cancelled
History Rewrite Dry-Run / Dry-run preview for history rewrite (push) Has been cancelled
Prune Renovate Branches / prune (push) Has been cancelled
Renovate / renovate (push) Has been cancelled
Nightly Build & Package / sync-development-to-nightly (push) Has been cancelled
Nightly Build & Package / Trigger Nightly Validation Workflows (push) Has been cancelled
Nightly Build & Package / build-and-push-nightly (push) Has been cancelled
Nightly Build & Package / test-nightly-image (push) Has been cancelled
Nightly Build & Package / verify-nightly-supply-chain (push) Has been cancelled
Update GeoLite2 Checksum / update-checksum (push) Has been cancelled
Container Registry Prune / prune-ghcr (push) Has been cancelled
Container Registry Prune / prune-dockerhub (push) Has been cancelled
Container Registry Prune / summarize (push) Has been cancelled
Supply Chain Verification / Verify SBOM (push) Has been cancelled
Supply Chain Verification / Verify Release Artifacts (push) Has been cancelled
Supply Chain Verification / Verify Docker Image Supply Chain (push) Has been cancelled
Monitor Caddy Major Release / check-caddy-major (push) Has been cancelled
Weekly Nightly to Main Promotion / Verify Nightly Branch Health (push) Has been cancelled
Weekly Nightly to Main Promotion / Create Promotion PR (push) Has been cancelled
Weekly Nightly to Main Promotion / Trigger Missing Required Checks (push) Has been cancelled
Weekly Nightly to Main Promotion / Notify on Failure (push) Has been cancelled
Weekly Nightly to Main Promotion / Workflow Summary (push) Has been cancelled
Weekly Security Rebuild / Security Rebuild & Scan (push) Has been cancelled
changed perms
2026-04-22 18:19:14 +00:00

130 lines
4.0 KiB
Go
Executable File

package middleware
import (
"crypto/subtle"
"net"
"os"
"github.com/Wikid82/charon/backend/internal/logger"
"github.com/Wikid82/charon/backend/internal/util"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
const (
// EmergencyTokenHeader is the HTTP header name for emergency token
EmergencyTokenHeader = "X-Emergency-Token"
// EmergencyTokenEnvVar is the environment variable name for emergency token
EmergencyTokenEnvVar = "CHARON_EMERGENCY_TOKEN"
// MinTokenLength is the minimum required length for emergency tokens
MinTokenLength = 32
)
// EmergencyBypass creates middleware that bypasses all security checks
// when a valid emergency token is present from an authorized source.
//
// Security conditions (ALL must be met):
// 1. Request from management CIDR (RFC1918 private networks by default)
// 2. X-Emergency-Token header matches configured token (timing-safe)
// 3. Token meets minimum length requirement (32+ chars)
//
// This middleware must be registered FIRST in the middleware chain.
func EmergencyBypass(managementCIDRs []string, db *gorm.DB) gin.HandlerFunc {
// Load emergency token from environment
emergencyToken := os.Getenv(EmergencyTokenEnvVar)
if emergencyToken == "" {
logger.Log().Warn("CHARON_EMERGENCY_TOKEN not set - emergency bypass disabled")
return func(c *gin.Context) { c.Next() } // noop
}
if len(emergencyToken) < MinTokenLength {
logger.Log().Warn("CHARON_EMERGENCY_TOKEN too short - emergency bypass disabled")
return func(c *gin.Context) { c.Next() } // noop
}
// Parse management CIDRs
var managementNets []*net.IPNet
for _, cidr := range managementCIDRs {
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
logger.Log().WithError(err).WithField("cidr", cidr).Warn("Invalid management CIDR")
continue
}
managementNets = append(managementNets, ipnet)
}
// Default to RFC1918 private networks if none specified
if len(managementNets) == 0 {
managementNets = []*net.IPNet{
mustParseCIDR("10.0.0.0/8"),
mustParseCIDR("172.16.0.0/12"),
mustParseCIDR("192.168.0.0/16"),
mustParseCIDR("127.0.0.0/8"), // localhost for local development
mustParseCIDR("::1/128"), // IPv6 localhost
}
}
return func(c *gin.Context) {
// Check if emergency token is present
providedToken := c.GetHeader(EmergencyTokenHeader)
if providedToken == "" {
c.Next() // No emergency token - proceed normally
return
}
// Validate source IP is from management network
clientIPStr := util.CanonicalizeIPForSecurity(c.ClientIP())
clientIP := net.ParseIP(clientIPStr)
if clientIP == nil {
logger.Log().WithField("ip", util.SanitizeForLog(clientIPStr)).Warn("Emergency bypass: invalid client IP")
c.Next()
return
}
inManagementNet := false
for _, ipnet := range managementNets {
if ipnet.Contains(clientIP) {
inManagementNet = true
break
}
}
if !inManagementNet {
logger.Log().WithField("ip", util.SanitizeForLog(clientIP.String())).Warn("Emergency bypass: IP not in management network")
c.Next()
return
}
// Timing-safe token comparison
if !constantTimeCompare(emergencyToken, providedToken) {
logger.Log().WithField("ip", util.SanitizeForLog(clientIP.String())).Warn("Emergency bypass: invalid token")
c.Next()
return
}
// Valid emergency token from authorized source
logger.Log().WithFields(map[string]interface{}{
"ip": util.SanitizeForLog(clientIP.String()),
"path": util.SanitizeForLog(c.Request.URL.Path),
}).Warn("EMERGENCY BYPASS ACTIVE: Request bypassing all security checks")
// Set flag for downstream handlers to know this is an emergency request
c.Set("emergency_bypass", true)
// Strip emergency token header to prevent it from reaching application
// This is critical for security - prevents token exposure in logs
c.Request.Header.Del(EmergencyTokenHeader)
c.Next()
}
}
func mustParseCIDR(cidr string) *net.IPNet {
_, ipnet, _ := net.ParseCIDR(cidr)
return ipnet
}
func constantTimeCompare(a, b string) bool {
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}