Configuration Audit Scripts
Configuration Audit Scripts
Comprehensive configuration auditing:
#!/usr/bin/env python3
# /usr/local/bin/config-audit.py
import os
import re
import json
import subprocess
from datetime import datetime
class WebServerAuditor:
def __init__(self):
self.findings = []
self.apache_config = '/etc/apache2/apache2.conf'
self.nginx_config = '/etc/nginx/nginx.conf'
def audit_apache(self):
"""Audit Apache configuration"""
if not os.path.exists(self.apache_config):
return
print("Auditing Apache configuration...")
# Read main config
with open(self.apache_config, 'r') as f:
config = f.read()
# Security checks
checks = [
{
'name': 'ServerTokens',
'pattern': r'ServerTokens\s+(\w+)',
'expected': 'Prod',
'severity': 'Medium'
},
{
'name': 'ServerSignature',
'pattern': r'ServerSignature\s+(\w+)',
'expected': 'Off',
'severity': 'Low'
},
{
'name': 'TraceEnable',
'pattern': r'TraceEnable\s+(\w+)',
'expected': 'Off',
'severity': 'Medium'
}
]
for check in checks:
match = re.search(check['pattern'], config)
if match:
if match.group(1) != check['expected']:
self.findings.append({
'type': 'Apache Configuration',
'severity': check['severity'],
'finding': f"{check['name']} is set to {match.group(1)}, should be {check['expected']}"
})
else:
self.findings.append({
'type': 'Apache Configuration',
'severity': check['severity'],
'finding': f"{check['name']} is not configured"
})
# Check loaded modules
dangerous_modules = ['mod_status', 'mod_info', 'mod_userdir', 'mod_autoindex']
try:
result = subprocess.run(['apache2ctl', '-M'], capture_output=True, text=True)
for module in dangerous_modules:
if module in result.stdout:
self.findings.append({
'type': 'Apache Modules',
'severity': 'Medium',
'finding': f"Potentially dangerous module loaded: {module}"
})
except:
pass
def audit_nginx(self):
"""Audit Nginx configuration"""
if not os.path.exists(self.nginx_config):
return
print("Auditing Nginx configuration...")
with open(self.nginx_config, 'r') as f:
config = f.read()
# Security checks
if 'server_tokens off' not in config:
self.findings.append({
'type': 'Nginx Configuration',
'severity': 'Medium',
'finding': 'server_tokens is not set to off'
})
if 'client_max_body_size' not in config:
self.findings.append({
'type': 'Nginx Configuration',
'severity': 'Low',
'finding': 'client_max_body_size is not configured'
})
# Check for weak SSL protocols
if re.search(r'ssl_protocols.*TLSv1(?:\.0)?[\s;]', config):
self.findings.append({
'type': 'Nginx SSL',
'severity': 'High',
'finding': 'Weak TLS v1.0 protocol enabled'
})
def audit_ssl_certificates(self):
"""Audit SSL certificate configuration"""
print("Auditing SSL certificates...")
cert_paths = []
# Find certificate paths
for config_dir in ['/etc/apache2/sites-enabled', '/etc/nginx/sites-enabled']:
if os.path.exists(config_dir):
for file in os.listdir(config_dir):
filepath = os.path.join(config_dir, file)
with open(filepath, 'r') as f:
content = f.read()
# Extract certificate paths
certs = re.findall(r'(?:SSLCertificateFile|ssl_certificate)\s+([^\s;]+)', content)
cert_paths.extend(certs)
# Check each certificate
for cert_path in set(cert_paths):
if os.path.exists(cert_path):
try:
result = subprocess.run(
['openssl', 'x509', '-enddate', '-noout', '-in', cert_path],
capture_output=True,
text=True
)
if 'notAfter=' in result.stdout:
expiry_str = result.stdout.split('=')[1].strip()
# Check if certificate expires soon
# Simple check - production should parse the date properly
self.findings.append({
'type': 'SSL Certificate',
'severity': 'Info',
'finding': f"Certificate {cert_path} expires on {expiry_str}"
})
except:
pass
def audit_file_permissions(self):
"""Audit file and directory permissions"""
print("Auditing file permissions...")
critical_paths = [
('/etc/apache2', '755', 'Apache config directory'),
('/etc/nginx', '755', 'Nginx config directory'),
('/var/www', '755', 'Web root directory'),
('/etc/ssl/private', '700', 'SSL private keys directory')
]
for path, expected_perms, description in critical_paths:
if os.path.exists(path):
stat_info = os.stat(path)
actual_perms = oct(stat_info.st_mode)[-3:]
if actual_perms != expected_perms:
self.findings.append({
'type': 'File Permissions',
'severity': 'High' if 'private' in path else 'Medium',
'finding': f"{description} has permissions {actual_perms}, should be {expected_perms}"
})
def audit_security_headers(self):
"""Audit security header configuration"""
print("Auditing security headers configuration...")
required_headers = [
'Strict-Transport-Security',
'X-Content-Type-Options',
'X-Frame-Options',
'X-XSS-Protection',
'Content-Security-Policy'
]
# Check Apache configs
apache_sites = '/etc/apache2/sites-enabled'
if os.path.exists(apache_sites):
for site in os.listdir(apache_sites):
with open(os.path.join(apache_sites, site), 'r') as f:
content = f.read()
for header in required_headers:
if f'Header.*{header}' not in content:
self.findings.append({
'type': 'Security Headers',
'severity': 'Medium',
'finding': f"{header} not configured in Apache site {site}"
})
def generate_report(self):
"""Generate audit report"""
report = {
'audit_date': datetime.now().isoformat(),
'total_findings': len(self.findings),
'findings_by_severity': {
'High': len([f for f in self.findings if f['severity'] == 'High']),
'Medium': len([f for f in self.findings if f['severity'] == 'Medium']),
'Low': len([f for f in self.findings if f['severity'] == 'Low']),
'Info': len([f for f in self.findings if f['severity'] == 'Info'])
},
'findings': self.findings
}
return report
def run_audit(self):
"""Run all audit checks"""
print("Starting web server security audit...")
self.audit_apache()
self.audit_nginx()
self.audit_ssl_certificates()
self.audit_file_permissions()
self.audit_security_headers()
report = self.generate_report()
# Save report
with open(f'/var/log/security-audit-{datetime.now().strftime("%Y%m%d")}.json', 'w') as f:
json.dump(report, f, indent=2)
# Print summary
print(f"\nAudit Complete:")
print(f"Total Findings: {report['total_findings']}")
print(f"High Severity: {report['findings_by_severity']['High']}")
print(f"Medium Severity: {report['findings_by_severity']['Medium']}")
print(f"Low Severity: {report['findings_by_severity']['Low']}")
return report
if __name__ == '__main__':
auditor = WebServerAuditor()
auditor.run_audit()