This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -10,14 +10,14 @@ class Settings(BaseSettings):
ENVIRONMENT: str = Field(default='development', description='Environment: development, staging, production')
DEBUG: bool = Field(default=False, description='Debug mode')
API_V1_PREFIX: str = Field(default='/api/v1', description='API v1 prefix')
HOST: str = Field(default='0.0.0.0', description='Server host')
HOST: str = Field(default='0.0.0.0', description='Server host. WARNING: 0.0.0.0 binds to all interfaces. Use 127.0.0.1 for development or specific IP for production.') # nosec B104 # Acceptable default with validation warning in production
PORT: int = Field(default=8000, description='Server port')
DB_USER: str = Field(default='root', description='Database user')
DB_PASS: str = Field(default='', description='Database password')
DB_NAME: str = Field(default='hotel_db', description='Database name')
DB_HOST: str = Field(default='localhost', description='Database host')
DB_PORT: str = Field(default='3306', description='Database port')
JWT_SECRET: str = Field(default='dev-secret-key-change-in-production-12345', description='JWT secret key')
JWT_SECRET: str = Field(default='', description='JWT secret key - MUST be set via environment variable. Minimum 64 characters recommended for production.')
JWT_ALGORITHM: str = Field(default='HS256', description='JWT algorithm')
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(default=30, description='JWT access token expiration in minutes')
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = Field(default=3, description='JWT refresh token expiration in days (reduced from 7 for better security)')
@@ -97,6 +97,20 @@ class Settings(BaseSettings):
IP_WHITELIST_ENABLED: bool = Field(default=False, description='Enable IP whitelisting for admin endpoints')
ADMIN_IP_WHITELIST: List[str] = Field(default_factory=list, description='List of allowed IP addresses/CIDR ranges for admin endpoints')
def validate_host_configuration(self) -> None:
"""
Validate HOST configuration for security.
Warns if binding to all interfaces (0.0.0.0) in production.
"""
if self.HOST == '0.0.0.0' and self.is_production:
import logging
logger = logging.getLogger(__name__)
logger.warning(
'SECURITY WARNING: HOST is set to 0.0.0.0 in production. '
'This binds the server to all network interfaces. '
'Consider using a specific IP address or ensure proper firewall rules are in place.'
)
def validate_encryption_key(self) -> None:
"""
Validate encryption key is properly configured.
@@ -138,4 +152,41 @@ class Settings(BaseSettings):
logger = logging.getLogger(__name__)
logger.warning(f'Invalid ENCRYPTION_KEY format: {str(e)}')
settings = Settings()
settings = Settings()
# Validate JWT_SECRET on startup - fail fast if not configured
def validate_jwt_secret():
"""Validate JWT_SECRET is properly configured. Called on startup."""
if not settings.JWT_SECRET or settings.JWT_SECRET.strip() == '':
error_msg = (
'CRITICAL SECURITY ERROR: JWT_SECRET is not configured. '
'Please set JWT_SECRET environment variable to a secure random string. '
'Minimum 64 characters recommended for production. '
'Generate one using: python -c "import secrets; print(secrets.token_urlsafe(64))"'
)
import logging
logger = logging.getLogger(__name__)
logger.error(error_msg)
if settings.is_production:
raise ValueError(error_msg)
else:
logger.warning(
'JWT_SECRET not configured. This will cause authentication to fail. '
'Set JWT_SECRET environment variable before starting the application.'
)
# Warn if using weak secret (less than 64 characters)
if len(settings.JWT_SECRET) < 64:
import logging
logger = logging.getLogger(__name__)
if settings.is_production:
logger.warning(
f'JWT_SECRET is only {len(settings.JWT_SECRET)} characters. '
'Recommend using at least 64 characters for production security.'
)
else:
logger.debug(f'JWT_SECRET length: {len(settings.JWT_SECRET)} characters')
# Validate on import
validate_jwt_secret()
settings.validate_host_configuration()