feat(config): integrate rate limiting and CORS configuration from furt.conf

- Add RateLimiter:configure() function to accept config-based limits
- Integrate security section parameters (rate_limit_api_key_max, ip_max, window)
- Add CORS configuration from config file with environment fallback
- Replace hardcoded rate limiting defaults with configurable values
- Add test endpoint control via config.security.enable_test_endpoint
- Update startup logging to show actual configured rate limits
- Add configuration validation and detailed startup information

Rate limiting now uses values from [security] section instead of hardcoded
defaults. CORS origins prioritize config file over environment variables.

Related to DAW/furt#89
This commit is contained in:
michael 2025-08-29 20:01:47 +02:00
parent ecd4f68595
commit 5c17c86fd4
4 changed files with 128 additions and 49 deletions

View file

@ -4,8 +4,20 @@
# Server configuration
[server]
host = 127.0.0.1
port = 8080
port = 7811
log_level = info
log_requests = true
client_timeout = 10
# CORS configuration
cors_allowed_origins = http://localhost:1313,http://127.0.0.1:1313,https://dragons-at-work.de,https://www.dragons-at-work.de
# Security settings
[security]
rate_limit_api_key_max = 60
rate_limit_ip_max = 100
rate_limit_window = 3600
enable_test_endpoint = false
# Default SMTP settings (used when API keys don't have custom SMTP)
[smtp_default]

View file

@ -7,6 +7,45 @@ local ConfigParser = require("src.config_parser")
-- Load configuration from furt.conf
local config = ConfigParser.load_config()
-- Configure rate limiting from config
local RateLimiter = require("src.rate_limiter")
local rate_limits = {
api_key_max = config.security and config.security.rate_limit_api_key_max or 60,
ip_max = config.security and config.security.rate_limit_ip_max or 100,
window = config.security and config.security.rate_limit_window or 3600
}
RateLimiter:configure(rate_limits)
-- Parse CORS origins from config or environment
local function get_cors_origins()
-- 1. Try config file first
if config.server.cors_allowed_origins then
local origins = {}
for origin in config.server.cors_allowed_origins:gmatch("([^,]+)") do
table.insert(origins, origin:match("^%s*(.-)%s*$"))
end
return origins
end
-- 2. Try environment variable
local env_origins = os.getenv("CORS_ALLOWED_ORIGINS")
if env_origins then
local origins = {}
for origin in env_origins:gmatch("([^,]+)") do
table.insert(origins, origin:match("^%s*(.-)%s*$"))
end
return origins
end
-- 3. Development defaults
return {
"http://localhost:1313", -- Hugo dev server
"http://127.0.0.1:1313", -- Hugo dev server alternative
"http://localhost:3000", -- Common dev port
"http://127.0.0.1:3000" -- Common dev port alternative
}
end
-- Add legacy compatibility and runtime enhancements
local server_config = {
-- HTTP Server settings (from [server] section)
@ -16,33 +55,21 @@ local server_config = {
-- Timeouts and limits
client_timeout = config.server.client_timeout or 10,
-- CORS Configuration
-- CORS Configuration (prioritize config file over environment)
cors = {
allowed_origins = (function()
local env_origins = os.getenv("CORS_ALLOWED_ORIGINS")
if env_origins then
-- Parse comma-separated list from environment
local origins = {}
for origin in env_origins:gmatch("([^,]+)") do
table.insert(origins, origin:match("^%s*(.-)%s*$"))
end
return origins
else
-- Default development origins
return {
"http://localhost:1313", -- Hugo dev server
"http://127.0.0.1:1313", -- Hugo dev server alternative
"http://localhost:3000", -- Common dev port
"http://127.0.0.1:3000" -- Common dev port alternative
}
end
end)()
allowed_origins = get_cors_origins()
},
-- Logging
log_level = config.server.log_level or "info",
log_requests = config.server.log_requests or true,
-- Security settings
security = {
enable_test_endpoint = config.security and config.security.enable_test_endpoint or false,
rate_limits = rate_limits
},
-- API Keys (converted from nginx-style to old format for backward compatibility)
api_keys = config.api_keys,
@ -62,8 +89,18 @@ local server_config = {
print("Furt Multi-Tenant Configuration Loaded:")
print(" Server: " .. server_config.host .. ":" .. server_config.port)
print(" Log Level: " .. server_config.log_level)
-- Print CORS configuration
print(" CORS Origins:")
for i, origin in ipairs(server_config.cors.allowed_origins) do
print(" " .. i .. ": " .. origin)
end
-- Print security configuration
print(" Test Endpoint: " .. (server_config.security.enable_test_endpoint and "enabled" or "disabled"))
print(" Default SMTP: " .. (config.smtp_default.host or "not configured"))
-- Print API key information
local api_key_count = 0
for key_name, key_config in pairs(config.api_keys) do
api_key_count = api_key_count + 1

View file

@ -1,4 +1,4 @@
-- furt-lua/src/main.lua
-- src/main.lua
-- Pure Lua HTTP-Server for Furt API-Gateway
-- Dragons@Work Digital Sovereignty Project
@ -256,8 +256,17 @@ function FurtServer:start()
print("Content-Hash: " .. (version_info.content_hash or "unknown"))
print("VCS: " .. (version_info.vcs_info and version_info.vcs_info.hash or "none"))
print("API-Key authentication: ENABLED")
print("Rate limiting: ENABLED (60 req/hour per API key, 100 req/hour per IP)")
print("CORS enabled for configured origins")
-- Show actual configured rate limits
local rate_limits = config.security and config.security.rate_limits
if rate_limits then
print(string.format("Rate limiting: ENABLED (%d req/hour per API key, %d req/hour per IP)",
rate_limits.api_key_max, rate_limits.ip_max))
else
print("Rate limiting: ENABLED (default values)")
end
print("CORS enabled for " .. (#config.cors.allowed_origins) .. " configured origins")
print("Press Ctrl+C to stop")
while true do
@ -288,20 +297,21 @@ server:add_route("GET", "/health", function(request, server)
smtp_configured = config.smtp_default and config.smtp_default.host ~= nil,
auth_enabled = true,
rate_limiting = true,
rate_limits = config.security and config.security.rate_limits,
merkwerk_integrated = version_info.source == "merkwerk"
}
}
return server:create_response(200, response_data, nil, nil, request)
end)
-- Test endpoint for development (disable in production)
if os.getenv("ENABLE_TEST_ENDPOINT") == "true" then
-- Test endpoint for development (configurable via furt.conf)
if config.security and config.security.enable_test_endpoint then
server:add_route("POST", "/test", function(request, server)
local response_data = {
message = "Test endpoint working",
received_data = request.body,
headers_count = 0,
warning = "This is a development endpoint"
warning = "This is a development endpoint (enabled via config)"
}
-- Count headers
@ -311,7 +321,7 @@ if os.getenv("ENABLE_TEST_ENDPOINT") == "true" then
return server:create_response(200, response_data, nil, nil, request)
end)
print("[WARN] Test endpoint enabled (development mode)")
print("[WARN] Test endpoint enabled via configuration")
end
-- Protected routes (require authentication)

View file

@ -1,4 +1,4 @@
-- furt-lua/src/rate_limiter.lua
-- src/rate_limiter.lua
-- Rate limiting system for API requests
-- Dragons@Work Digital Sovereignty Project
@ -7,7 +7,7 @@ local RateLimiter = {
cleanup_interval = 300, -- Cleanup every 5 minutes
last_cleanup = os.time(),
-- Default limits
-- Default limits (configurable)
default_limits = {
api_key_max = 60, -- 60 requests per hour per API key
ip_max = 100, -- 100 requests per hour per IP
@ -15,6 +15,26 @@ local RateLimiter = {
}
}
-- Configure rate limits from config
function RateLimiter:configure(limits)
if limits then
if limits.api_key_max then
self.default_limits.api_key_max = limits.api_key_max
end
if limits.ip_max then
self.default_limits.ip_max = limits.ip_max
end
if limits.window then
self.default_limits.window = limits.window
end
print("Rate limiting configured:")
print(" API Key limit: " .. self.default_limits.api_key_max .. " req/hour")
print(" IP limit: " .. self.default_limits.ip_max .. " req/hour")
print(" Window: " .. self.default_limits.window .. " seconds")
end
end
-- Cleanup old requests from memory
function RateLimiter:cleanup_old_requests()
local now = os.time()