config builder and certs generator
This commit is contained in:
+15
-1
@@ -3,4 +3,18 @@ database.db
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.db
|
*.db
|
||||||
*.db.old
|
*.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
|
||||||
@@ -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]
|
|
||||||
@@ -41,6 +41,31 @@ try:
|
|||||||
config = initialize_config(config_file, preserve_existing=True)
|
config = initialize_config(config_file, preserve_existing=True)
|
||||||
logger.info("Configuration initialized successfully")
|
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
|
# Update logging level based on config
|
||||||
debug_mode = config.getboolean('app', 'APP_DEBUG', fallback=False)
|
debug_mode = config.getboolean('app', 'APP_DEBUG', fallback=False)
|
||||||
if debug_mode:
|
if debug_mode:
|
||||||
|
|||||||
-113
@@ -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
|
|
||||||
|
|
||||||
@@ -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-----
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Create self signed certificates if they do not existst
|
||||||
openssl req -x509 -newkey rsa:4096 \
|
openssl req -x509 -newkey rsa:4096 \
|
||||||
-keyout instance/certs/key.pem \
|
-keyout instance/certs/key.pem \
|
||||||
-out instance/certs/cert.pem \
|
-out instance/certs/cert.pem \
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
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
|
|
||||||
NdF3HK9zpB3w25vIFq7AJ8GpxjEbDA==
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@@ -177,3 +177,236 @@ def get_filtered_message_patterns():
|
|||||||
'file changed',
|
'file changed',
|
||||||
'reloading'
|
'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
|
||||||
|
|||||||
Reference in New Issue
Block a user