config builder and certs generator

This commit is contained in:
ghostersk
2025-05-28 10:40:58 +01:00
parent 98ea741e1d
commit 3eef9ad290
8 changed files with 274 additions and 318 deletions

16
.gitignore vendored
View File

@@ -3,4 +3,18 @@ database.db
__pycache__/
*.db
*.db.old
config.ini
# Configuration files (contain secrets)
config.ini
config.ini.backup
*.ini.backup
cert.pem
key.pem
# Logs
*.log
gunicorn.pid
# OS generated files
.DS_Store
Thumbs.db

118
app.log
View File

@@ -1,118 +0,0 @@
nohup: ignoring input
2025-05-25 05:20:10,213 - __main__ - INFO - No specific proxy IPs configured - trusting all proxies
2025-05-25 05:20:10,229 - utils.rate_limiter - INFO - No Redis URL configured, using in-memory rate limiting
2025-05-25 05:20:10,243 - app - INFO - Database logging initialized
2025-05-25 05:20:10,244 - __main__ - INFO - Starting application on 0.0.0.0:8000 with SSL
2025-05-25 05:20:10,244 - __main__ - INFO - SSL certificate: instance/certs/cert.pem
2025-05-25 05:20:10,244 - __main__ - INFO - SSL key: instance/certs/key.pem
2025-05-25 05:20:10,244 - __main__ - INFO - Development mode: False
2025-05-25 05:20:10,244 - __main__ - INFO - File watching: False
2025-05-25 05:20:10,244 - __main__ - INFO - Workers: 2
2025-05-25 05:20:10,244 - __main__ - INFO - Uvicorn allowing all IPs for forwarded headers
INFO: Uvicorn running on https://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: Started parent process [7718]
2025-05-25 05:20:10,568 - __mp_main__ - INFO - No specific proxy IPs configured - trusting all proxies
2025-05-25 05:20:10,571 - __mp_main__ - INFO - No specific proxy IPs configured - trusting all proxies
2025-05-25 05:20:10,586 - utils.rate_limiter - INFO - No Redis URL configured, using in-memory rate limiting
2025-05-25 05:20:10,589 - utils.rate_limiter - INFO - No Redis URL configured, using in-memory rate limiting
2025-05-25 05:20:10,602 - __mp_main__ - INFO - Database logging initialized
2025-05-25 05:20:10,605 - __mp_main__ - INFO - Database logging initialized
2025-05-25 05:20:10,620 - app - INFO - No specific proxy IPs configured - trusting all proxies
2025-05-25 05:20:10,623 - app - INFO - No specific proxy IPs configured - trusting all proxies
2025-05-25 05:20:10,632 - utils.rate_limiter - INFO - No Redis URL configured, using in-memory rate limiting
2025-05-25 05:20:10,635 - utils.rate_limiter - INFO - No Redis URL configured, using in-memory rate limiting
2025-05-25 05:20:10,636 - app - INFO - Database logging initialized
INFO: Started server process [7721]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
2025-05-25 05:20:10,639 - app - INFO - Database logging initialized
INFO: Started server process [7722]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
INFO: 127.0.0.1:49078 - "GET / HTTP/1.1" 200 OK
2025-05-25 05:21:45,554 - frontend.routes - WARNING - TEST: Warning level log from /test-error-logging route
2025-05-25 05:21:45,557 - frontend.routes - ERROR - TEST: Error level log from /test-error-logging route
2025-05-25 05:21:45,559 - frontend.routes - CRITICAL - TEST: Critical level log from /test-error-logging route
2025-05-25 05:21:45,562 - app - WARNING - TEST: Flask app warning from test route
2025-05-25 05:21:45,564 - app - ERROR - TEST: Flask app error from test route
2025-05-25 05:21:45,567 - frontend.routes - ERROR - TEST: Exception caught in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
2025-05-25 05:21:45,569 - app - ERROR - TEST: Flask app exception in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
INFO: 127.0.0.1:54616 - "GET /test-error-logging HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /auth/admin/error_logs?start_date=2025-05-18&end_date=2025-05-25&level= HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/img/favicon.png HTTP/1.1" 304 Not Modified
INFO: 213.249.224.235:0 - "GET /auth/admin/error_logs?start_date=2025-05-18&end_date=2025-05-25&level= HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/css/custom.css HTTP/1.1" 304 Not Modified
INFO: 213.249.224.235:0 - "GET /static/img/favicon.png HTTP/1.1" 304 Not Modified
INFO: 213.249.224.235:0 - "GET /static/css/buttons.bootstrap5.min.css HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/css/buttons.dataTables.min.css HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/css/bootstrap.min.css HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/css/dataTables.bootstrap5.min.css HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/jquery-3.6.0.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/jquery.dataTables.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/bootstrap.bundle.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/buttons.html5.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/dataTables.bootstrap5.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/jszip.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/dataTables.buttons.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/js/buttons.bootstrap5.min.js HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /auth/admin HTTP/1.1" 404 Not Found
INFO: 213.249.224.235:0 - "GET /favicon.ico HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /auth/admin/error_logs?start_date=2025-05-18&end_date=2025-05-25&level= HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/img/favicon.png HTTP/1.1" 304 Not Modified
INFO: 213.249.224.235:0 - "GET /static/img/favicon.png HTTP/1.1" 304 Not Modified
2025-05-25 05:23:04,784 - frontend.routes - WARNING - TEST: Warning level log from /test-error-logging route
2025-05-25 05:23:04,786 - frontend.routes - ERROR - TEST: Error level log from /test-error-logging route
2025-05-25 05:23:04,787 - frontend.routes - CRITICAL - TEST: Critical level log from /test-error-logging route
2025-05-25 05:23:04,789 - app - WARNING - TEST: Flask app warning from test route
2025-05-25 05:23:04,792 - app - ERROR - TEST: Flask app error from test route
2025-05-25 05:23:04,793 - frontend.routes - ERROR - TEST: Exception caught in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
2025-05-25 05:23:04,795 - app - ERROR - TEST: Flask app exception in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
INFO: 127.0.0.1:60580 - "GET /test-error-logging HTTP/1.1" 200 OK
2025-05-25 05:24:41,331 - frontend.routes - WARNING - TEST: Warning level log from /test-error-logging route
2025-05-25 05:24:41,335 - frontend.routes - ERROR - TEST: Error level log from /test-error-logging route
2025-05-25 05:24:41,336 - frontend.routes - CRITICAL - TEST: Critical level log from /test-error-logging route
2025-05-25 05:24:41,338 - app - WARNING - TEST: Flask app warning from test route
2025-05-25 05:24:41,339 - app - ERROR - TEST: Flask app error from test route
2025-05-25 05:24:41,343 - frontend.routes - ERROR - TEST: Exception caught in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
2025-05-25 05:24:41,345 - app - ERROR - TEST: Flask app exception in test route
Traceback (most recent call last):
File "/opt/dockerbuilds/domain-logons/frontend/routes.py", line 42, in test_error_logging
raise ValueError("TEST: Intentional exception for database logging verification")
ValueError: TEST: Intentional exception for database logging verification
INFO: 127.0.0.1:35888 - "GET /test-error-logging HTTP/1.1" 200 OK
INFO: 127.0.0.1:35904 - "POST /api/log_event HTTP/1.1" 401 Unauthorized
INFO: 213.249.224.235:0 - "GET /auth/admin/error_logs?start_date=2025-05-18&end_date=2025-05-25&level= HTTP/1.1" 200 OK
INFO: 213.249.224.235:0 - "GET /static/img/favicon.png HTTP/1.1" 304 Not Modified
INFO: 127.0.0.1:41952 - "GET / HTTP/1.1" 200 OK
INFO: Received SIGTERM, exiting.
INFO: Terminated child process [7721]
INFO: Terminated child process [7722]
INFO: Waiting for child process [7721]
INFO: Shutting down
INFO: Shutting down
INFO: Finished server process [7721]
INFO: Finished server process [7722]
INFO: Waiting for child process [7722]
INFO: Stopping parent process [7718]

25
app.py
View File

@@ -41,6 +41,31 @@ try:
config = initialize_config(config_file, preserve_existing=True)
logger.info("Configuration initialized successfully")
# Auto-generate secure secret key if needed
from utils.toolbox import ensure_secure_secret_key, ensure_ssl_certificates
config_updated = False
secret_key_updated = ensure_secure_secret_key(config)
if secret_key_updated:
config_updated = True
logger.info("Generated new secure secret key")
# Auto-generate SSL certificates if needed
cert_path, key_path, ssl_enabled = ensure_ssl_certificates(config, os.path.dirname(__file__))
if ssl_enabled and cert_path and key_path:
config_updated = True
logger.info("SSL certificates configured")
# Save configuration if any updates were made
if config_updated:
try:
with open(config_file, 'w') as f:
config.write(f)
logger.info("Configuration file updated with new settings")
except Exception as e:
logger.error(f"Failed to save configuration updates: {e}")
# Update logging level based on config
debug_mode = config.getboolean('app', 'APP_DEBUG', fallback=False)
if debug_mode:

View File

@@ -1,113 +0,0 @@
; Configuration file for User Monitor Application
; This file is auto-managed - existing values are preserved
; Generated/Updated by ConfigManager
[app]
; Application configuration
; SECRET_KEY: Change this to a random secret key in production
; APP_DEBUG: Set to false in production
; TIMEZONE: Your local timezone for log display
secret_key = your_secret_key_change_this_in_production
app_debug = false
timezone = Europe/London
[server]
; Server configuration
; HOST: IP address to bind to (0.0.0.0 for all interfaces)
; PORT: Port number to listen on
; SSL_CERTFILE/SSL_KEYFILE: SSL certificate paths (for reverse proxy setups)
; WORKERS: Number of threads (Waitress) or processes (Gunicorn)
; DEVELOPMENT_MODE: Enable development features (false in production)
host = 0.0.0.0
port = 8000
ssl_certfile = instance/certs/cert.pem
ssl_keyfile = instance/certs/key.pem
development_mode = false
watch_files = false
workers = 4
worker_lifetime = 86400
graceful_shutdown = true
shutdown_timeout = 30
[database]
; Database configuration
; SQLALCHEMY_DATABASE_URI: Database connection string
; For SQLite (default): sqlite:///database.db
; For PostgreSQL: postgresql://user:pass@localhost:5432/dbname
; For MySQL: mysql+pymysql://user:pass@localhost:3306/dbname
; For MSSQL: mssql+pyodbc://user:pass@server/db?driver=ODBC+Driver+18+for+SQL+Server
sqlalchemy_database_uri = sqlite:///database.db
sqlalchemy_track_modifications = false
[session]
; Session and cookie configuration
; SESSION_COOKIE_SECURE: Only send cookies over HTTPS
; REMEMBER_COOKIE_DURATION: Remember me duration in seconds
session_cookie_secure = true
session_cookie_httponly = true
session_cookie_samesite = Lax
remember_cookie_secure = true
remember_cookie_httponly = true
remember_cookie_duration = 7200
permanent_session_lifetime = 7200
[cache]
; Cache and compression settings
; MAX_AGE values are in seconds
; COMPRESSION_LEVEL: 1-9 (higher = better compression, more CPU)
static_max_age = 86400
image_max_age = 604800
js_css_max_age = 43200
enable_compression = true
compression_level = 6
compression_min_size = 500
[security]
; Security headers configuration
; CONTENT_SECURITY_POLICY: Controls allowed content sources
; ENABLE_HSTS: HTTP Strict Transport Security (HTTPS only)
; HSTS_MAX_AGE: HSTS duration in seconds
content_security_policy = default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.datatables.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://cdn.datatables.net; img-src 'self' data:; font-src 'self' https://cdn.datatables.net; connect-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self'
enable_hsts = true
hsts_max_age = 31536000
enable_security_headers = true
[rate_limiting]
; Rate limiting configuration
; REDIS_URL: Redis connection for distributed rate limiting (leave empty for in-memory)
; LOGIN_LIMIT: Max login attempts per LOGIN_PERIOD seconds
; API_LIMIT: Max API requests per API_PERIOD seconds
; Leave REDIS_URL empty to use in-memory rate limiting
enable_rate_limiting = true
redis_url =
login_limit = 10
login_period = 60
register_limit = 5
register_period = 300
api_limit = 60
api_period = 60
[proxy]
; Reverse proxy configuration
; PROXY_COUNT: Number of proxies between client and app
; TRUSTED_PROXIES: Comma-separated proxy IPs (empty = trust all)
; For Docker: 172.16.0.0/12,10.0.0.0/8,192.168.0.0/16
proxy_count = 1
trust_x_forwarded_for = true
trust_x_forwarded_proto = true
trust_x_forwarded_host = true
trust_x_forwarded_port = true
trust_x_forwarded_prefix = false
trusted_proxies =
[logging]
; Database logging configuration
; DB_LOGGING_ENABLED: Enable/disable database logging
; DB_LOGGING_FILTERED_LOGGERS: Comma-separated logger names to exclude
; DB_LOGGING_FILTERED_PATTERNS: Comma-separated patterns to exclude
db_logging_enabled = true
db_logging_filtered_loggers = watchfiles.main,watchfiles.watcher,watchdog,__mp_main__,__main__,app,waitress.queue
db_logging_filtered_patterns = database.db,instance/,file changed,reloading
filter_file_watcher_logs = true
db_logging_dedupe_interval = 1

View File

@@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF7zCCA9egAwIBAgIUZPH8NtUboyhjRENnydLCoYy7ZTAwDQYJKoZIhvcNAQEL
BQAwgYYxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5hbWUxETAPBgNVBAcM
CENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkGA1UECwwSQ29tcGFu
eVNlY3Rpb25OYW1lMR0wGwYDVQQDDBRDb21tb25OYW1lT3JIb3N0bmFtZTAeFw0y
NTA1MjUwMzM2NTJaFw0zNTA1MjMwMzM2NTJaMIGGMQswCQYDVQQGEwJYWDESMBAG
A1UECAwJU3RhdGVOYW1lMREwDwYDVQQHDAhDaXR5TmFtZTEUMBIGA1UECgwLQ29t
cGFueU5hbWUxGzAZBgNVBAsMEkNvbXBhbnlTZWN0aW9uTmFtZTEdMBsGA1UEAwwU
Q29tbW9uTmFtZU9ySG9zdG5hbWUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQC9oguQAa1YeTqOXjNMuXnOJwSZFYI20Yeb4VgapqL4obFi+JvbbgkiqqiL
1gy+aID6KIpVJEBauzpx5/SI2anC51FTfY/n6R/NwBCJW4CazDUm2qE5Q7MaPUSI
ymOvR70XanxXvPev9VLpAcaRd1rD/sSOuCjPEVJiMnhoeoYlXn9/uiLAYHqeF3QY
0jVYK4vyRG9SZF6lUyV0b6wBd5uIKUDU+LIyjl3iIkRk8M41Uqg4TLLQnXVa3vVR
LDW2y1cOtWmnz8WZmvVT2QBrh4g6s2+t3OGfkfo+OKYYAA+owGfW7CzrsPf9sPhB
7xIk0/05GKSxDT83fwMDv5Ie8Oa0BEJ7LUHw5KWF7wJMfzWYobMgngtEZReImOXF
AwAo/K7g25rqC2wlLgqeAZqs1FQ8tXEZ28mMmLKWADVG5bgtBxTYisVsmcgSpulo
o3iBQzHZGrM1BcMrDwjSPuF7i+dHsM1pxw+adBRs65U0CeidyhcSWzRN/ApZUMcy
FoCwxpPwi/zd5Hd1n5uNk8KjMpc6sY+paKsfM91A3TZlPmAOjhRsGfbcFcX4MK58
XRB6Kju8JFoUduh3DySZtQ89MFVzez96VNTVCDg6kl3B03++gxCNRy5XlmRI6nbb
o/WEnXI5ffWQhvd+wz0rr8Fg3/7+w1ljhf9aRh3MCSScPZE5swIDAQABo1MwUTAd
BgNVHQ4EFgQUnNq+CjzjU5fUeJtqf8PvJ6DVgZMwHwYDVR0jBBgwFoAUnNq+Cjzj
U5fUeJtqf8PvJ6DVgZMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AgEAHVFCdCRgiRBlQXP7P8eX4npRYVJnRcCzKL2ALpVu9l8ZZJOOJEPX7Mn6E8OT
Dcwca5k9d8Kdyc+/8giQ4ank93ZBU/Ptgd/m++KsTUMmwVbytAQ7uer0lhyYcOUj
OnNvM3HY5llRHcVvQ697RS22GNzOe3l/a5DU5PTHq3kdeVcqhTNzbL0pyNOKWcrg
uBZWLQjfWyQhU6eZPqnQLbeTXLtg+PGl7zM3sRLj4mU+chFQ1Q3hQALl9onpr150
jPfntUkKJH4RAGpTYokbhnGfGbUiuKrDGqUuuCWIzOxw+65edwIpgz+/h7jOQeVR
QlCFgXjHplIJHIo5u1wvYxBRAJ1oMrJcvD4lDTyLYrSdniOXQOKOHsaf9GC+6wp/
LUk2pPSqvH8JzYwSIvbtg1BQpDYgxhnphAx4nNpF5U2X04hkjpK7LZrV2qKGhJDo
bTcLk6LhX1ssS5udvU7Vgmof0o5n4HaS6rcDaSp8ywnHcBAtNt8sme3/NQxU0wZs
iU3+LrmTm9kEztFYznfUyPDBjjIctHq/k7sCFPlbqGsjcGDfCd0hpHOT/xaS2CYV
0c8XzkU1B3VC4pNmtLYs0THRy0NSgYVkL7iENXLe1jBhspcLrDKRp0Ke1hPmcFLY
oVRbSDRvbKL+htXFAu2bCM1uW4tnyZQzKgIHjxrEjiR4VbU=
-----END CERTIFICATE-----

View File

@@ -1,4 +1,5 @@
#!/bin/bash
# Create self signed certificates if they do not existst
openssl req -x509 -newkey rsa:4096 \
-keyout instance/certs/key.pem \
-out instance/certs/cert.pem \

View File

@@ -1,52 +0,0 @@
-----BEGIN PRIVATE KEY-----
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
NdF3HK9zpB3w25vIFq7AJ8GpxjEbDA==
-----END PRIVATE KEY-----

View File

@@ -177,3 +177,236 @@ def get_filtered_message_patterns():
'file changed',
'reloading'
]
def generate_secret_key():
"""Generate a secure random secret key for Flask"""
import secrets
return secrets.token_hex(32)
def ensure_secure_secret_key(config):
"""
Ensure config has a secure secret key, generate if needed.
Args:
config: ConfigParser object
Returns:
bool: True if key was generated/updated, False if existing key was secure
"""
import logging
logger = logging.getLogger(__name__)
current_key = config.get('app', 'SECRET_KEY', fallback='')
# List of insecure keys that should be replaced
insecure_keys = [
'',
'your_secret_key',
'your_secret_key_change_this_in_production',
'dev',
'development',
'changeme',
'insecure'
]
if current_key in insecure_keys or len(current_key) < 32:
new_key = generate_secret_key()
if not config.has_section('app'):
config.add_section('app')
config.set('app', 'SECRET_KEY', new_key)
logger.info("Generated new secure secret key")
return True
return False
def generate_ssl_certificates(cert_dir, cert_file='cert.pem', key_file='key.pem',
country='XX', state='StateName', city='CityName',
org='CompanyName', org_unit='CompanySectionName',
common_name='localhost'):
"""
Generate self-signed SSL certificates if they don't exist.
Args:
cert_dir: Directory to store certificates
cert_file: Certificate filename (default: cert.pem)
key_file: Private key filename (default: key.pem)
country: Country code (default: XX)
state: State name (default: StateName)
city: City name (default: CityName)
org: Organization name (default: CompanyName)
org_unit: Organizational unit (default: CompanySectionName)
common_name: Common name/hostname (default: localhost)
Returns:
tuple: (cert_path, key_path, was_generated)
"""
import os
import subprocess
import logging
logger = logging.getLogger(__name__)
# Ensure certificate directory exists
os.makedirs(cert_dir, exist_ok=True)
cert_path = os.path.join(cert_dir, cert_file)
key_path = os.path.join(cert_dir, key_file)
# Check if certificates already exist and are valid
if os.path.exists(cert_path) and os.path.exists(key_path):
# Check if files are not empty
if os.path.getsize(cert_path) > 0 and os.path.getsize(key_path) > 0:
logger.info(f"SSL certificates already exist at {cert_path} and {key_path}")
return cert_path, key_path, False
logger.info("Generating self-signed SSL certificates...")
try:
# Build the subject string
subject = f"/C={country}/ST={state}/L={city}/O={org}/OU={org_unit}/CN={common_name}"
# Generate certificate using openssl
cmd = [
'openssl', 'req', '-x509', '-newkey', 'rsa:4096',
'-keyout', key_path,
'-out', cert_path,
'-sha256', '-days', '3650',
'-nodes',
'-subj', subject
]
# Run the command
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
# Verify files were created successfully
if os.path.exists(cert_path) and os.path.exists(key_path):
# Set appropriate file permissions (readable by owner only for key)
os.chmod(key_path, 0o600) # Private key: owner read/write only
os.chmod(cert_path, 0o644) # Certificate: owner read/write, others read
logger.info(f"✅ SSL certificates generated successfully:")
logger.info(f" Certificate: {cert_path}")
logger.info(f" Private key: {key_path}")
logger.info(f" Valid for: 3650 days (10 years)")
logger.info(f" Common Name: {common_name}")
return cert_path, key_path, True
else:
logger.error("Certificate generation failed - files not created")
return None, None, False
except subprocess.CalledProcessError as e:
logger.error(f"Failed to generate SSL certificates: {e}")
logger.error(f"OpenSSL stderr: {e.stderr}")
return None, None, False
except FileNotFoundError:
logger.error("OpenSSL not found. Please install OpenSSL to generate SSL certificates.")
logger.info("On Ubuntu/Debian: sudo apt-get install openssl")
logger.info("On CentOS/RHEL: sudo yum install openssl")
logger.info("On macOS: brew install openssl")
return None, None, False
except Exception as e:
logger.error(f"Unexpected error generating SSL certificates: {e}")
return None, None, False
def ensure_ssl_certificates(config, app_dir):
"""
Ensure SSL certificates exist, generate them if they don't.
Args:
config: ConfigParser object with SSL configuration
app_dir: Application directory path
Returns:
tuple: (cert_path, key_path, ssl_enabled)
"""
import os
import logging
logger = logging.getLogger(__name__)
# Ensure required sections exist
if not config.has_section('server'):
config.add_section('server')
if not config.has_section('ssl'):
config.add_section('ssl')
# Get SSL configuration from config with proper fallbacks
ssl_certfile = config.get('server', 'SSL_CERTFILE', fallback=None)
ssl_keyfile = config.get('server', 'SSL_KEYFILE', fallback=None)
auto_generate_ssl = config.getboolean('server', 'AUTO_GENERATE_SSL', fallback=True)
# If SSL is not configured, set default paths
if not ssl_certfile or not ssl_keyfile:
cert_dir = os.path.join(app_dir, 'instance', 'certs')
ssl_certfile = os.path.join(cert_dir, 'cert.pem')
ssl_keyfile = os.path.join(cert_dir, 'key.pem')
# Set default paths in config
config.set('server', 'SSL_CERTFILE', ssl_certfile)
config.set('server', 'SSL_KEYFILE', ssl_keyfile)
config.set('server', 'AUTO_GENERATE_SSL', 'true')
else:
# Use configured paths
cert_dir = os.path.dirname(ssl_certfile)
# Check if certificates exist
if os.path.exists(ssl_certfile) and os.path.exists(ssl_keyfile):
# Verify files are not empty
if os.path.getsize(ssl_certfile) > 0 and os.path.getsize(ssl_keyfile) > 0:
logger.info("SSL certificates found and valid")
return ssl_certfile, ssl_keyfile, True
else:
logger.warning("SSL certificate files exist but are empty, regenerating...")
# Generate certificates if auto-generation is enabled
if auto_generate_ssl:
logger.info("SSL certificates not found or invalid, generating self-signed certificates...")
# Get certificate details from config or set defaults
cert_country = config.get('ssl', 'CERT_COUNTRY', fallback='XX')
cert_state = config.get('ssl', 'CERT_STATE', fallback='StateName')
cert_city = config.get('ssl', 'CERT_CITY', fallback='CityName')
cert_org = config.get('ssl', 'CERT_ORGANIZATION', fallback='WinAuthMon')
cert_org_unit = config.get('ssl', 'CERT_ORG_UNIT', fallback='IT Department')
cert_common_name = config.get('ssl', 'CERT_COMMON_NAME', fallback='localhost')
# Set defaults in config if they don't exist
config.set('ssl', 'CERT_COUNTRY', cert_country)
config.set('ssl', 'CERT_STATE', cert_state)
config.set('ssl', 'CERT_CITY', cert_city)
config.set('ssl', 'CERT_ORGANIZATION', cert_org)
config.set('ssl', 'CERT_ORG_UNIT', cert_org_unit)
config.set('ssl', 'CERT_COMMON_NAME', cert_common_name)
cert_path, key_path, generated = generate_ssl_certificates(
cert_dir=cert_dir,
country=cert_country,
state=cert_state,
city=cert_city,
org=cert_org,
org_unit=cert_org_unit,
common_name=cert_common_name
)
if generated and cert_path and key_path:
# Update config with generated certificate paths
config.set('server', 'SSL_CERTFILE', cert_path)
config.set('server', 'SSL_KEYFILE', key_path)
return cert_path, key_path, True
elif cert_path and key_path:
# Certificates already existed
return cert_path, key_path, True
else:
logger.warning("Failed to generate SSL certificates, SSL will be disabled")
config.set('server', 'AUTO_GENERATE_SSL', 'false')
return None, None, False
else:
logger.info("SSL certificate auto-generation is disabled")
return None, None, False