feat: implement AITBC mesh network deployment infrastructure
✅ Phase 0: Pre-implementation checklist completed - Environment configurations (dev/staging/production) - Directory structure setup (logs, backups, monitoring) - Virtual environment with dependencies ✅ Master deployment script created - Single command deployment with validation - Progress tracking and rollback capability - Health checks and deployment reporting ✅ Validation script created - Module import validation - Basic functionality testing - Configuration and script verification ✅ Implementation fixes - Fixed dataclass import in consensus keys - Fixed async function syntax in tests - Updated deployment script for virtual environment 🚀 Ready for deployment: ./scripts/deploy-mesh-network.sh dev
This commit is contained in:
279
backups/pre_deployment_20260402_120604/config/security/environment-audit.py
Executable file
279
backups/pre_deployment_20260402_120604/config/security/environment-audit.py
Executable file
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Environment Configuration Security Auditor
|
||||
Validates environment files against security rules
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import yaml
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Any
|
||||
|
||||
|
||||
class EnvironmentAuditor:
|
||||
"""Audits environment configurations for security issues"""
|
||||
|
||||
def __init__(self, config_dir: Path = None):
|
||||
self.config_dir = config_dir or Path(__file__).parent.parent
|
||||
self.validation_rules = self._load_validation_rules()
|
||||
self.issues: List[Dict[str, Any]] = []
|
||||
|
||||
def _load_validation_rules(self) -> Dict[str, Any]:
|
||||
"""Load secret validation rules"""
|
||||
rules_file = self.config_dir / "security" / "secret-validation.yaml"
|
||||
if rules_file.exists():
|
||||
with open(rules_file) as f:
|
||||
return yaml.safe_load(f)
|
||||
return {}
|
||||
|
||||
def audit_environment_file(self, env_file: Path) -> List[Dict[str, Any]]:
|
||||
"""Audit a single environment file"""
|
||||
issues = []
|
||||
|
||||
if not env_file.exists():
|
||||
return [{"file": str(env_file), "level": "ERROR", "message": "File does not exist"}]
|
||||
|
||||
with open(env_file) as f:
|
||||
content = f.read()
|
||||
|
||||
# Check for forbidden patterns
|
||||
forbidden_patterns = self.validation_rules.get("forbidden_patterns", [])
|
||||
production_forbidden_patterns = self.validation_rules.get("production_forbidden_patterns", [])
|
||||
|
||||
# Always check general forbidden patterns
|
||||
for pattern in forbidden_patterns:
|
||||
if re.search(pattern, content, re.IGNORECASE):
|
||||
issues.append({
|
||||
"file": str(env_file),
|
||||
"level": "CRITICAL",
|
||||
"message": f"Forbidden pattern detected: {pattern}",
|
||||
"line": self._find_pattern_line(content, pattern)
|
||||
})
|
||||
|
||||
# Check production-specific forbidden patterns
|
||||
if "production" in str(env_file):
|
||||
for pattern in production_forbidden_patterns:
|
||||
if re.search(pattern, content, re.IGNORECASE):
|
||||
issues.append({
|
||||
"file": str(env_file),
|
||||
"level": "CRITICAL",
|
||||
"message": f"Production forbidden pattern: {pattern}",
|
||||
"line": self._find_pattern_line(content, pattern)
|
||||
})
|
||||
|
||||
# Check for template secrets
|
||||
template_patterns = [
|
||||
r"your-.*-key-here",
|
||||
r"change-this-.*",
|
||||
r"your-.*-password"
|
||||
]
|
||||
|
||||
for pattern in template_patterns:
|
||||
if re.search(pattern, content, re.IGNORECASE):
|
||||
issues.append({
|
||||
"file": str(env_file),
|
||||
"level": "HIGH",
|
||||
"message": f"Template secret found: {pattern}",
|
||||
"line": self._find_pattern_line(content, pattern)
|
||||
})
|
||||
|
||||
# Check for localhost in production files
|
||||
if "production" in str(env_file):
|
||||
localhost_patterns = [r"localhost", r"127\.0\.0\.1", r"sqlite://"]
|
||||
for pattern in localhost_patterns:
|
||||
if re.search(pattern, content):
|
||||
issues.append({
|
||||
"file": str(env_file),
|
||||
"level": "HIGH",
|
||||
"message": f"Localhost reference in production: {pattern}",
|
||||
"line": self._find_pattern_line(content, pattern)
|
||||
})
|
||||
|
||||
# Validate secret references
|
||||
lines = content.split('\n')
|
||||
for i, line in enumerate(lines, 1):
|
||||
if '=' in line and not line.strip().startswith('#'):
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
# Check if value should be a secret reference
|
||||
if self._should_be_secret(key) and not value.startswith('secretRef:'):
|
||||
issues.append({
|
||||
"file": str(env_file),
|
||||
"level": "MEDIUM",
|
||||
"message": f"Potential secret not using secretRef: {key}",
|
||||
"line": i
|
||||
})
|
||||
|
||||
return issues
|
||||
|
||||
def _should_be_secret(self, key: str) -> bool:
|
||||
"""Check if a key should be a secret reference"""
|
||||
secret_keywords = [
|
||||
'key', 'secret', 'password', 'token', 'credential',
|
||||
'api_key', 'encryption_key', 'hmac_secret', 'jwt_secret',
|
||||
'dsn', 'database_url'
|
||||
]
|
||||
|
||||
return any(keyword in key.lower() for keyword in secret_keywords)
|
||||
|
||||
def _find_pattern_line(self, content: str, pattern: str) -> int:
|
||||
"""Find line number where pattern appears"""
|
||||
lines = content.split('\n')
|
||||
for i, line in enumerate(lines, 1):
|
||||
if re.search(pattern, line, re.IGNORECASE):
|
||||
return i
|
||||
return 0
|
||||
|
||||
def audit_all_environments(self) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""Audit all environment files"""
|
||||
results = {}
|
||||
|
||||
# Check environments directory
|
||||
env_dir = self.config_dir / "environments"
|
||||
if env_dir.exists():
|
||||
for env_file in env_dir.rglob("*.env*"):
|
||||
if env_file.is_file():
|
||||
issues = self.audit_environment_file(env_file)
|
||||
if issues:
|
||||
results[str(env_file)] = issues
|
||||
|
||||
# Check root directory .env files
|
||||
root_dir = self.config_dir.parent
|
||||
for pattern in [".env.example", ".env*"]:
|
||||
for env_file in root_dir.glob(pattern):
|
||||
if env_file.is_file() and env_file.name != ".env":
|
||||
issues = self.audit_environment_file(env_file)
|
||||
if issues:
|
||||
results[str(env_file)] = issues
|
||||
|
||||
return results
|
||||
|
||||
def generate_report(self) -> Dict[str, Any]:
|
||||
"""Generate comprehensive security report"""
|
||||
results = self.audit_all_environments()
|
||||
|
||||
# Count issues by severity
|
||||
severity_counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0}
|
||||
total_issues = 0
|
||||
|
||||
for file_issues in results.values():
|
||||
for issue in file_issues:
|
||||
severity = issue["level"]
|
||||
severity_counts[severity] += 1
|
||||
total_issues += 1
|
||||
|
||||
return {
|
||||
"summary": {
|
||||
"total_issues": total_issues,
|
||||
"files_audited": len(results),
|
||||
"severity_breakdown": severity_counts
|
||||
},
|
||||
"issues": results,
|
||||
"recommendations": self._generate_recommendations(severity_counts)
|
||||
}
|
||||
|
||||
def _generate_recommendations(self, severity_counts: Dict[str, int]) -> List[str]:
|
||||
"""Generate security recommendations based on findings"""
|
||||
recommendations = []
|
||||
|
||||
if severity_counts["CRITICAL"] > 0:
|
||||
recommendations.append("CRITICAL: Fix forbidden patterns immediately")
|
||||
|
||||
if severity_counts["HIGH"] > 0:
|
||||
recommendations.append("HIGH: Remove template secrets and localhost references")
|
||||
|
||||
if severity_counts["MEDIUM"] > 0:
|
||||
recommendations.append("MEDIUM: Use secretRef for all sensitive values")
|
||||
|
||||
if severity_counts["LOW"] > 0:
|
||||
recommendations.append("LOW: Review and improve configuration structure")
|
||||
|
||||
if not any(severity_counts.values()):
|
||||
recommendations.append("✅ No security issues found")
|
||||
|
||||
return recommendations
|
||||
|
||||
|
||||
def main():
|
||||
"""Main audit function"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Audit environment configurations")
|
||||
parser.add_argument("--config-dir", help="Configuration directory path")
|
||||
parser.add_argument("--output", help="Output report to file")
|
||||
parser.add_argument("--format", choices=["json", "yaml", "text"], default="json", help="Report format")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
auditor = EnvironmentAuditor(Path(args.config_dir) if args.config_dir else None)
|
||||
report = auditor.generate_report()
|
||||
|
||||
# Output report
|
||||
if args.format == "json":
|
||||
import json
|
||||
output = json.dumps(report, indent=2)
|
||||
elif args.format == "yaml":
|
||||
output = yaml.dump(report, default_flow_style=False)
|
||||
else:
|
||||
output = format_text_report(report)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(output)
|
||||
print(f"Report saved to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
# Exit with error code if issues found
|
||||
if report["summary"]["total_issues"] > 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def format_text_report(report: Dict[str, Any]) -> str:
|
||||
"""Format report as readable text"""
|
||||
lines = []
|
||||
lines.append("=" * 60)
|
||||
lines.append("ENVIRONMENT SECURITY AUDIT REPORT")
|
||||
lines.append("=" * 60)
|
||||
lines.append("")
|
||||
|
||||
# Summary
|
||||
summary = report["summary"]
|
||||
lines.append(f"Files Audited: {summary['files_audited']}")
|
||||
lines.append(f"Total Issues: {summary['total_issues']}")
|
||||
lines.append("")
|
||||
|
||||
# Severity breakdown
|
||||
lines.append("Severity Breakdown:")
|
||||
for severity, count in summary["severity_breakdown"].items():
|
||||
if count > 0:
|
||||
lines.append(f" {severity}: {count}")
|
||||
lines.append("")
|
||||
|
||||
# Issues by file
|
||||
if report["issues"]:
|
||||
lines.append("ISSUES FOUND:")
|
||||
lines.append("-" * 40)
|
||||
|
||||
for file_path, file_issues in report["issues"].items():
|
||||
lines.append(f"\n📁 {file_path}")
|
||||
for issue in file_issues:
|
||||
lines.append(f" {issue['level']}: {issue['message']}")
|
||||
if issue.get('line'):
|
||||
lines.append(f" Line: {issue['line']}")
|
||||
|
||||
# Recommendations
|
||||
lines.append("\nRECOMMENDATIONS:")
|
||||
lines.append("-" * 40)
|
||||
for rec in report["recommendations"]:
|
||||
lines.append(f"• {rec}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
283
backups/pre_deployment_20260402_120604/config/security/helm-values-audit.py
Executable file
283
backups/pre_deployment_20260402_120604/config/security/helm-values-audit.py
Executable file
@@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Helm Values Security Auditor
|
||||
Validates Helm values files for proper secret references
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import yaml
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Any
|
||||
|
||||
|
||||
class HelmValuesAuditor:
|
||||
"""Audits Helm values files for security issues"""
|
||||
|
||||
def __init__(self, helm_dir: Path = None):
|
||||
self.helm_dir = helm_dir or Path(__file__).parent.parent.parent / "infra" / "helm"
|
||||
self.issues: List[Dict[str, Any]] = []
|
||||
|
||||
def audit_helm_values_file(self, values_file: Path) -> List[Dict[str, Any]]:
|
||||
"""Audit a single Helm values file"""
|
||||
issues = []
|
||||
|
||||
if not values_file.exists():
|
||||
return [{"file": str(values_file), "level": "ERROR", "message": "File does not exist"}]
|
||||
|
||||
with open(values_file) as f:
|
||||
try:
|
||||
values = yaml.safe_load(f)
|
||||
except yaml.YAMLError as e:
|
||||
return [{"file": str(values_file), "level": "ERROR", "message": f"YAML parsing error: {e}"}]
|
||||
|
||||
# Recursively check for potential secrets
|
||||
self._check_secrets_recursive(values, "", values_file, issues)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_secrets_recursive(self, obj: Any, path: str, file_path: Path, issues: List[Dict[str, Any]]):
|
||||
"""Recursively check object for potential secrets"""
|
||||
|
||||
if isinstance(obj, dict):
|
||||
for key, value in obj.items():
|
||||
current_path = f"{path}.{key}" if path else key
|
||||
|
||||
if isinstance(value, str):
|
||||
# Check for potential secrets that should use secretRef
|
||||
if self._is_potential_secret(key, value):
|
||||
if not value.startswith('secretRef:'):
|
||||
issues.append({
|
||||
"file": str(file_path),
|
||||
"level": "HIGH",
|
||||
"message": f"Potential secret not using secretRef: {current_path}",
|
||||
"value": value,
|
||||
"suggestion": f"Use secretRef:secret-name:key"
|
||||
})
|
||||
|
||||
# Recursively check nested objects
|
||||
self._check_secrets_recursive(value, current_path, file_path, issues)
|
||||
|
||||
elif isinstance(obj, list):
|
||||
for i, item in enumerate(obj):
|
||||
current_path = f"{path}[{i}]" if path else f"[{i}]"
|
||||
self._check_secrets_recursive(item, current_path, file_path, issues)
|
||||
|
||||
def _is_potential_secret(self, key: str, value: str) -> bool:
|
||||
"""Check if a key-value pair represents a potential secret"""
|
||||
|
||||
# Skip Kubernetes built-in values
|
||||
kubernetes_builtins = [
|
||||
'topology.kubernetes.io/zone',
|
||||
'topology.kubernetes.io/region',
|
||||
'kubernetes.io/hostname',
|
||||
'app.kubernetes.io/name'
|
||||
]
|
||||
|
||||
if value in kubernetes_builtins:
|
||||
return False
|
||||
|
||||
# Skip common non-secret values
|
||||
non_secret_values = [
|
||||
'warn', 'info', 'debug', 'error',
|
||||
'admin', 'user', 'postgres',
|
||||
'http://prometheus-server:9090',
|
||||
'http://127.0.0.1:5001/',
|
||||
'stable', 'latest', 'IfNotPresent',
|
||||
'db-credentials', 'redis-credentials',
|
||||
'aitbc', 'coordinator', 'postgresql'
|
||||
]
|
||||
|
||||
if value in non_secret_values:
|
||||
return False
|
||||
|
||||
# Skip Helm chart specific configurations
|
||||
helm_config_keys = [
|
||||
'existingSecret', 'existingSecretPassword',
|
||||
'serviceAccountName', 'serviceAccount.create',
|
||||
'ingress.enabled', 'networkPolicy.enabled',
|
||||
'podSecurityPolicy.enabled', 'autoscaling.enabled'
|
||||
]
|
||||
|
||||
if key in helm_config_keys:
|
||||
return False
|
||||
|
||||
# Check key patterns for actual secrets
|
||||
secret_key_patterns = [
|
||||
r'.*password$', r'.*secret$', r'.*token$',
|
||||
r'.*credential$', r'.*dsn$',
|
||||
r'database_url', r'api_key', r'encryption_key', r'hmac_secret',
|
||||
r'jwt_secret', r'private_key', r'adminPassword'
|
||||
]
|
||||
|
||||
key_lower = key.lower()
|
||||
value_lower = value.lower()
|
||||
|
||||
# Check if key suggests it's a secret
|
||||
for pattern in secret_key_patterns:
|
||||
if re.match(pattern, key_lower):
|
||||
return True
|
||||
|
||||
# Check if value looks like a secret (more strict)
|
||||
secret_value_patterns = [
|
||||
r'^postgresql://.*:.*@', # PostgreSQL URLs with credentials
|
||||
r'^mysql://.*:.*@', # MySQL URLs with credentials
|
||||
r'^mongodb://.*:.*@', # MongoDB URLs with credentials
|
||||
r'^sk-[a-zA-Z0-9]{48}', # Stripe keys
|
||||
r'^ghp_[a-zA-Z0-9]{36}', # GitHub personal access tokens
|
||||
r'^xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]{24}', # Slack bot tokens
|
||||
r'^[a-fA-F0-9]{64}$', # 256-bit hex keys
|
||||
r'^[a-zA-Z0-9+/]{40,}={0,2}$', # Base64 encoded secrets
|
||||
]
|
||||
|
||||
for pattern in secret_value_patterns:
|
||||
if re.match(pattern, value):
|
||||
return True
|
||||
|
||||
# Check for actual secrets in value (more strict)
|
||||
if len(value) > 20 and any(indicator in value_lower for indicator in ['password', 'secret', 'key', 'token']):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def audit_all_helm_values(self) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""Audit all Helm values files"""
|
||||
results = {}
|
||||
|
||||
# Find all values.yaml files
|
||||
for values_file in self.helm_dir.rglob("values*.yaml"):
|
||||
if values_file.is_file():
|
||||
issues = self.audit_helm_values_file(values_file)
|
||||
if issues:
|
||||
results[str(values_file)] = issues
|
||||
|
||||
return results
|
||||
|
||||
def generate_report(self) -> Dict[str, Any]:
|
||||
"""Generate comprehensive security report"""
|
||||
results = self.audit_all_helm_values()
|
||||
|
||||
# Count issues by severity
|
||||
severity_counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0}
|
||||
total_issues = 0
|
||||
|
||||
for file_issues in results.values():
|
||||
for issue in file_issues:
|
||||
severity = issue["level"]
|
||||
severity_counts[severity] += 1
|
||||
total_issues += 1
|
||||
|
||||
return {
|
||||
"summary": {
|
||||
"total_issues": total_issues,
|
||||
"files_audited": len(results),
|
||||
"severity_breakdown": severity_counts
|
||||
},
|
||||
"issues": results,
|
||||
"recommendations": self._generate_recommendations(severity_counts)
|
||||
}
|
||||
|
||||
def _generate_recommendations(self, severity_counts: Dict[str, int]) -> List[str]:
|
||||
"""Generate security recommendations based on findings"""
|
||||
recommendations = []
|
||||
|
||||
if severity_counts["CRITICAL"] > 0:
|
||||
recommendations.append("CRITICAL: Fix critical secret exposure immediately")
|
||||
|
||||
if severity_counts["HIGH"] > 0:
|
||||
recommendations.append("HIGH: Use secretRef for all sensitive values")
|
||||
|
||||
if severity_counts["MEDIUM"] > 0:
|
||||
recommendations.append("MEDIUM: Review and validate secret references")
|
||||
|
||||
if severity_counts["LOW"] > 0:
|
||||
recommendations.append("LOW: Improve secret management practices")
|
||||
|
||||
if not any(severity_counts.values()):
|
||||
recommendations.append("✅ No security issues found")
|
||||
|
||||
return recommendations
|
||||
|
||||
|
||||
def main():
|
||||
"""Main audit function"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Audit Helm values for security issues")
|
||||
parser.add_argument("--helm-dir", help="Helm directory path")
|
||||
parser.add_argument("--output", help="Output report to file")
|
||||
parser.add_argument("--format", choices=["json", "yaml", "text"], default="json", help="Report format")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
auditor = HelmValuesAuditor(Path(args.helm_dir) if args.helm_dir else None)
|
||||
report = auditor.generate_report()
|
||||
|
||||
# Output report
|
||||
if args.format == "json":
|
||||
import json
|
||||
output = json.dumps(report, indent=2)
|
||||
elif args.format == "yaml":
|
||||
output = yaml.dump(report, default_flow_style=False)
|
||||
else:
|
||||
output = format_text_report(report)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(output)
|
||||
print(f"Report saved to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
# Exit with error code if issues found
|
||||
if report["summary"]["total_issues"] > 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def format_text_report(report: Dict[str, Any]) -> str:
|
||||
"""Format report as readable text"""
|
||||
lines = []
|
||||
lines.append("=" * 60)
|
||||
lines.append("HELM VALUES SECURITY AUDIT REPORT")
|
||||
lines.append("=" * 60)
|
||||
lines.append("")
|
||||
|
||||
# Summary
|
||||
summary = report["summary"]
|
||||
lines.append(f"Files Audited: {summary['files_audited']}")
|
||||
lines.append(f"Total Issues: {summary['total_issues']}")
|
||||
lines.append("")
|
||||
|
||||
# Severity breakdown
|
||||
lines.append("Severity Breakdown:")
|
||||
for severity, count in summary["severity_breakdown"].items():
|
||||
if count > 0:
|
||||
lines.append(f" {severity}: {count}")
|
||||
lines.append("")
|
||||
|
||||
# Issues by file
|
||||
if report["issues"]:
|
||||
lines.append("ISSUES FOUND:")
|
||||
lines.append("-" * 40)
|
||||
|
||||
for file_path, file_issues in report["issues"].items():
|
||||
lines.append(f"\n📁 {file_path}")
|
||||
for issue in file_issues:
|
||||
lines.append(f" {issue['level']}: {issue['message']}")
|
||||
if 'value' in issue:
|
||||
lines.append(f" Current value: {issue['value']}")
|
||||
if 'suggestion' in issue:
|
||||
lines.append(f" Suggestion: {issue['suggestion']}")
|
||||
|
||||
# Recommendations
|
||||
lines.append("\nRECOMMENDATIONS:")
|
||||
lines.append("-" * 40)
|
||||
for rec in report["recommendations"]:
|
||||
lines.append(f"• {rec}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,73 @@
|
||||
# Secret Validation Rules
|
||||
# Defines which environment variables must use secret references
|
||||
|
||||
production_secrets:
|
||||
coordinator:
|
||||
required_secrets:
|
||||
- pattern: "DATABASE_URL"
|
||||
secret_ref: "db-credentials"
|
||||
validation: "postgresql://"
|
||||
|
||||
- pattern: "ADMIN_API_KEY"
|
||||
secret_ref: "api-keys:admin"
|
||||
validation: "^[a-zA-Z0-9]{32,}$"
|
||||
|
||||
- pattern: "CLIENT_API_KEY"
|
||||
secret_ref: "api-keys:client"
|
||||
validation: "^[a-zA-Z0-9]{32,}$"
|
||||
|
||||
- pattern: "ENCRYPTION_KEY"
|
||||
secret_ref: "security-keys:encryption"
|
||||
validation: "^[a-fA-F0-9]{64}$"
|
||||
|
||||
- pattern: "HMAC_SECRET"
|
||||
secret_ref: "security-keys:hmac"
|
||||
validation: "^[a-fA-F0-9]{64}$"
|
||||
|
||||
- pattern: "JWT_SECRET"
|
||||
secret_ref: "security-keys:jwt"
|
||||
validation: "^[a-fA-F0-9]{64}$"
|
||||
|
||||
- pattern: "OPENAI_API_KEY"
|
||||
secret_ref: "external-services:openai"
|
||||
validation: "^sk-"
|
||||
|
||||
- pattern: "SENTRY_DSN"
|
||||
secret_ref: "monitoring:sentry"
|
||||
validation: "^https://"
|
||||
|
||||
wallet_daemon:
|
||||
required_secrets:
|
||||
- pattern: "COORDINATOR_API_KEY"
|
||||
secret_ref: "api-keys:coordinator"
|
||||
validation: "^[a-zA-Z0-9]{32,}$"
|
||||
|
||||
forbidden_patterns:
|
||||
# These patterns should never appear in ANY configs
|
||||
- "your-.*-key-here"
|
||||
- "change-this-.*"
|
||||
- "password="
|
||||
- "secret_key="
|
||||
- "api_secret="
|
||||
|
||||
production_forbidden_patterns:
|
||||
# These patterns should never appear in PRODUCTION configs
|
||||
- "localhost"
|
||||
- "127.0.0.1"
|
||||
- "sqlite://"
|
||||
- "debug.*true"
|
||||
|
||||
validation_rules:
|
||||
# Minimum security requirements
|
||||
min_key_length: 32
|
||||
require_complexity: true
|
||||
no_default_values: true
|
||||
no_localhost_in_prod: true
|
||||
|
||||
# Database security
|
||||
require_ssl_database: true
|
||||
forbid_sqlite_in_prod: true
|
||||
|
||||
# API security
|
||||
require_https_urls: true
|
||||
validate_api_key_format: true
|
||||
Reference in New Issue
Block a user