feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Framework\Logging\Security;
/**
* Redaction Mode für Sensitive Data Redaction
*
* - FULL: Vollständige Maskierung mit '[REDACTED]'
* - PARTIAL: Teilweise Maskierung (zeige erste/letzte Zeichen)
* - HASH: Hash-basierte Maskierung für deterministische Redaction
*/
enum RedactionMode: string
{
case FULL = 'full';
case PARTIAL = 'partial';
case HASH = 'hash';
}

View File

@@ -0,0 +1,314 @@
<?php
declare(strict_types=1);
namespace App\Framework\Logging\Security;
/**
* Sensitive Data Redactor für PII Protection in Logs
*
* Automatische Erkennung und Maskierung von sensitiven Daten:
* - Passwörter
* - API Keys/Tokens
* - Kreditkarten-Nummern
* - Email-Adressen (optional)
* - IP-Adressen (optional)
* - SSN/Personalausweis-Nummern
*
* Verwendung:
* $redactor = new SensitiveDataRedactor();
* $sanitized = $redactor->redact($data);
*/
final readonly class SensitiveDataRedactor
{
/**
* Liste der Keys, die immer redacted werden
*/
private const SENSITIVE_KEYS = [
'password',
'passwd',
'pwd',
'secret',
'api_key',
'apikey',
'api_secret',
'apisecret',
'token',
'access_token',
'refresh_token',
'bearer',
'auth',
'authorization',
'private_key',
'privatekey',
'encryption_key',
'encryptionkey',
'session_id',
'sessionid',
'cookie',
'csrf',
'csrf_token',
'credit_card',
'creditcard',
'card_number',
'cardnumber',
'cvv',
'cvc',
'ssn',
'social_security',
'tax_id',
'passport',
];
/**
* Regex-Patterns für Content-basierte Erkennung
*/
private const PATTERNS = [
// Kreditkarten (Visa, MasterCard, Amex, Discover)
'credit_card' => '/\b(?:\d{4}[-\s]?){3}\d{4}\b/',
// Email-Adressen
'email' => '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/',
// Bearer Tokens (JWT-ähnlich)
'bearer_token' => '/Bearer\s+[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/',
// API Keys (alphanumerische Strings mit mindestens 20 Zeichen)
'api_key' => '/\b[A-Za-z0-9]{20,}\b/',
// IP-Adressen (IPv4)
'ipv4' => '/\b(?:\d{1,3}\.){3}\d{1,3}\b/',
// Sozialversicherungsnummern (US Format: XXX-XX-XXXX)
'ssn' => '/\b\d{3}-\d{2}-\d{4}\b/',
];
public function __construct(
private RedactionMode $mode = RedactionMode::PARTIAL,
private bool $redactEmails = false,
private bool $redactIps = false,
private string $mask = '[REDACTED]'
) {}
/**
* Redacted sensitive Daten in einem Array rekursiv
*
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
public function redact(array $data): array
{
$result = [];
foreach ($data as $key => $value) {
$normalizedKey = $this->normalizeKey($key);
// Key-basierte Redaction
if ($this->isSensitiveKey($normalizedKey)) {
$result[$key] = $this->maskValue($value);
continue;
}
// Rekursive Redaction für Arrays
if (is_array($value)) {
$result[$key] = $this->redact($value);
continue;
}
// Content-basierte Redaction für Strings
if (is_string($value)) {
$result[$key] = $this->redactContent($value);
continue;
}
// Andere Werte unverändert
$result[$key] = $value;
}
return $result;
}
/**
* Redacted sensitive Daten in einem String
*/
public function redactString(string $content): string
{
return $this->redactContent($content);
}
/**
* Prüft ob ein Key sensitiv ist
*/
private function isSensitiveKey(string $key): bool
{
return in_array($key, self::SENSITIVE_KEYS, true);
}
/**
* Normalisiert einen Key für Vergleich (lowercase, underscores)
*/
private function normalizeKey(string $key): string
{
return strtolower(str_replace(['-', ' '], '_', $key));
}
/**
* Maskiert einen Wert basierend auf RedactionMode
*/
private function maskValue(mixed $value): string|array
{
// Arrays rekursiv redacten
if (is_array($value)) {
return array_map(fn($v) => $this->mask, $value);
}
// Strings basierend auf Mode maskieren
if (is_string($value)) {
return match ($this->mode) {
RedactionMode::FULL => $this->mask,
RedactionMode::PARTIAL => $this->partialMask($value),
RedactionMode::HASH => $this->hashValue($value),
};
}
// Andere Typen vollständig redacten
return $this->mask;
}
/**
* Partial Masking: Zeige erste und letzte Zeichen
*/
private function partialMask(string $value): string
{
$length = mb_strlen($value);
if ($length <= 4) {
return str_repeat('*', $length);
}
$visible = 2;
$start = mb_substr($value, 0, $visible);
$end = mb_substr($value, -$visible);
$masked = str_repeat('*', max(0, $length - ($visible * 2)));
return $start . $masked . $end;
}
/**
* Hash-basierte Redaction für deterministische Maskierung
*/
private function hashValue(string $value): string
{
$hash = hash('sha256', $value);
return '[HASH:' . substr($hash, 0, 12) . ']';
}
/**
* Content-basierte Redaction mit Regex-Patterns
*/
private function redactContent(string $content): string
{
// Kreditkarten
$content = preg_replace(
self::PATTERNS['credit_card'],
'[CREDIT_CARD]',
$content
);
// Bearer Tokens
$content = preg_replace(
self::PATTERNS['bearer_token'],
'Bearer [REDACTED]',
$content
);
// API Keys (nur wenn nicht bereits durch Key-Matching redacted)
$content = preg_replace(
self::PATTERNS['api_key'],
'[API_KEY]',
$content
);
// SSN
$content = preg_replace(
self::PATTERNS['ssn'],
'[SSN]',
$content
);
// Optional: Email-Adressen
if ($this->redactEmails) {
$content = preg_replace_callback(
self::PATTERNS['email'],
fn($matches) => $this->maskEmail($matches[0]),
$content
);
}
// Optional: IP-Adressen
if ($this->redactIps) {
$content = preg_replace(
self::PATTERNS['ipv4'],
'[IP_ADDRESS]',
$content
);
}
return $content;
}
/**
* Maskiert Email-Adresse: john.doe@example.com → j***e@example.com
*/
private function maskEmail(string $email): string
{
[$local, $domain] = explode('@', $email, 2);
$length = mb_strlen($local);
if ($length <= 2) {
$maskedLocal = str_repeat('*', $length);
} else {
$maskedLocal = mb_substr($local, 0, 1)
. str_repeat('*', max(0, $length - 2))
. mb_substr($local, -1);
}
return $maskedLocal . '@' . $domain;
}
/**
* Factory: Erstelle Redactor für Production (Full Redaction)
*/
public static function production(): self
{
return new self(
mode: RedactionMode::FULL,
redactEmails: true,
redactIps: true
);
}
/**
* Factory: Erstelle Redactor für Development (Partial Redaction)
*/
public static function development(): self
{
return new self(
mode: RedactionMode::PARTIAL,
redactEmails: false,
redactIps: false
);
}
/**
* Factory: Erstelle Redactor für Testing (Hash-based Redaction)
*/
public static function testing(): self
{
return new self(
mode: RedactionMode::HASH,
redactEmails: false,
redactIps: false
);
}
}