config builder and certs generator
This commit is contained in:
16
.gitignore
vendored
16
.gitignore
vendored
@@ -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
118
app.log
@@ -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
25
app.py
@@ -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:
|
||||
|
||||
113
config.ini
113
config.ini
@@ -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
|
||||
# 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 \
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
NdF3HK9zpB3w25vIFq7AJ8GpxjEbDA==
|
||||
-----END PRIVATE KEY-----
|
||||
233
utils/toolbox.py
233
utils/toolbox.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user