Add security headers and configurable rate limiting middleware

Change-Id: Id976e75e58d79ea38e7939afe7576a2295a1c5af
diff --git a/config/config.go b/config/config.go
index b7d2a16..2ad6c43 100644
--- a/config/config.go
+++ b/config/config.go
@@ -19,6 +19,7 @@
 	defaultCookieName = "km-config"
 	defaultPort       = 5725
 	defaultLogLevel   = "warn"
+	defaultRateLimit  = 100
 )
 
 // MappingRule represents a single mapping rule in the configuration
@@ -96,6 +97,7 @@
 	CookieName string        `yaml:"cookieName,omitempty"`
 	Port       int           `yaml:"port,omitempty"`
 	LogLevel   string        `yaml:"loglevel,omitempty"`
+	RateLimit  int           `yaml:"rateLimit,omitempty"` // max requests per minute per IP (0 = use default 100)
 	Lists      []MappingList `yaml:"lists,omitempty"`
 }
 
@@ -195,6 +197,7 @@
 		ServiceURL: globalConfig.ServiceURL,
 		Port:       globalConfig.Port,
 		LogLevel:   globalConfig.LogLevel,
+		RateLimit:  globalConfig.RateLimit,
 		Lists:      allLists,
 	}
 
@@ -227,6 +230,9 @@
 	if config.Port == 0 {
 		config.Port = defaultPort
 	}
+	if config.RateLimit == 0 {
+		config.RateLimit = defaultRateLimit
+	}
 }
 
 // ApplyEnvOverrides overrides configuration fields from environment variables.
@@ -253,6 +259,12 @@
 			config.Port = port
 		}
 	}
+
+	if val := os.Getenv("KORAL_MAPPER_RATE_LIMIT"); val != "" {
+		if rl, err := strconv.Atoi(val); err == nil {
+			config.RateLimit = rl
+		}
+	}
 }
 
 // validateMappingLists validates a slice of mapping lists (without duplicate ID checking)