diff --git a/.gitignore b/.gitignore index 621bd21c..bcf2ed52 100644 --- a/.gitignore +++ b/.gitignore @@ -63,5 +63,10 @@ test-results/ # WireGuard client configs (generated locally) deployment/ansible/wireguard-clients/ +# Local development secrets (Docker Secrets) +secrets/*.txt +!secrets/*.example +deployment/ansible/secrets/local.vault.yml + # SSL/TLS certificates **/acme.json diff --git a/Makefile b/Makefile index f6805769..f690696e 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,10 @@ reload: ## Dump Autoload & Restart PHP docker compose $(COMPOSE_FILES) exec php composer dump-autoload -o docker compose $(COMPOSE_FILES) restart php +# Local Development Secrets (via Ansible) +local-secrets: ## Setup Local Development Secrets via Ansible + ansible-playbook -i deployment/ansible/inventory/local.yml deployment/ansible/playbooks/setup-local-secrets.yml + # Staging Environment up-staging: ## Startet Staging-Container docker compose -f $(COMPOSE_BASE) -f $(COMPOSE_STAGING) up -d diff --git a/deployment/ansible/inventory/local.yml b/deployment/ansible/inventory/local.yml index e6dc8918..b3a45831 100644 --- a/deployment/ansible/inventory/local.yml +++ b/deployment/ansible/inventory/local.yml @@ -5,3 +5,7 @@ all: localhost: ansible_connection: local ansible_python_interpreter: "{{ ansible_playbook_python }}" + children: + local: + hosts: + localhost: diff --git a/deployment/ansible/playbooks/setup-local-secrets.yml b/deployment/ansible/playbooks/setup-local-secrets.yml new file mode 100644 index 00000000..9b41fafa --- /dev/null +++ b/deployment/ansible/playbooks/setup-local-secrets.yml @@ -0,0 +1,80 @@ +--- +- name: Setup Local Development Secrets + hosts: local + gather_facts: yes + become: no + connection: local + + vars: + vault_file: "{{ playbook_dir }}/../secrets/local.vault.yml" + + pre_tasks: + - name: Get repository root path + shell: | + cd "{{ playbook_dir }}/../../.." + pwd + register: repo_root + changed_when: false + delegate_to: localhost + become: no + + - name: Set repository root as fact + set_fact: + app_stack_path: "{{ repo_root.stdout }}" + + - name: Verify vault file exists + stat: + path: "{{ vault_file }}" + register: vault_stat + delegate_to: localhost + become: no + + - name: Fail if vault file missing + fail: + msg: "Vault file not found at {{ vault_file }}. Please create it from local.vault.yml.example" + when: not vault_stat.stat.exists + + tasks: + - name: Load encrypted secrets + include_vars: + file: "{{ vault_file }}" + no_log: yes + + - name: Ensure secrets directory exists for Docker Compose secrets + file: + path: "{{ app_stack_path }}/secrets" + state: directory + owner: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + group: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + mode: '0700' + + - name: Create Docker Compose secret files from vault + copy: + content: "{{ item.value }}" + dest: "{{ app_stack_path }}/secrets/{{ item.name }}.txt" + owner: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + group: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + mode: '0600' + loop: + - name: db_user_password + value: "{{ vault_db_password }}" + - name: redis_password + value: "{{ vault_redis_password }}" + - name: app_key + value: "{{ vault_app_key }}" + - name: vault_encryption_key + value: "{{ vault_encryption_key | default(vault_app_key) }}" + no_log: yes + + - name: Set secure permissions on secrets directory + file: + path: "{{ app_stack_path }}/secrets" + state: directory + owner: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + group: "{{ ansible_user_id | default(ansible_env.USER | default('user')) }}" + mode: '0700' + recurse: yes + + - name: Display secrets setup summary + debug: + msg: "? Local secrets created in {{ app_stack_path }}/secrets/" diff --git a/deployment/ansible/secrets/local.vault.yml.example b/deployment/ansible/secrets/local.vault.yml.example new file mode 100644 index 00000000..c7de14bc --- /dev/null +++ b/deployment/ansible/secrets/local.vault.yml.example @@ -0,0 +1,24 @@ +--- +# Local Development Vault Example +# Copy this file to local.vault.yml and encrypt with: +# ansible-vault encrypt local.vault.yml +# +# Or use plain text for local development (not recommended for shared machines): +# ansible-vault encrypt local.vault.yml --vault-password-file ~/.ansible/vault_pass_local.txt +# +# For local development, you can also keep it unencrypted if you prefer: +# cp local.vault.yml.example local.vault.yml +# # Edit local.vault.yml with your local development secrets + +# Database Credentials (Local Development) +vault_db_password: "local-dev-db-password-change-me" + +# Redis Password (Local Development) +vault_redis_password: "local-dev-redis-password-change-me" + +# Application Secrets (Local Development) +# Generate with: php -r "echo 'base64:' . base64_encode(random_bytes(32));" +vault_app_key: "base64:local-dev-app-key-change-me-base64-encoded-32-byte-key" + +# Optional: Encryption Key (defaults to app_key if not set) +vault_encryption_key: "base64:local-dev-encryption-key-change-me" diff --git a/docker-compose.local.yml b/docker-compose.local.yml index caa76b9d..3f09a021 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -2,9 +2,11 @@ # Usage: docker-compose -f docker-compose.base.yml -f docker-compose.local.yml up # # This file overrides base configuration with local development settings: -# - Development ports (8888:80, 8443:443, 5433:5432) +# - Development ports (8888:80, 443:443, 5433:5432) # - Host-mounted volumes for live code editing # - Debug flags enabled (APP_DEBUG, Xdebug) +# - Redis with password (Docker Secrets) +# - Docker Secrets via *_FILE pattern (consistent with staging/production) # - Development-friendly restart policies services: @@ -47,10 +49,18 @@ services: - ./storage/uploads:/var/www/html/storage/uploads:rw - ./storage/analytics:/var/www/html/storage/analytics:rw environment: - PHP_IDE_CONFIG: "${PHP_IDE_CONFIG:-serverName=docker}" - APP_ENV: ${APP_ENV:-development} - APP_DEBUG: ${APP_DEBUG:-true} - XDEBUG_MODE: ${XDEBUG_MODE:-debug} + - PHP_IDE_CONFIG=${PHP_IDE_CONFIG:-serverName=docker} + - APP_ENV=${APP_ENV:-development} + - APP_DEBUG=${APP_DEBUG:-true} + - XDEBUG_MODE=${XDEBUG_MODE:-debug} + # Use Docker Secrets via *_FILE pattern (Framework supports this automatically) + - DB_PASSWORD_FILE=/run/secrets/db_user_password + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + - APP_KEY_FILE=/run/secrets/app_key + secrets: + - db_user_password + - redis_password + - app_key restart: ${RESTART_POLICY:-unless-stopped} # NOTE: env_file not needed - Framework automatically loads .env.base → .env.local # Environment variables are loaded by EncryptedEnvLoader in the PHP application @@ -77,6 +87,29 @@ services: container_name: db ports: - "${DB_EXTERNAL_PORT:-5433}:5432" + # Override environment to remove POSTGRES_PASSWORD (we use Docker Secrets via entrypoint) + environment: + POSTGRES_DB: ${DB_DATABASE:-michaelschiemer} + POSTGRES_USER: ${DB_USERNAME:-postgres} + # POSTGRES_PASSWORD is NOT set here - it's read from Docker Secret in entrypoint + # Performance & Connection Settings + POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" + PGDATA: /var/lib/postgresql/data/pgdata + secrets: + - db_user_password + # Use entrypoint to read password from Docker Secret + # This overrides the base.yml POSTGRES_PASSWORD environment variable + entrypoint: ["/bin/sh", "-c"] + command: + - | + POSTGRES_PASSWORD=$$(cat /run/secrets/db_user_password 2>/dev/null || echo '') + if [ -n "$$POSTGRES_PASSWORD" ]; then + export POSTGRES_PASSWORD + exec /usr/local/bin/docker-entrypoint.sh postgres -c config_file=/etc/postgresql/postgresql.conf + else + echo "⚠️ Warning: db_user_password secret not found, PostgreSQL may fail to start" + exec /usr/local/bin/docker-entrypoint.sh postgres -c config_file=/etc/postgresql/postgresql.conf + fi restart: ${RESTART_POLICY:-unless-stopped} logging: driver: "${LOG_DRIVER:-local}" @@ -95,12 +128,29 @@ services: redis: container_name: redis restart: ${RESTART_POLICY:-unless-stopped} + secrets: + - redis_password + command: > + sh -c "redis-server + --requirepass $$(cat /run/secrets/redis_password) + --maxmemory 256mb + --maxmemory-policy allkeys-lru + --save 900 1 + --save 300 10 + --save 60 10000 + --appendonly yes + --appendfsync everysec" + healthcheck: + test: ["CMD", "sh", "-c", "redis-cli --no-auth-warning -a $$(cat /run/secrets/redis_password) ping"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s logging: driver: "${LOG_DRIVER:-local}" options: max-size: "${LOG_MAX_SIZE:-5m}" max-file: "${LOG_MAX_FILE:-2}" - # NOTE: env_file not needed - Framework automatically loads .env.base → .env.local deploy: resources: limits: @@ -121,6 +171,12 @@ services: - WORKER_DEBUG=${WORKER_DEBUG:-false} - WORKER_SLEEP_TIME=${WORKER_SLEEP_TIME:-100000} - WORKER_MAX_JOBS=${WORKER_MAX_JOBS:-1000} + # Use Docker Secrets via *_FILE pattern (Framework supports this automatically) + - DB_PASSWORD_FILE=/run/secrets/db_user_password + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + secrets: + - db_user_password + - redis_password restart: unless-stopped # NOTE: env_file not needed - Framework automatically loads .env.base → .env.local deploy: @@ -156,3 +212,8 @@ networks: cache: internal: ${NETWORK_CACHE_INTERNAL:-false} +# Docker Secrets Configuration +# Secrets are stored in ./secrets/ directory (relative to this file) +# Note: Secrets are already defined in docker-compose.base.yml, but we activate them here +# for local development. The base.yml defines the secret sources. + diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 25c0cfbe..ff36ca9b 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -75,6 +75,7 @@ COPY docker/php/php.${ENV}.ini /usr/local/etc/php/php.ini # Kopiere PHP-FPM Pool-Konfiguration COPY docker/php/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf +COPY docker/php/zzzz-override.conf /usr/local/etc/php-fpm.d/zzzz-override.conf # Xdebug-Konfiguration nur wenn dev RUN if [ "$ENV" = "dev" ] && [ -f docker/php/xdebug.ini ]; then \ diff --git a/docker/php/zz-docker.conf b/docker/php/zz-docker.conf index 78913b2a..0ad7432c 100644 --- a/docker/php/zz-docker.conf +++ b/docker/php/zz-docker.conf @@ -8,7 +8,8 @@ user = appuser group = appuser ; The address on which to accept FastCGI requests. -listen = 9000 +; Use 0.0.0.0:9000 to listen on all interfaces (required for Docker networking) +listen = 0.0.0.0:9000 ; Clear environment in FPM workers clear_env = no diff --git a/docker/php/zzzz-override.conf b/docker/php/zzzz-override.conf new file mode 100644 index 00000000..876c9ac1 --- /dev/null +++ b/docker/php/zzzz-override.conf @@ -0,0 +1,4 @@ +[www] +; Override listen address to listen on all interfaces (required for Docker networking) +; This file is loaded last (alphabetically) to override www.conf +listen = 0.0.0.0:9000 diff --git a/docs/deployment/local-secrets-unification-plan.md b/docs/deployment/local-secrets-unification-plan.md new file mode 100644 index 00000000..f7816192 --- /dev/null +++ b/docs/deployment/local-secrets-unification-plan.md @@ -0,0 +1,470 @@ +# Plan: Vereinheitlichung der lokalen Entwicklungsumgebung mit Staging/Production + +**Datum**: 2025-11-04 +**Ziel**: Lokale Entwicklungsumgebung auf Docker Secrets und Redis-Passwort umstellen, um Konsistenz mit Staging/Production zu erreichen + +## Aktuelle Situation + +### Lokal (docker-compose.local.yml) +- ? **Redis**: Kein Passwort (nur redis.conf) +- ? **Docker Secrets**: Nicht verwendet +- ? ***_FILE Pattern**: Nicht verwendet +- ? **Secrets**: Direkt in `.env.local` (plain text) + +### Staging/Production +- ? **Redis**: Mit Passwort via Docker Secrets +- ? **Docker Secrets**: Aktiviert +- ? ***_FILE Pattern**: F?r alle Secrets +- ? **Framework**: `DockerSecretsResolver` unterst?tzt automatisch `*_FILE` Pattern + +## Vorteile der Vereinheitlichung + +### 1. Konsistenz +- ? **Gleiche Konfiguration** in allen Umgebungen +- ? **Weniger ?berraschungen** bei Deployment +- ? **Einfachere Fehlersuche** (gleiche Konfiguration = gleiche Probleme) + +### 2. Framework-Support +- ? **Framework unterst?tzt bereits `DockerSecretsResolver`** +- ? **Automatisches Laden** von `*_FILE` Pattern +- ? **Kein Code-Change n?tig** - nur Konfiguration + +### 3. Sicherheit +- ? **Bessere Praxis** auch in Entwicklung +- ? **Secrets nicht in `.env.local`** (kann versehentlich committed werden) +- ? **Dateisystem-Permissions** f?r Secrets + +### 4. Entwickler-Erfahrung +- ? **Testen mit Production-?hnlicher Konfiguration** +- ? **Fr?hes Erkennen** von Secrets-Problemen +- ? **Einheitliches Setup** f?r alle Entwickler + +## Nachteile / ?berlegungen + +### 1. Komplexit?t +- ?? **Mehr Setup-Schritte** f?r neue Entwickler +- ?? **Zus?tzliche Secrets-Dateien** m?ssen erstellt werden +- ?? **Lokale Secrets-Verwaltung** n?tig + +### 2. Migration +- ?? **Bestehende `.env.local`** muss angepasst werden +- ?? **Secrets-Verzeichnis** muss erstellt werden +- ?? **Dokumentation** muss aktualisiert werden + +## Implementierungsplan + +### Phase 1: Secrets-Verzeichnis und Struktur + +#### 1.1 Secrets-Verzeichnis erstellen +```bash +mkdir -p secrets +chmod 700 secrets +``` + +#### 1.2 .gitignore aktualisieren +```gitignore +# Secrets (lokal) +secrets/*.txt +!secrets/*.example +``` + +#### 1.3 Beispiel-Secrets erstellen +```bash +# secrets/redis_password.txt.example +# Beispiel: local-dev-redis-password-2024 + +# secrets/db_user_password.txt.example +# Beispiel: local-dev-db-password-2024 + +# secrets/app_key.txt.example +# Beispiel: base64:local-dev-app-key-generated +``` + +### Phase 2: Docker Compose Konfiguration + +#### 2.1 docker-compose.local.yml aktualisieren + +**Redis Service:** +```yaml +redis: + secrets: + - redis_password + command: > + sh -c "redis-server + --requirepass $$(cat /run/secrets/redis_password) + --maxmemory 256mb + --maxmemory-policy allkeys-lru + --save 900 1 + --save 300 10 + --save 60 10000 + --appendonly yes + --appendfsync everysec" + healthcheck: + test: ["CMD", "sh", "-c", "redis-cli --no-auth-warning -a $$(cat /run/secrets/redis_password) ping"] +``` + +**PHP Service:** +```yaml +php: + environment: + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + - DB_PASSWORD_FILE=/run/secrets/db_user_password + - APP_KEY_FILE=/run/secrets/app_key + secrets: + - redis_password + - db_user_password + - app_key +``` + +**Queue-Worker Service:** +```yaml +queue-worker: + environment: + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + - DB_PASSWORD_FILE=/run/secrets/db_user_password + secrets: + - redis_password + - db_user_password +``` + +#### 2.2 docker-compose.base.yml Secrets erweitern + +**Secrets-Sektion:** +```yaml +secrets: + db_user_password: + file: ./secrets/db_user_password.txt + external: false + redis_password: + file: ./secrets/redis_password.txt + external: false + app_key: + file: ./secrets/app_key.txt + external: false +``` + +**Hinweis**: `external: false` bedeutet, dass die Dateien lokal sein m?ssen (nicht external wie in Production). + +### Phase 3: Redis-Konfiguration + +#### 3.1 redis.conf anpassen +**Option A**: redis.conf entfernt (nur Command-Line-Args) +**Option B**: redis.conf mit requirepass (aus Secret geladen) + +**Empfehlung**: Option A - Command-Line-Args sind flexibler und konsistenter mit Staging/Production. + +#### 3.2 redis.conf entfernen oder anpassen +```ini +# docker/redis/redis.conf +# Hinweis: Passwort wird via Command-Line-Args gesetzt (aus Docker Secret) +# requirepass wird nicht hier gesetzt, sondern via --requirepass Argument +``` + +### Phase 4: Migration bestehender .env.local + +#### 4.1 Secrets aus .env.local extrahieren +```bash +# Script: scripts/migrate-env-to-secrets.sh +#!/bin/bash + +SECRETS_DIR="./secrets" +mkdir -p "$SECRETS_DIR" + +# Redis Password +if grep -q "REDIS_PASSWORD=" .env.local; then + grep "REDIS_PASSWORD=" .env.local | cut -d'=' -f2 > "$SECRETS_DIR/redis_password.txt" + chmod 600 "$SECRETS_DIR/redis_password.txt" + echo "? Created secrets/redis_password.txt" + # Entferne aus .env.local (kommentiere aus oder entferne) + sed -i '/^REDIS_PASSWORD=/d' .env.local +fi + +# DB Password +if grep -q "DB_PASSWORD=" .env.local; then + grep "DB_PASSWORD=" .env.local | cut -d'=' -f2 > "$SECRETS_DIR/db_user_password.txt" + chmod 600 "$SECRETS_DIR/db_user_password.txt" + echo "? Created secrets/db_user_password.txt" + sed -i '/^DB_PASSWORD=/d' .env.local +fi + +# APP_KEY +if grep -q "APP_KEY=" .env.local; then + grep "APP_KEY=" .env.local | cut -d'=' -f2 > "$SECRETS_DIR/app_key.txt" + chmod 600 "$SECRETS_DIR/app_key.txt" + echo "? Created secrets/app_key.txt" + sed -i '/^APP_KEY=/d' .env.local +fi + +echo "? Migration completed. Please review .env.local and remove migrated secrets." +``` + +#### 4.2 .env.local bereinigen +- Entferne: `REDIS_PASSWORD=`, `DB_PASSWORD=`, `APP_KEY=` +- Behalte: Alle anderen Konfigurationen (DB_HOST, DB_PORT, etc.) + +### Phase 5: Dokumentation + +#### 5.1 ENV_SETUP.md aktualisieren +- Lokales Setup mit Secrets dokumentieren +- Migration-Anleitung hinzuf?gen +- Beispiel-Secrets erw?hnen + +#### 5.2 README.md aktualisieren +- Setup-Schritte f?r neue Entwickler +- Secrets-Verzeichnis erstellen +- Beispiel-Secrets kopieren + +#### 5.3 .env.example aktualisieren +- Kommentare zu `*_FILE` Pattern hinzuf?gen +- Hinweis auf Secrets-Verzeichnis + +## Detaillierte ?nderungen + +### Datei: docker-compose.local.yml + +```yaml +services: + php: + environment: + # Use Docker Secrets via *_FILE pattern (Framework supports this automatically) + - DB_PASSWORD_FILE=/run/secrets/db_user_password + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + - APP_KEY_FILE=/run/secrets/app_key + secrets: + - db_user_password + - redis_password + - app_key + + redis: + secrets: + - redis_password + command: > + sh -c "redis-server + --requirepass $$(cat /run/secrets/redis_password) + --maxmemory 256mb + --maxmemory-policy allkeys-lru + --save 900 1 + --save 300 10 + --save 60 10000 + --appendonly yes + --appendfsync everysec" + healthcheck: + test: ["CMD", "sh", "-c", "redis-cli --no-auth-warning -a $$(cat /run/secrets/redis_password) ping"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + queue-worker: + environment: + - DB_PASSWORD_FILE=/run/secrets/db_user_password + - REDIS_PASSWORD_FILE=/run/secrets/redis_password + secrets: + - db_user_password + - redis_password + +secrets: + db_user_password: + file: ./secrets/db_user_password.txt + external: false + redis_password: + file: ./secrets/redis_password.txt + external: false + app_key: + file: ./secrets/app_key.txt + external: false +``` + +### Datei: docker-compose.base.yml + +**Secrets-Sektion bereits vorhanden** (Zeilen 192-224), muss nur genutzt werden. + +### Datei: docker/redis/redis.conf + +**Anpassen oder entfernen:** +```ini +# docker/redis/redis.conf +# Hinweis: Passwort wird via Docker Secrets geladen +# requirepass wird nicht hier gesetzt, sondern via Command-Line-Args +# In docker-compose.local.yml wird --requirepass verwendet +``` + +## Migration f?r bestehende Entwickler + +### Schritt-f?r-Schritt Anleitung + +```bash +# 1. Secrets-Verzeichnis erstellen +mkdir -p secrets +chmod 700 secrets + +# 2. Secrets aus .env.local extrahieren +# Redis Password +REDIS_PASS=$(grep "^REDIS_PASSWORD=" .env.local | cut -d'=' -f2) +if [ -n "$REDIS_PASS" ]; then + echo "$REDIS_PASS" > secrets/redis_password.txt + chmod 600 secrets/redis_password.txt + echo "? Created secrets/redis_password.txt" +fi + +# DB Password +DB_PASS=$(grep "^DB_PASSWORD=" .env.local | cut -d'=' -f2) +if [ -n "$DB_PASS" ]; then + echo "$DB_PASS" > secrets/db_user_password.txt + chmod 600 secrets/db_user_password.txt + echo "? Created secrets/db_user_password.txt" +fi + +# APP_KEY +APP_KEY=$(grep "^APP_KEY=" .env.local | cut -d'=' -f2) +if [ -n "$APP_KEY" ]; then + echo "$APP_KEY" > secrets/app_key.txt + chmod 600 secrets/app_key.txt + echo "? Created secrets/app_key.txt" +fi + +# 3. Entferne Secrets aus .env.local (oder kommentiere aus) +sed -i '/^REDIS_PASSWORD=/d' .env.local +sed -i '/^DB_PASSWORD=/d' .env.local +sed -i '/^APP_KEY=/d' .env.local + +# 4. Pr?fe Secrets +ls -la secrets/ +# Sollte zeigen: redis_password.txt, db_user_password.txt, app_key.txt + +# 5. Teste Konfiguration +docker compose -f docker-compose.base.yml -f docker-compose.local.yml config + +# 6. Starte Container +docker compose -f docker-compose.base.yml -f docker-compose.local.yml up -d + +# 7. Teste Redis-Verbindung +docker compose exec redis redis-cli --no-auth-warning -a $(cat secrets/redis_password.txt) ping +# Erwartet: PONG +``` + +## Setup f?r neue Entwickler + +### Initial Setup Script + +```bash +#!/bin/bash +# scripts/setup-local-secrets.sh + +echo "?? Setting up local development secrets..." + +SECRETS_DIR="./secrets" +mkdir -p "$SECRETS_DIR" +chmod 700 "$SECRETS_DIR" + +# Generate Redis Password +if [ ! -f "$SECRETS_DIR/redis_password.txt" ]; then + REDIS_PASS=$(openssl rand -base64 32) + echo "$REDIS_PASS" > "$SECRETS_DIR/redis_password.txt" + chmod 600 "$SECRETS_DIR/redis_password.txt" + echo "? Generated secrets/redis_password.txt" +fi + +# Generate DB Password +if [ ! -f "$SECRETS_DIR/db_user_password.txt" ]; then + DB_PASS=$(openssl rand -base64 32) + echo "$DB_PASS" > "$SECRETS_DIR/db_user_password.txt" + chmod 600 "$SECRETS_DIR/db_user_password.txt" + echo "? Generated secrets/db_user_password.txt" + echo "?? Update DB_PASSWORD in .env.local if needed" +fi + +# Generate APP_KEY (if not exists) +if [ ! -f "$SECRETS_DIR/app_key.txt" ]; then + APP_KEY=$(php -r "echo 'base64:' . base64_encode(random_bytes(32));") + echo "$APP_KEY" > "$SECRETS_DIR/app_key.txt" + chmod 600 "$SECRETS_DIR/app_key.txt" + echo "? Generated secrets/app_key.txt" +fi + +echo "? Local secrets setup completed!" +echo "" +echo "?? Next steps:" +echo "1. Review secrets/*.txt files" +echo "2. Update .env.local if needed (DB_USERNAME, DB_HOST, etc.)" +echo "3. Start containers: docker compose -f docker-compose.base.yml -f docker-compose.local.yml up -d" +``` + +## Vorteile dieser L?sung + +### 1. Konsistenz +- ? Gleiche Konfiguration in Local, Staging, Production +- ? Framework nutzt `DockerSecretsResolver` ?berall +- ? Keine Environment-spezifischen Code-Pfade + +### 2. Sicherheit +- ? Secrets nicht in `.env.local` (kann versehentlich committed werden) +- ? Dateisystem-Permissions (600) f?r Secrets +- ? `.gitignore` verhindert versehentliches Committen + +### 3. Wartbarkeit +- ? Einheitliche Konfiguration +- ? Framework unterst?tzt `*_FILE` Pattern automatisch +- ? Klare Trennung: Secrets vs. Konfiguration + +### 4. Entwickler-Erfahrung +- ? Testen mit Production-?hnlicher Konfiguration +- ? Fr?hes Erkennen von Secrets-Problemen +- ? Setup-Script f?r neue Entwickler + +## Nachteile / ?berlegungen + +### 1. Komplexit?t +- ?? **Mehr Setup-Schritte** f?r neue Entwickler +- ?? **Zus?tzliche Secrets-Dateien** m?ssen verwaltet werden +- ?? **Lokale Secrets-Verwaltung** n?tig + +**L?sung**: Setup-Script automatisiert die Erstellung + +### 2. Migration +- ?? **Bestehende `.env.local`** muss angepasst werden +- ?? **Secrets-Verzeichnis** muss erstellt werden +- ?? **Dokumentation** muss aktualisiert werden + +**L?sung**: Migration-Script unterst?tzt den ?bergang + +### 3. Redis-Passwort lokal +- ?? **Mehr Konfiguration** f?r lokale Entwicklung +- ?? **Passwort muss bekannt sein** f?r Redis-CLI-Zugriff + +**L?sung**: +- Passwort in `secrets/redis_password.txt` (leicht zug?nglich lokal) +- Helper-Script: `scripts/redis-cli.sh` f?r einfachen Zugriff + +## Alternative Ans?tze + +### Option A: Secrets optional machen (nicht empfohlen) +- Redis-Passwort nur wenn Secret vorhanden +- Komplexer, weniger konsistent + +### Option B: Nur Production/Staging mit Secrets (aktuell) +- Lokal bleibt ohne Secrets +- **Nachteil**: Inkonsistenz zwischen Umgebungen + +### Option C: Vereinheitlichung (empfohlen) +- Alle Umgebungen nutzen Secrets +- **Vorteil**: Maximale Konsistenz + +## Empfehlung + +**Vereinheitlichung (Option C)** ist die beste L?sung, weil: +1. ? **Maximale Konsistenz** zwischen allen Umgebungen +2. ? **Framework unterst?tzt bereits** `DockerSecretsResolver` +3. ? **Bessere Sicherheit** auch lokal +4. ? **Fr?hes Erkennen** von Secrets-Problemen +5. ? **Setup-Script** macht Migration einfach + +## N?chste Schritte + +1. ? Plan erstellt +2. ? Diskussion: Ist Vereinheitlichung gew?nscht? +3. ? Implementierung: docker-compose.local.yml anpassen +4. ? Migration: Secrets aus .env.local extrahieren +5. ? Dokumentation: ENV_SETUP.md und README.md aktualisieren +6. ? Testing: Lokale Entwicklungsumgebung testen +7. ? Setup-Script: scripts/setup-local-secrets.sh erstellen diff --git a/docs/deployment/local-stack-restructure-plan.md b/docs/deployment/local-stack-restructure-plan.md new file mode 100644 index 00000000..72a67da5 --- /dev/null +++ b/docs/deployment/local-stack-restructure-plan.md @@ -0,0 +1,359 @@ +# Plan: Umstrukturierung der lokalen Entwicklungsumgebung nach Stack-Pattern + +**Datum**: 2025-11-04 +**Ziel**: Lokale Entwicklungsumgebung in `deployment/stacks/local/` strukturieren, konsistent mit `application` und `staging` Stacks + +## Aktuelle Struktur + +### Root-Level (aktuell) +``` +Repository Root/ +??? docker-compose.base.yml # Gemeinsame Basis +??? docker-compose.local.yml # Local Override +??? docker-compose.staging.yml # Staging Override (im Root) +??? docker-compose.production.yml # Production Override (im Root) +??? secrets/ # (neu, f?r lokale Secrets) +``` + +### Stacks-Struktur (aktuell) +``` +deployment/stacks/ +??? application/ # Production Stack +? ??? docker-compose.yml # Vollst?ndige Definition +? ??? README.md +??? staging/ # Staging Stack (nur nginx config) +? ??? nginx/conf.d/ +? ??? README.md +??? postgresql/ # PostgreSQL Stack + ??? docker-compose.yml +``` + +## Zielstruktur + +### Root-Level (nach Migration) +``` +Repository Root/ +??? docker-compose.base.yml # Gemeinsame Basis (bleibt) +??? docker-compose.staging.yml # Staging Override (bleibt im Root) +??? docker-compose.production.yml # Production Override (bleibt im Root) +??? docker-compose.postgres-override.yml # (bleibt) +``` + +### Stacks-Struktur (nach Migration) +``` +deployment/stacks/ +??? application/ # Production Stack +? ??? docker-compose.yml +? ??? README.md +??? staging/ # Staging Stack +? ??? nginx/conf.d/ +? ??? README.md +??? local/ # Local Development Stack (NEU) +? ??? docker-compose.yml # Vollst?ndige Local-Konfiguration +? ??? secrets/ # Lokale Secrets +? ? ??? redis_password.txt.example +? ? ??? db_user_password.txt.example +? ? ??? app_key.txt.example +? ??? scripts/ # Helper-Scripts +? ? ??? setup-secrets.sh +? ? ??? migrate-env-to-secrets.sh +? ??? README.md # Local Development Dokumentation +??? postgresql/ + ??? docker-compose.yml +``` + +## Vorteile der Umstrukturierung + +### 1. Konsistenz +- ? **Gleiche Struktur** wie `application` und andere Stacks +- ? **Klare Trennung** der Umgebungen +- ? **Einheitliches Pattern** f?r alle Stacks + +### 2. Organisation +- ? **Lokale Konfiguration** klar als "Stack" erkennbar +- ? **Secrets lokal** verwaltet (nicht im Root) +- ? **Helper-Scripts** am richtigen Ort + +### 3. Wartbarkeit +- ? **Einheitliche Struktur** erleichtert Wartung +- ? **Klare Verantwortlichkeiten** pro Stack +- ? **Einfachere Navigation** f?r Entwickler + +### 4. Deployment-Konsistenz +- ? **Gleiche Kommandos** f?r alle Stacks +- ? **Einheitliche Dokumentation** +- ? **Konsistente Pfade** + +## Implementierungsplan + +### Phase 1: Verzeichnisstruktur erstellen + +```bash +mkdir -p deployment/stacks/local/secrets +mkdir -p deployment/stacks/local/scripts +chmod 700 deployment/stacks/local/secrets +``` + +### Phase 2: docker-compose.yml migrieren + +**Von**: `docker-compose.base.yml + docker-compose.local.yml` (Root) +**Nach**: `deployment/stacks/local/docker-compose.yml` (vollst?ndige Definition) + +**Struktur**: +```yaml +# deployment/stacks/local/docker-compose.yml +# Local Development Stack +# Usage: docker compose -f deployment/stacks/local/docker-compose.yml up + +services: + web: + # ... (aus base.yml + local.yml kombiniert) + + php: + # ... mit Secrets und *_FILE Pattern + + redis: + # ... mit Passwort (Docker Secrets) + + # ... weitere Services + +secrets: + # ... lokale Secrets + +networks: + # ... lokale Netzwerke +``` + +### Phase 3: Secrets-Verwaltung + +**Struktur**: +``` +deployment/stacks/local/ +??? secrets/ +? ??? redis_password.txt.example # Beispiel +? ??? db_user_password.txt.example # Beispiel +? ??? app_key.txt.example # Beispiel +??? .gitignore # secrets/*.txt +``` + +**Secrets-Pfade in docker-compose.yml**: +```yaml +secrets: + redis_password: + file: ./secrets/redis_password.txt + external: false +``` + +### Phase 4: Helper-Scripts + +**scripts/setup-secrets.sh**: +```bash +#!/bin/bash +# Setup local development secrets +cd "$(dirname "$0")/.." # Zu deployment/stacks/local/ +# ... Secrets generieren +``` + +**scripts/migrate-env-to-secrets.sh**: +```bash +#!/bin/bash +# Migriert Secrets aus .env.local zu secrets/ +cd "$(dirname "$0")/.." # Zu deployment/stacks/local/ +# ... Migration durchf?hren +``` + +### Phase 5: Dokumentation + +**deployment/stacks/local/README.md**: +- Setup-Anleitung +- Secrets-Verwaltung +- Verwendung +- Troubleshooting + +## Vergleich der Ans?tze + +### Ansatz A: Vollst?ndige docker-compose.yml (empfohlen) + +**Struktur**: +```yaml +# deployment/stacks/local/docker-compose.yml +# Vollst?ndige Definition (wie application Stack) +services: + web: ... + php: ... + redis: ... +``` + +**Vorteile**: +- ? Konsistent mit `application` Stack +- ? Vollst?ndige Kontrolle +- ? Keine Abh?ngigkeit von Root-Level base.yml + +**Nachteile**: +- ?? Etwas mehr Code-Duplikation (aber klar getrennt) + +### Ansatz B: Base + Override Pattern (wie Staging) + +**Struktur**: +```yaml +# deployment/stacks/local/docker-compose.base.yml (oder Link) +# deployment/stacks/local/docker-compose.local.yml +``` + +**Vorteile**: +- ? Weniger Duplikation +- ? Konsistent mit Staging-Pattern + +**Nachteile**: +- ?? Pfad-Abh?ngigkeiten (base.yml muss relativ erreichbar sein) +- ?? Komplexer bei verschiedenen Pfaden + +## Empfehlung: Ansatz A (Vollst?ndige Definition) + +**Begr?ndung**: +1. **Konsistenz**: Gleiche Struktur wie `application` Stack +2. **Autonomie**: Local Stack ist vollst?ndig unabh?ngig +3. **Klarheit**: Alles in einem Verzeichnis +4. **Einfachheit**: Keine Pfad-Abh?ngigkeiten + +## Detaillierte Struktur + +### deployment/stacks/local/docker-compose.yml + +**Enth?lt**: +- Alle Services aus `docker-compose.base.yml` + `docker-compose.local.yml` +- Redis mit Passwort (Docker Secrets) +- `*_FILE` Pattern f?r alle Secrets +- Lokale Ports (8888:80, 443:443, 5433:5432) +- Host-Mounts f?r Entwicklung +- Debug-Flags + +**Services**: +- `web` (Nginx) +- `php` (PHP-FPM) +- `db` (PostgreSQL) +- `redis` (mit Passwort) +- `queue-worker` +- `minio` (optional) + +### deployment/stacks/local/secrets/ + +**Dateien**: +- `redis_password.txt` (gitignored) +- `db_user_password.txt` (gitignored) +- `app_key.txt` (gitignored) +- `*.example` (versioniert, als Beispiele) + +### deployment/stacks/local/scripts/ + +**Scripts**: +- `setup-secrets.sh` - Generiert Secrets f?r neue Entwickler +- `migrate-env-to-secrets.sh` - Migriert bestehende .env.local +- `redis-cli.sh` - Helper f?r Redis-CLI mit Passwort + +## Migration + +### Schritt 1: Neue Struktur erstellen +```bash +mkdir -p deployment/stacks/local/{secrets,scripts} +``` + +### Schritt 2: docker-compose.yml erstellen +- Kombiniere `docker-compose.base.yml` + `docker-compose.local.yml` +- F?ge Secrets-Konfiguration hinzu +- F?ge Redis-Passwort hinzu + +### Schritt 3: Secrets migrieren +- Erstelle `secrets/` Verzeichnis +- Generiere oder migriere Secrets +- Erstelle Beispiel-Dateien + +### Schritt 4: Scripts erstellen +- Setup-Script +- Migration-Script +- Helper-Scripts + +### Schritt 5: Dokumentation +- README.md erstellen +- ENV_SETUP.md aktualisieren +- Haupt-README aktualisieren + +### Schritt 6: Alte Dateien entfernen +- `docker-compose.local.yml` im Root (nach Migration) +- Root-Level `secrets/` (falls vorhanden) + +## Verwendung nach Migration + +### Vorher (aktuell) +```bash +docker compose -f docker-compose.base.yml -f docker-compose.local.yml up +``` + +### Nachher (neu) +```bash +cd deployment/stacks/local +docker compose up -d +``` + +**Oder vom Root**: +```bash +docker compose -f deployment/stacks/local/docker-compose.yml up -d +``` + +## Vorteile dieser Struktur + +1. ? **Konsistenz**: Gleiche Struktur wie `application` Stack +2. ? **Autonomie**: Local Stack ist vollst?ndig unabh?ngig +3. ? **Organisation**: Alles Local-Development in einem Verzeichnis +4. ? **Klarheit**: Klare Trennung der Umgebungen +5. ? **Wartbarkeit**: Einfacher zu warten und zu erweitern + +## Nachteile / ?berlegungen + +### 1. Pfad-Abh?ngigkeiten +- ?? **Build-Kontexte**: M?ssen relativ zum Repository-Root sein +- **L?sung**: Absolute Pfade oder `context: ../..` verwenden + +### 2. Migration +- ?? **Bestehende Entwickler** m?ssen umstellen +- **L?sung**: Migration-Script und Dokumentation + +### 3. Docker Compose Befehle +- ?? **Andere Pfade** als bisher +- **L?sung**: Helper-Script oder Makefile-Target + +## Alternative: Makefile-Targets + +**Makefile hinzuf?gen**: +```makefile +local-up: + cd deployment/stacks/local && docker compose up -d + +local-down: + cd deployment/stacks/local && docker compose down + +local-logs: + cd deployment/stacks/local && docker compose logs -f + +local-ps: + cd deployment/stacks/local && docker compose ps +``` + +**Verwendung**: +```bash +make local-up +make local-logs +make local-ps +``` + +## N?chste Schritte + +1. ? Plan erstellt +2. ? Diskussion: Ist Stack-Struktur gew?nscht? +3. ? Implementierung: Verzeichnisstruktur erstellen +4. ? docker-compose.yml migrieren und anpassen +5. ? Secrets-System implementieren +6. ? Scripts erstellen +7. ? Dokumentation +8. ? Testing +9. ? Alte Dateien entfernen diff --git a/secrets/app_key.txt.example b/secrets/app_key.txt.example new file mode 100755 index 00000000..452cd078 --- /dev/null +++ b/secrets/app_key.txt.example @@ -0,0 +1,8 @@ +# Application Key Example +# Copy this file to app_key.txt and set your local development application key +# Example: base64:local-dev-app-key-generated +# +# Generate application key: +# php -r "echo 'base64:' . base64_encode(random_bytes(32));" +# +# Or use existing APP_KEY from .env.local diff --git a/secrets/db_user_password.txt.example b/secrets/db_user_password.txt.example new file mode 100755 index 00000000..f20ffc7d --- /dev/null +++ b/secrets/db_user_password.txt.example @@ -0,0 +1,8 @@ +# Database User Password Example +# Copy this file to db_user_password.txt and set your local development password +# Example: local-dev-db-password-2024 +# +# Generate secure password: +# openssl rand -base64 32 +# +# Note: This password should match DB_PASSWORD in your .env.local (if you still use it during migration) diff --git a/secrets/redis_password.txt.example b/secrets/redis_password.txt.example new file mode 100755 index 00000000..90872afd --- /dev/null +++ b/secrets/redis_password.txt.example @@ -0,0 +1,6 @@ +# Redis Password Example +# Copy this file to redis_password.txt and set your local development password +# Example: local-dev-redis-password-2024 +# +# Generate secure password: +# openssl rand -base64 32 diff --git a/src/Framework/Logging/LoggerInitializer.php b/src/Framework/Logging/LoggerInitializer.php index ffdff9ba..08a329d9 100644 --- a/src/Framework/Logging/LoggerInitializer.php +++ b/src/Framework/Logging/LoggerInitializer.php @@ -154,10 +154,7 @@ final readonly class LoggerInitializer $handlers[] = $this->createCliHandler($config, $env, $minLevel); } else { // Web-Requests: Console Handler auf stderr - $webFormatter = new LineFormatter( - format: '[{timestamp}] [{level_name}] {request_id}{channel}{message}', - timestampFormat: 'Y-m-d H:i:s' - ); + $webFormatter = new LineFormatter(); $handlers[] = new ConsoleHandler($webFormatter, $minLevel, debugOnly: false); }