config builder and certs generator
This commit is contained in:
@@ -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