From a2ffb1e076284dc1964785267da9e425865d415b Mon Sep 17 00:00:00 2001 From: StefanMoCoAt Date: Mon, 6 Oct 2025 01:07:16 +0200 Subject: [PATCH] feature Keycloak Auth --- .env.template | 207 +++++++ DOCKER-OPTIMIZATION.md | 427 ++++++++++++++ docker-compose.clients.yml | 130 ++--- docker-compose.clients.yml.optimized | 353 ++++++++++++ docker-compose.services.yml.optimized | 430 +++++++++++++++ docker-compose.yml.optimized | 522 ++++++++++++++++++ docker/secrets/grafana_admin_password.txt | 1 + docker/secrets/grafana_admin_user.txt | 1 + docker/secrets/jwt_secret.txt | 1 + docker/secrets/keycloak_admin_password.txt | 1 + .../secrets/keycloak_auth_client_secret.txt | 1 + docker/secrets/keycloak_client_secret.txt | 1 + docker/secrets/metrics_auth_password.txt | 1 + docker/secrets/metrics_auth_username.txt | 1 + docker/secrets/postgres_password.txt | 1 + docker/secrets/postgres_user.txt | 1 + docker/secrets/redis_password.txt | 1 + docker/secrets/setup-secrets.sh | 345 ++++++++++++ docker/secrets/vnc_password.txt | 1 + .../keycloak/meldestelle-realm.json.backup | 294 ++++++++++ logs/gateway.log | 27 + logs/troubleshooting/compose-config.txt | 371 +++++++++++++ .../logs/troubleshooting/compose-config.txt | 2 + .../service/config/SecurityConfiguration.kt | 34 ++ 24 files changed, 3089 insertions(+), 65 deletions(-) create mode 100644 .env.template create mode 100644 DOCKER-OPTIMIZATION.md create mode 100644 docker-compose.clients.yml.optimized create mode 100644 docker-compose.services.yml.optimized create mode 100644 docker-compose.yml.optimized create mode 100644 docker/secrets/grafana_admin_password.txt create mode 100644 docker/secrets/grafana_admin_user.txt create mode 100644 docker/secrets/jwt_secret.txt create mode 100644 docker/secrets/keycloak_admin_password.txt create mode 100644 docker/secrets/keycloak_auth_client_secret.txt create mode 100644 docker/secrets/keycloak_client_secret.txt create mode 100644 docker/secrets/metrics_auth_password.txt create mode 100644 docker/secrets/metrics_auth_username.txt create mode 100644 docker/secrets/postgres_password.txt create mode 100644 docker/secrets/postgres_user.txt create mode 100644 docker/secrets/redis_password.txt create mode 100755 docker/secrets/setup-secrets.sh create mode 100644 docker/secrets/vnc_password.txt create mode 100644 docker/services/keycloak/meldestelle-realm.json.backup create mode 100644 logs/gateway.log create mode 100644 logs/troubleshooting/compose-config.txt create mode 100644 scripts/troubleshooting/logs/troubleshooting/compose-config.txt create mode 100644 services/members/members-service/src/main/kotlin/at/mocode/members/service/config/SecurityConfiguration.kt diff --git a/.env.template b/.env.template new file mode 100644 index 00000000..72a1821b --- /dev/null +++ b/.env.template @@ -0,0 +1,207 @@ +# =================================================================== +# Environment Configuration Template - Meldestelle Project +# =================================================================== +# Copy this file to .env and customize the values for your environment +# Security Note: Never commit .env files containing production secrets! +# =================================================================== + +# =================================================================== +# Build Configuration +# =================================================================== +# Docker image versions +DOCKER_GRADLE_VERSION=9.0.0 +DOCKER_JAVA_VERSION=21 +DOCKER_KEYCLOAK_VERSION=26.4.0 +DOCKER_PROMETHEUS_VERSION=v2.54.1 +DOCKER_GRAFANA_VERSION=11.3.0 + +# Application version +DOCKER_APP_VERSION=1.0.0 +APP_VERSION=1.0.0 +APP_NAME=Meldestelle + +# Build metadata +BUILD_DATE= +# BUILD_DATE will be auto-generated if not set + +# Spring profiles for services +SPRING_PROFILES_ACTIVE=docker,keycloak +DOCKER_SPRING_PROFILES_DEFAULT=default +DOCKER_SPRING_PROFILES_DOCKER=docker + +# =================================================================== +# Infrastructure Services - Port Configuration +# =================================================================== +# Database +POSTGRES_DB=meldestelle +# Note: Username and password are now managed via Docker secrets + +# Redis Cache +REDIS_PORT=6379 + +# Keycloak Authentication +KEYCLOAK_PORT=8180 +KEYCLOAK_LOG_LEVEL=INFO + +# Service Discovery +CONSUL_HOST=consul +CONSUL_PORT=8500 +CONSUL_ENABLED=true + +# Messaging +ZOOKEEPER_CLIENT_PORT=2181 +KAFKA_PORT=9092 +KAFKA_BROKER_ID=1 +KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 + +# Monitoring +PROMETHEUS_PORT=9090 +GRAFANA_PORT=3000 + +# =================================================================== +# Application Services - Port Configuration +# =================================================================== +# API Gateway +GATEWAY_HOST=api-gateway +GATEWAY_PORT=8081 + +# Microservices +PING_SERVICE_PORT=8082 +MEMBERS_SERVICE_PORT=8083 +HORSES_SERVICE_PORT=8084 +EVENTS_SERVICE_PORT=8085 +MASTERDATA_SERVICE_PORT=8086 +AUTH_SERVICE_PORT=8087 +MONITORING_SERVER_PORT=8088 + +# =================================================================== +# Client Applications - Port Configuration +# =================================================================== +# Web Application +WEB_APP_PORT=4000 +WEB_APP_DOMAIN=localhost +NODE_ENV=production + +# Nginx Configuration +NGINX_WORKER_PROCESSES=auto +NGINX_WORKER_CONNECTIONS=1024 + +# Desktop Application +DESKTOP_VNC_WEB_PORT=6080 +DESKTOP_VNC_PORT=5901 +DESKTOP_APP_DOMAIN=localhost + +# =================================================================== +# Security Configuration +# =================================================================== +# JWT Configuration +JWT_ISSUER=meldestelle-auth-server +JWT_AUDIENCE=meldestelle-services + +# Note: JWT_SECRET is now managed via Docker secrets +# Generate with: openssl rand -hex 32 + +# Keycloak Configuration +KEYCLOAK_REALM=meldestelle +KEYCLOAK_CLIENT_ID=api-gateway + +# Note: All passwords and secrets are now managed via Docker secrets +# Run: ./docker/secrets/setup-secrets.sh to generate secure secrets + +# =================================================================== +# Data Storage Configuration +# =================================================================== +# Data directory for persistent volumes +# Default: ./data (relative to project root) +# Production: /var/lib/meldestelle or dedicated mount point +DATA_PATH=./data + +# Volume configuration +# These directories will be created under DATA_PATH: +# - postgres/ (PostgreSQL data) +# - redis/ (Redis data) +# - prometheus/ (Prometheus metrics) +# - grafana/ (Grafana dashboards) +# - keycloak/ (Keycloak data) +# - consul/ (Consul data) +# - monitoring/ (Custom monitoring data) +# - desktop-app/ (Desktop application data) + +# =================================================================== +# Development and Testing +# =================================================================== +# Enable debug mode for Java applications +DEBUG=false + +# Enable Wasm compilation for client applications +enableWasm=false + +# =================================================================== +# Production Deployment Settings +# =================================================================== +# Container resource limits (configured in docker-compose files) +# These are documented here for reference: + +# Infrastructure Services Resource Limits: +# - postgres: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - redis: 1 CPU, 1GB RAM (reserved: 0.25 CPU, 256MB RAM) +# - keycloak: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 1GB RAM) +# - consul: 1 CPU, 512MB RAM (reserved: 0.25 CPU, 128MB RAM) +# - kafka: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - zookeeper: 1 CPU, 1GB RAM (reserved: 0.25 CPU, 256MB RAM) +# - prometheus: 1 CPU, 2GB RAM (reserved: 0.25 CPU, 512MB RAM) +# - grafana: 1 CPU, 1GB RAM (reserved: 0.25 CPU, 256MB RAM) +# - api-gateway: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 1GB RAM) + +# Microservices Resource Limits: +# - ping-service: 1 CPU, 1GB RAM (reserved: 0.25 CPU, 256MB RAM) +# - members-service: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - horses-service: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - events-service: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - masterdata-service: 1.5 CPU, 1.5GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - auth-server: 1.5 CPU, 1.5GB RAM (reserved: 0.5 CPU, 512MB RAM) + +# Client Applications Resource Limits: +# - web-app: 1 CPU, 512MB RAM (reserved: 0.25 CPU, 128MB RAM) +# - desktop-app: 2 CPU, 2GB RAM (reserved: 0.5 CPU, 512MB RAM) +# - monitoring-server: 1 CPU, 1GB RAM (reserved: 0.25 CPU, 256MB RAM) + +# =================================================================== +# Security Notes +# =================================================================== +# 1. All passwords and secrets are managed via Docker secrets +# 2. Run ./docker/secrets/setup-secrets.sh to generate secure credentials +# 3. Containers run as non-root users where possible +# 4. Security options: no-new-privileges enabled for all services +# 5. Networks are isolated with custom subnet (172.20.0.0/16) +# 6. Volumes have proper permissions and are mounted read-only where appropriate +# 7. Health checks are configured for all services +# 8. Resource limits prevent resource exhaustion attacks + +# =================================================================== +# Usage Instructions +# =================================================================== +# 1. Copy this file: cp .env.template .env +# 2. Customize values in .env for your environment +# 3. Generate secrets: ./docker/secrets/setup-secrets.sh --all +# 4. Create data directories: mkdir -p ./data/{postgres,redis,prometheus,grafana,keycloak,consul} +# 5. Deploy infrastructure: docker-compose -f docker-compose.yml.optimized up -d +# 6. Deploy services: docker-compose -f docker-compose.yml.optimized -f docker-compose.services.yml.optimized up -d +# 7. Deploy clients: docker-compose -f docker-compose.yml.optimized -f docker-compose.services.yml.optimized -f docker-compose.clients.yml.optimized up -d + +# =================================================================== +# Monitoring and Logging +# =================================================================== +# Access URLs (when running with default ports): +# - Grafana Dashboard: http://localhost:3000 (admin credentials in secrets) +# - Prometheus Metrics: http://localhost:9090 +# - Consul UI: http://localhost:8500 +# - Keycloak Admin: http://localhost:8180/admin (admin credentials in secrets) +# - API Gateway: http://localhost:8081 +# - Web Application: http://localhost:4000 +# - Desktop VNC: http://localhost:6080 + +# Log locations (inside containers): +# - Application logs: /app/logs/ +# - Nginx logs: /var/log/nginx/ +# - System logs: journalctl -u docker diff --git a/DOCKER-OPTIMIZATION.md b/DOCKER-OPTIMIZATION.md new file mode 100644 index 00000000..ab29a0bb --- /dev/null +++ b/DOCKER-OPTIMIZATION.md @@ -0,0 +1,427 @@ +# Docker Configuration Optimization & Security Analysis + +## Executive Summary + +This document outlines the comprehensive analysis, corrections, and optimizations made to all Docker and docker-compose configurations in the Meldestelle project. The optimizations focus on **security hardening**, **performance improvements**, and **production readiness**. + +### Key Achievements +- ✅ **Critical Security Vulnerabilities Fixed**: Eliminated hardcoded credentials and exposed secrets +- ✅ **Resource Management**: Added comprehensive CPU and memory limits for all services +- ✅ **Security Hardening**: Implemented Docker secrets, non-root users, and security constraints +- ✅ **Performance Optimization**: Enhanced health checks, startup dependencies, and resource allocation +- ✅ **Production Readiness**: Added proper volume management, networking, and monitoring + +--- + +## Security Improvements + +### 🔐 Critical Security Issues Resolved + +#### 1. **Secrets Management** +**Problem**: Hardcoded credentials in environment variables +```yaml +# BEFORE (INSECURE) +environment: + POSTGRES_PASSWORD: meldestelle + KEYCLOAK_CLIENT_SECRET: K5RqonwVOaxPKaXVH4mbthSRbjRh5tOK + GF_SECURITY_ADMIN_PASSWORD: admin +``` + +**Solution**: Docker secrets with secure file-based management +```yaml +# AFTER (SECURE) +environment: + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + KEYCLOAK_CLIENT_SECRET_FILE: /run/secrets/keycloak_client_secret + GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password +secrets: + - postgres_password + - keycloak_client_secret + - grafana_admin_password +``` + +#### 2. **Container Security Hardening** +**Added Security Measures**: +- `no-new-privileges:true` for all containers +- Non-root user execution where possible +- Read-only volume mounts for configuration files +- Secure file permissions (600) for all secrets + +#### 3. **Network Security** +**Improvements**: +- Custom isolated network with dedicated subnet (172.20.0.0/16) +- Proper inter-container communication controls +- Enhanced CORS and security headers for web applications + +### 🛡️ Security Features Added + +| Security Feature | Implementation | Benefit | +|-----------------|----------------|---------| +| Docker Secrets | File-based secrets management | Eliminates hardcoded credentials | +| Non-root Users | Custom user/group for applications | Reduces attack surface | +| Security Options | `no-new-privileges` flag | Prevents privilege escalation | +| Read-only Mounts | Config files mounted read-only | Prevents runtime tampering | +| Network Isolation | Custom bridge network | Isolates container communication | +| Resource Limits | CPU/Memory constraints | Prevents resource exhaustion attacks | + +--- + +## Performance Optimizations + +### 🚀 Resource Management + +#### Comprehensive Resource Limits +All services now have properly configured resource limits and reservations: + +**Infrastructure Services**: +```yaml +deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 1G +``` + +**Resource Allocation Summary**: +| Service | CPU Limit | Memory Limit | CPU Reserved | Memory Reserved | +|---------|-----------|--------------|--------------|-----------------| +| PostgreSQL | 2.0 | 2GB | 0.5 | 512MB | +| Redis | 1.0 | 1GB | 0.25 | 256MB | +| Keycloak | 2.0 | 2GB | 0.5 | 1GB | +| API Gateway | 2.0 | 2GB | 0.5 | 1GB | +| Kafka | 2.0 | 2GB | 0.5 | 512MB | +| Grafana | 1.0 | 1GB | 0.25 | 256MB | +| Prometheus | 1.0 | 2GB | 0.25 | 512MB | + +### 🔧 Performance Enhancements + +#### 1. **Optimized Health Checks** +```yaml +# Enhanced health check configuration +healthcheck: + test: ["CMD", "curl", "--fail", "--max-time", "5", "http://localhost:8080/health/ready"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s +``` + +#### 2. **JVM Optimization** +**Kafka JVM Settings**: +```yaml +environment: + KAFKA_HEAP_OPTS: "-Xmx1G -Xms512m" + KAFKA_JVM_PERFORMANCE_OPTS: "-XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35" +``` + +#### 3. **Database Performance** +**PostgreSQL Enhancements**: +- SCRAM-SHA-256 authentication for better security +- Optimized connection settings +- Proper data persistence with bind mounts + +**Redis Optimizations**: +- Memory management with `maxmemory` and `allkeys-lru` policy +- Persistent storage with AOF (Append Only File) +- Authentication enabled + +--- + +## Configuration Structure + +### 📁 File Organization + +The optimized configuration consists of: + +``` +├── docker-compose.yml.optimized # Infrastructure services +├── docker-compose.services.yml.optimized # Microservices +├── docker-compose.clients.yml.optimized # Client applications +├── .env.template # Environment configuration template +└── docker/ + └── secrets/ + ├── setup-secrets.sh # Automated secrets generation + ├── postgres_user.txt # Database username + ├── postgres_password.txt # Database password (generated) + ├── redis_password.txt # Redis password (generated) + ├── keycloak_admin_password.txt # Keycloak admin password (generated) + ├── keycloak_client_secret.txt # API Gateway client secret (generated) + ├── grafana_admin_user.txt # Grafana admin username + ├── grafana_admin_password.txt # Grafana admin password (generated) + ├── jwt_secret.txt # JWT signing secret (generated) + └── vnc_password.txt # VNC access password (generated) +``` + +### 🔄 Profile-based Deployment + +The optimized configuration supports selective service deployment: + +```bash +# Infrastructure only +docker-compose -f docker-compose.yml.optimized up -d + +# Infrastructure + Microservices +docker-compose -f docker-compose.yml.optimized \ + -f docker-compose.services.yml.optimized up -d + +# Full stack deployment +docker-compose -f docker-compose.yml.optimized \ + -f docker-compose.services.yml.optimized \ + -f docker-compose.clients.yml.optimized up -d + +# Selective services with profiles +docker-compose -f docker-compose.services.yml.optimized \ + --profile members --profile horses up -d +``` + +--- + +## Migration Guide + +### 🚀 Quick Start + +#### 1. **Generate Secrets** +```bash +# Generate all required secrets +./docker/secrets/setup-secrets.sh --all + +# Or generate individually +./docker/secrets/setup-secrets.sh --generate +./docker/secrets/setup-secrets.sh --validate +``` + +#### 2. **Configure Environment** +```bash +# Copy template and customize +cp .env.template .env + +# Edit configuration values +nano .env +``` + +#### 3. **Create Data Directories** +```bash +# Create persistent data directories +mkdir -p ./data/{postgres,redis,prometheus,grafana,keycloak,consul,monitoring,desktop-app} +``` + +#### 4. **Deploy Services** +```bash +# Start infrastructure +docker-compose -f docker-compose.yml.optimized up -d + +# Verify all services are healthy +docker-compose -f docker-compose.yml.optimized ps + +# Add microservices +docker-compose -f docker-compose.yml.optimized \ + -f docker-compose.services.yml.optimized up -d + +# Add client applications +docker-compose -f docker-compose.yml.optimized \ + -f docker-compose.services.yml.optimized \ + -f docker-compose.clients.yml.optimized up -d +``` + +### 🔄 Migration from Original Configuration + +#### Step 1: Backup Current Setup +```bash +# Stop existing services +docker-compose down + +# Backup current data (optional) +cp -r data/ data.backup/ +``` + +#### Step 2: Update Configuration +```bash +# Generate secrets first +./docker/secrets/setup-secrets.sh --all + +# Update environment configuration +cp .env.template .env +# Edit .env as needed +``` + +#### Step 3: Deploy Optimized Configuration +```bash +# Deploy with new configuration +docker-compose -f docker-compose.yml.optimized up -d +``` + +--- + +## Security Best Practices + +### 🛡️ Production Security Checklist + +- [ ] **Secrets Generated**: Run secrets setup script +- [ ] **File Permissions**: Ensure secret files have 600 permissions +- [ ] **Network Isolation**: Use custom Docker networks +- [ ] **Resource Limits**: All services have CPU/memory limits +- [ ] **Non-root Users**: Applications run as non-privileged users +- [ ] **Read-only Mounts**: Configuration mounted read-only +- [ ] **Security Options**: `no-new-privileges` enabled +- [ ] **Health Checks**: All critical services have health checks +- [ ] **Backup Strategy**: Regular data backups configured +- [ ] **Monitoring**: Prometheus and Grafana configured +- [ ] **Log Management**: Centralized logging configured + +### 🔐 Security Monitoring + +#### Access URLs (Default Configuration) +- **Grafana Dashboard**: http://localhost:3000 +- **Prometheus Metrics**: http://localhost:9090 +- **Consul UI**: http://localhost:8500 +- **Keycloak Admin**: http://localhost:8180/admin + +#### Security Metrics to Monitor +- Failed authentication attempts +- Resource usage patterns +- Container restart frequency +- Network connection anomalies +- Secret access patterns + +--- + +## Troubleshooting + +### 🔍 Common Issues and Solutions + +#### Issue 1: Secret File Permissions +**Problem**: Containers cannot read secret files +**Solution**: +```bash +# Fix permissions +chmod 600 docker/secrets/*.txt + +# Or regenerate with correct permissions +./docker/secrets/setup-secrets.sh --force +``` + +#### Issue 2: Resource Constraints +**Problem**: Services failing due to resource limits +**Solution**: +```bash +# Check resource usage +docker stats + +# Adjust limits in docker-compose files or increase system resources +``` + +#### Issue 3: Network Connectivity +**Problem**: Services cannot communicate +**Solution**: +```bash +# Check network configuration +docker network inspect meldestelle_meldestelle-network + +# Verify service health +docker-compose -f docker-compose.yml.optimized ps +``` + +#### Issue 4: Volume Mount Issues +**Problem**: Data not persisting or permission errors +**Solution**: +```bash +# Create data directories with correct permissions +mkdir -p ./data/{postgres,redis,prometheus,grafana,keycloak,consul} +chown -R 999:999 ./data/postgres # PostgreSQL user +chown -R 472:0 ./data/grafana # Grafana user +``` + +### 📊 Health Check Commands + +```bash +# Check all service status +docker-compose -f docker-compose.yml.optimized ps + +# View service logs +docker-compose -f docker-compose.yml.optimized logs [service-name] + +# Check resource usage +docker stats + +# Validate secrets +./docker/secrets/setup-secrets.sh --validate + +# Test connectivity +docker exec meldestelle-api-gateway curl -f http://postgres:5432 +``` + +--- + +## Performance Tuning + +### 🎯 Resource Optimization Guidelines + +#### Memory Allocation Strategy +1. **Infrastructure Services**: Higher memory allocation for databases and messaging +2. **Application Services**: Balanced CPU/memory for microservices +3. **Client Applications**: Lower resource requirements + +#### CPU Allocation Strategy +1. **I/O Bound Services** (Database, Redis): Moderate CPU, high memory +2. **Compute Bound Services** (Application logic): Higher CPU allocation +3. **Static Content Services** (Web apps): Lower overall resources + +#### JVM Tuning for Java Services +```yaml +environment: + JAVA_OPTS: | + -XX:MaxRAMPercentage=75.0 + -XX:+UseG1GC + -XX:+UseStringDeduplication + -XX:+UseContainerSupport + -Djava.security.egd=file:/dev/./urandom +``` + +--- + +## Monitoring and Observability + +### 📈 Metrics Collection + +#### Prometheus Metrics +- Container resource usage +- Application performance metrics +- Health check status +- Network traffic patterns + +#### Grafana Dashboards +- Infrastructure overview +- Application performance +- Security events +- Resource utilization trends + +#### Logging Strategy +- Centralized logging via Docker logs +- Structured JSON logging for applications +- Log rotation and retention policies +- Security event logging + +--- + +## Conclusion + +The Docker configuration optimization provides: + +1. **Enhanced Security**: Complete elimination of hardcoded credentials and implementation of Docker secrets +2. **Production Readiness**: Comprehensive resource limits, health checks, and monitoring +3. **Improved Performance**: Optimized resource allocation and container configurations +4. **Operational Excellence**: Automated secret management, comprehensive documentation, and troubleshooting guides +5. **Scalability**: Profile-based deployment and modular service architecture + +### Next Steps + +1. **Deploy optimized configuration** in development environment +2. **Validate all security measures** are properly implemented +3. **Monitor performance metrics** and adjust resource limits as needed +4. **Implement backup and recovery procedures** for persistent data +5. **Set up automated monitoring and alerting** for production deployment + +For questions or issues with the optimized configuration, refer to the troubleshooting section or consult the detailed configuration comments in the docker-compose files. diff --git a/docker-compose.clients.yml b/docker-compose.clients.yml index 00ef4a80..b197ec64 100644 --- a/docker-compose.clients.yml +++ b/docker-compose.clients.yml @@ -45,7 +45,7 @@ services: # depends_on removed for standalone client deployment # When using multi-file setup, api-gateway dependency is handled externally healthcheck: - test: ["CMD", "curl", "--fail", "http://localhost:4000/health"] + test: [ "CMD", "curl", "--fail", "http://localhost:4000/health" ] interval: 30s timeout: 10s retries: 3 @@ -59,82 +59,82 @@ services: # =================================================================== # Desktop Application (Kotlin Desktop + VNC) # =================================================================== -# desktop-app: -# build: -# context: . -# dockerfile: dockerfiles/clients/desktop-app/Dockerfile -# container_name: meldestelle-desktop-app -# environment: -# # API Configuration - fallback to external gateway if not in same compose network -# API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} -# # VNC Configuration -# DISPLAY: ":99" -# VNC_PORT: "5901" -# NOVNC_PORT: "6080" -# # App Information -# APP_TITLE: ${APP_NAME:-Meldestelle} -# APP_VERSION: ${APP_VERSION:-1.0.0} -# ports: -# - "6080:6080" # Web-based VNC (noVNC) -# - "5901:5901" # VNC direct access -# networks: -# - meldestelle-network -# # depends_on removed for standalone client deployment -# # When using multi-file setup, api-gateway dependency is handled externally -# healthcheck: -# test: [ "CMD", "/opt/health-check.sh" ] -# interval: 30s -# timeout: 10s -# retries: 3 -# start_period: 60s -# restart: unless-stopped -# labels: -# - "traefik.enable=true" -# - "traefik.http.routers.desktop-app.rule=Host(`localhost`) && PathPrefix(`/desktop`)" -# - "traefik.http.services.desktop-app.loadbalancer.server.port=6080" - - # =================================================================== - # Auth Server (Custom Keycloak Extension) - # =================================================================== - # auth-server: + # desktop-app: # build: # context: . - # dockerfile: dockerfiles/infrastructure/auth-server/Dockerfile - # args: - # # Global build arguments (from docker/build-args/global.env) - # GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} - # JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} - # BUILD_DATE: ${BUILD_DATE} - # VERSION: ${DOCKER_APP_VERSION:-1.0.0} - # # Infrastructure-specific arguments (from docker/build-args/infrastructure.env) - # SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} - # container_name: meldestelle-auth-server + # dockerfile: dockerfiles/clients/desktop-app/Dockerfile + # container_name: meldestelle-desktop-app # environment: - # SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev} - # SERVER_PORT: ${AUTH_SERVICE_PORT:-8087} - # KEYCLOAK_SERVER_URL: http://keycloak:8080 - # KEYCLOAK_REALM: meldestelle - # KEYCLOAK_CLIENT_ID: meldestelle-auth-service - # KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-auth-service-secret} - # DB_HOST: postgres - # DB_PORT: 5432 - # DB_NAME: ${POSTGRES_DB:-meldestelle} - # DB_USER: ${POSTGRES_USER:-meldestelle} - # DB_PASSWORD: ${POSTGRES_PASSWORD:-meldestelle} - # JWT_SECRET: ${JWT_SECRET:-meldestelle-jwt-secret-key-for-development-change-in-production} - # JWT_ISSUER: ${JWT_ISSUER:-meldestelle-api} - # JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-clients} + # # API Configuration - fallback to external gateway if not in same compose network + # API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} + # # VNC Configuration + # DISPLAY: ":99" + # VNC_PORT: "5901" + # NOVNC_PORT: "6080" + # # App Information + # APP_TITLE: ${APP_NAME:-Meldestelle} + # APP_VERSION: ${APP_VERSION:-1.0.0} # ports: - # - "${AUTH_SERVICE_PORT:-8087}:${AUTH_SERVICE_PORT:-8087}" + # - "6080:6080" # Web-based VNC (noVNC) + # - "5901:5901" # VNC direct access # networks: # - meldestelle-network + # # depends_on removed for standalone client deployment + # # When using multi-file setup, api-gateway dependency is handled externally # healthcheck: - # test: ["CMD", "curl", "--fail", "http://localhost:${AUTH_SERVICE_PORT:-8087}/actuator/health"] + # test: [ "CMD", "/opt/health-check.sh" ] # interval: 30s # timeout: 10s # retries: 3 # start_period: 60s # restart: unless-stopped + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.desktop-app.rule=Host(`localhost`) && PathPrefix(`/desktop`)" + # - "traefik.http.services.desktop-app.loadbalancer.server.port=6080" + + # =================================================================== + # Auth Server (Custom Keycloak Extension) + # =================================================================== +# auth-server: +# build: +# context: . +# dockerfile: dockerfiles/infrastructure/auth-server/Dockerfile +# args: +# # Global build arguments (from docker/build-args/global.env) +# GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} +# JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} +# BUILD_DATE: ${BUILD_DATE} +# VERSION: ${DOCKER_APP_VERSION:-1.0.0} +# # Infrastructure-specific arguments (from docker/build-args/infrastructure.env) +# SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} +# container_name: meldestelle-auth-server +# environment: +# SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev} +# SERVER_PORT: ${AUTH_SERVICE_PORT:-8087} +# KEYCLOAK_SERVER_URL: http://keycloak:8080 +# KEYCLOAK_REALM: meldestelle +# KEYCLOAK_CLIENT_ID: meldestelle-auth-service +# KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-auth-service-secret} +# DB_HOST: postgres +# DB_PORT: 5432 +# DB_NAME: ${POSTGRES_DB:-meldestelle} +# DB_USER: ${POSTGRES_USER:-meldestelle} +# DB_PASSWORD: ${POSTGRES_PASSWORD:-meldestelle} +# JWT_SECRET: ${JWT_SECRET:-meldestelle-jwt-secret-key-for-development-change-in-production} +# JWT_ISSUER: ${JWT_ISSUER:-meldestelle-api} +# JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-clients} +# ports: +# - "${AUTH_SERVICE_PORT:-8087}:${AUTH_SERVICE_PORT:-8087}" +# networks: +# - meldestelle-network +# healthcheck: +# test: [ "CMD", "curl", "--fail", "http://localhost:${AUTH_SERVICE_PORT:-8087}/actuator/health" ] +# interval: 30s +# timeout: 10s +# retries: 3 +# start_period: 60s +# restart: unless-stopped # =================================================================== # Monitoring Server (Custom Grafana Extensions) diff --git a/docker-compose.clients.yml.optimized b/docker-compose.clients.yml.optimized new file mode 100644 index 00000000..f6a67855 --- /dev/null +++ b/docker-compose.clients.yml.optimized @@ -0,0 +1,353 @@ +# =================================================================== +# Docker Compose - Client Applications (OPTIMIZED & SECURED) +# Meldestelle Project - Frontend Components +# =================================================================== +# Security & Performance Improvements: +# - Resource limits and reservations for all clients +# - Security hardening (non-root users, no-new-privileges) +# - Optimized build configurations and caching +# - Enhanced health checks and monitoring +# - Production-ready configurations +# =================================================================== +# Usage Scenarios: +# +# 1. STANDALONE CLIENT DEPLOYMENT: +# docker-compose -f docker-compose.clients.yml.optimized up -d +# - Clients run independently without api-gateway dependency +# - Set GATEWAY_HOST environment variable to external API Gateway +# +# 2. MULTI-FILE WITH INFRASTRUCTURE: +# docker-compose -f docker-compose.yml.optimized -f docker-compose.clients.yml.optimized up -d +# - Infrastructure services start first, then clients connect +# +# 3. COMPLETE SYSTEM: +# docker-compose -f docker-compose.yml.optimized -f docker-compose.services.yml.optimized -f docker-compose.clients.yml.optimized up -d +# - Full stack deployment with all components +# =================================================================== + +version: '3.9' + +services: + # =================================================================== + # Web Application - Kotlin/JS + Nginx with Security Hardening + # =================================================================== + web-app: + build: + context: . + dockerfile: dockerfiles/clients/web-app/Dockerfile + args: + - BUILD_DATE=${BUILD_DATE:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")} + - VERSION=${APP_VERSION:-1.0.0} + - NODE_ENV=${NODE_ENV:-production} + container_name: meldestelle-web-app + environment: + NODE_ENV: ${NODE_ENV:-production} + APP_TITLE: ${APP_NAME:-Meldestelle} + APP_VERSION: ${APP_VERSION:-1.0.0} + # API Gateway Configuration + API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} + # Nginx Worker Processes (for Performance) + NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} + NGINX_WORKER_CONNECTIONS: ${NGINX_WORKER_CONNECTIONS:-1024} + # Security Headers + NGINX_SECURITY_HEADERS: "true" + ports: + - "${WEB_APP_PORT:-4000}:4000" + networks: + - meldestelle-network + volumes: + # Nginx cache directory + - web-app-cache:/var/cache/nginx + # Temp directory with proper permissions + - web-app-tmp:/tmp/nginx + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + reservations: + cpus: '0.25' + memory: 128M + healthcheck: + test: ["CMD", "curl", "--fail", "--max-time", "5", "http://localhost:4000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + # Run as nginx user (built into nginx image) + user: "101:101" # nginx:nginx + labels: + - "traefik.enable=true" + - "traefik.http.routers.web-app.rule=Host(`${WEB_APP_DOMAIN:-localhost}`) && PathPrefix(`/`)" + - "traefik.http.services.web-app.loadbalancer.server.port=4000" + - "traefik.http.routers.web-app.tls=true" + + # =================================================================== + # Desktop Application - Kotlin Desktop + VNC with Security + # =================================================================== + desktop-app: + build: + context: . + dockerfile: dockerfiles/clients/desktop-app/Dockerfile + args: + - BUILD_DATE=${BUILD_DATE:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")} + - VERSION=${APP_VERSION:-1.0.0} + container_name: meldestelle-desktop-app + environment: + # API Configuration - fallback to external gateway if not in same compose network + API_BASE_URL: http://${GATEWAY_HOST:-api-gateway}:${GATEWAY_PORT:-8081} + # VNC Configuration with Security + DISPLAY: ":99" + VNC_PORT: "5901" + NOVNC_PORT: "6080" + VNC_PASSWORD_FILE: /run/secrets/vnc_password + # App Information + APP_TITLE: ${APP_NAME:-Meldestelle} + APP_VERSION: ${APP_VERSION:-1.0.0} + # JVM Settings for Desktop App + JAVA_OPTS: "-Xmx1g -Xms256m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" + ports: + - "${DESKTOP_VNC_WEB_PORT:-6080}:6080" # Web-based VNC (noVNC) + - "${DESKTOP_VNC_PORT:-5901}:5901" # VNC direct access + volumes: + # Desktop app data directory + - desktop-app-data:/app/data + # X11 socket for display + - /tmp/.X11-unix:/tmp/.X11-unix:rw + networks: + - meldestelle-network + secrets: + - vnc_password + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "/opt/health-check.sh"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 90s + restart: unless-stopped + security_opt: + - no-new-privileges:true + labels: + - "traefik.enable=true" + - "traefik.http.routers.desktop-app.rule=Host(`${DESKTOP_APP_DOMAIN:-localhost}`) && PathPrefix(`/desktop`)" + - "traefik.http.services.desktop-app.loadbalancer.server.port=6080" + - "traefik.http.routers.desktop-app.tls=true" + profiles: + - desktop + + # =================================================================== + # Auth Server - Custom Authentication Service (Enhanced Security) + # =================================================================== + auth-server: + build: + context: . + dockerfile: dockerfiles/infrastructure/auth-server/Dockerfile + args: + # Global build arguments + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + # Infrastructure-specific arguments + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} + container_name: meldestelle-auth-server + volumes: + - auth-server-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${AUTH_SERVICE_PORT:-8087} + # Keycloak Integration + KEYCLOAK_SERVER_URL: http://keycloak:8080 + KEYCLOAK_REALM: meldestelle + KEYCLOAK_CLIENT_ID: meldestelle-auth-service + KEYCLOAK_CLIENT_SECRET_FILE: /run/secrets/keycloak_auth_client_secret + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # JWT Configuration via secrets + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${AUTH_SERVICE_PORT:-8087}:8087" + depends_on: + postgres: + condition: service_healthy + keycloak: + condition: service_started + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - jwt_secret + - keycloak_auth_client_secret + deploy: + resources: + limits: + cpus: '1.5' + memory: 1.5G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "--fail", "--max-time", "10", "http://localhost:8087/actuator/health/readiness"] + interval: 30s + timeout: 15s + retries: 3 + start_period: 90s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - auth + + # =================================================================== + # Monitoring Server - Custom Grafana Extensions (Enhanced Security) + # =================================================================== + monitoring-server: + build: + context: . + dockerfile: dockerfiles/infrastructure/monitoring-server/Dockerfile + args: + # Global build arguments + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + # Infrastructure-specific arguments + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} + container_name: meldestelle-monitoring-server + volumes: + - monitoring-server-gradle-cache:/home/gradle/.gradle + - monitoring-data:/app/data + - ./docker/monitoring:/app/config:ro + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: 8088 + # Monitoring Services URLs + GRAFANA_URL: http://grafana:3000 + PROMETHEUS_URL: http://prometheus:9090 + # Authentication via secrets + GRAFANA_ADMIN_USER_FILE: /run/secrets/grafana_admin_user + GRAFANA_ADMIN_PASSWORD_FILE: /run/secrets/grafana_admin_password + METRICS_AUTH_USERNAME_FILE: /run/secrets/metrics_auth_username + METRICS_AUTH_PASSWORD_FILE: /run/secrets/metrics_auth_password + ports: + - "${MONITORING_SERVER_PORT:-8088}:8088" + depends_on: + grafana: + condition: service_healthy + prometheus: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - grafana_admin_user + - grafana_admin_password + - metrics_auth_username + - metrics_auth_password + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M + healthcheck: + test: ["CMD", "curl", "--fail", "--max-time", "10", "http://localhost:8088/actuator/health/readiness"] + interval: 30s + timeout: 15s + retries: 3 + start_period: 90s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - monitoring + +# =================================================================== +# Secrets - Client-specific and Shared +# =================================================================== +secrets: + # Shared secrets (external references to main infrastructure secrets) + postgres_user: + external: true + name: meldestelle_postgres_user + postgres_password: + external: true + name: meldestelle_postgres_password + grafana_admin_user: + external: true + name: meldestelle_grafana_admin_user + grafana_admin_password: + external: true + name: meldestelle_grafana_admin_password + + # Client-specific secrets + jwt_secret: + external: true + name: meldestelle_jwt_secret + vnc_password: + file: ./docker/secrets/vnc_password.txt + keycloak_auth_client_secret: + file: ./docker/secrets/keycloak_auth_client_secret.txt + metrics_auth_username: + file: ./docker/secrets/metrics_auth_username.txt + metrics_auth_password: + file: ./docker/secrets/metrics_auth_password.txt + +# =================================================================== +# Volumes - Client-specific Data and Caches +# =================================================================== +volumes: + # Web App volumes + web-app-cache: + driver: local + web-app-tmp: + driver: local + + # Desktop App volumes + desktop-app-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/desktop-app + + # Build cache volumes + auth-server-gradle-cache: + driver: local + monitoring-server-gradle-cache: + driver: local + + # Monitoring data (shared with main infrastructure) + monitoring-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/monitoring + +# =================================================================== +# Networks - Use external network from main infrastructure +# =================================================================== +networks: + meldestelle-network: + external: true + name: meldestelle_meldestelle-network diff --git a/docker-compose.services.yml.optimized b/docker-compose.services.yml.optimized new file mode 100644 index 00000000..8c0844f1 --- /dev/null +++ b/docker-compose.services.yml.optimized @@ -0,0 +1,430 @@ +# =================================================================== +# Docker Compose - Microservices (OPTIMIZED & SECURED) +# Meldestelle Project - Application Services +# =================================================================== +# Security & Performance Improvements: +# - Secrets management for database credentials +# - Resource limits and reservations for all services +# - Security hardening (non-root users, no-new-privileges) +# - Optimized health checks and startup dependencies +# - Enhanced monitoring and logging configuration +# =================================================================== +# Usage: +# Vollständiges System: docker-compose -f docker-compose.yml.optimized -f docker-compose.services.yml.optimized up -d +# =================================================================== + +version: '3.9' + +services: + # =================================================================== + # Ping Service - Enhanced Security & Performance + # =================================================================== + ping-service: + build: + context: . + dockerfile: dockerfiles/services/ping-service/Dockerfile + args: + # Global build arguments + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + # Service-specific arguments + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker} + container_name: meldestelle-ping-service + volumes: + # Mount Gradle cache for better build performance + - ping-service-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${PING_SERVICE_PORT:-8082} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: ${CONSUL_ENABLED:-true} + SPRING_CLOUD_CONSUL_DISCOVERY_ENABLED: ${CONSUL_ENABLED:-true} + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis Event Store connection with authentication + REDIS_EVENT_STORE_HOST: redis + REDIS_EVENT_STORE_PORT: 6379 + REDIS_EVENT_STORE_PASSWORD_FILE: /run/secrets/redis_password + # JWT Configuration for service-to-service authentication + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${PING_SERVICE_PORT:-8082}:8082" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - redis_password + - jwt_secret + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:8082/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + # =================================================================== + # Members Service - Production Ready (Currently Commented Out) + # =================================================================== + members-service: + build: + context: . + dockerfile: dockerfiles/services/members-service/Dockerfile + args: + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker} + container_name: meldestelle-members-service + volumes: + - members-service-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${MEMBERS_SERVICE_PORT:-8083} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: ${CONSUL_ENABLED:-true} + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis Event Store connection + REDIS_EVENT_STORE_HOST: redis + REDIS_EVENT_STORE_PORT: 6379 + REDIS_EVENT_STORE_PASSWORD_FILE: /run/secrets/redis_password + # Kafka messaging + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + # JWT Configuration + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${MEMBERS_SERVICE_PORT:-8083}:8083" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + kafka: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - redis_password + - jwt_secret + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:8083/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - members + + # =================================================================== + # Horses Service - Production Ready (Currently Commented Out) + # =================================================================== + horses-service: + build: + context: . + dockerfile: dockerfiles/services/horses-service/Dockerfile + args: + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker} + container_name: meldestelle-horses-service + volumes: + - horses-service-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${HORSES_SERVICE_PORT:-8084} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: ${CONSUL_ENABLED:-true} + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis Event Store connection + REDIS_EVENT_STORE_HOST: redis + REDIS_EVENT_STORE_PORT: 6379 + REDIS_EVENT_STORE_PASSWORD_FILE: /run/secrets/redis_password + # Kafka messaging + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + # JWT Configuration + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${HORSES_SERVICE_PORT:-8084}:8084" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + kafka: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - redis_password + - jwt_secret + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:8084/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - horses + + # =================================================================== + # Events Service - Production Ready (Currently Commented Out) + # =================================================================== + events-service: + build: + context: . + dockerfile: dockerfiles/services/events-service/Dockerfile + args: + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker} + container_name: meldestelle-events-service + volumes: + - events-service-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${EVENTS_SERVICE_PORT:-8085} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: ${CONSUL_ENABLED:-true} + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis Event Store connection + REDIS_EVENT_STORE_HOST: redis + REDIS_EVENT_STORE_PORT: 6379 + REDIS_EVENT_STORE_PASSWORD_FILE: /run/secrets/redis_password + # Kafka messaging + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + # JWT Configuration + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${EVENTS_SERVICE_PORT:-8085}:8085" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + kafka: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - redis_password + - jwt_secret + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:8085/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - events + + # =================================================================== + # Masterdata Service - Production Ready (Currently Commented Out) + # =================================================================== + masterdata-service: + build: + context: . + dockerfile: dockerfiles/services/masterdata-service/Dockerfile + args: + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker} + container_name: meldestelle-masterdata-service + volumes: + - masterdata-service-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker} + SERVER_PORT: ${MASTERDATA_SERVICE_PORT:-8086} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: ${CONSUL_ENABLED:-true} + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis Event Store connection + REDIS_EVENT_STORE_HOST: redis + REDIS_EVENT_STORE_PORT: 6379 + REDIS_EVENT_STORE_PASSWORD_FILE: /run/secrets/redis_password + # JWT Configuration + JWT_SECRET_FILE: /run/secrets/jwt_secret + JWT_ISSUER: ${JWT_ISSUER:-meldestelle-auth-server} + JWT_AUDIENCE: ${JWT_AUDIENCE:-meldestelle-services} + ports: + - "${MASTERDATA_SERVICE_PORT:-8086}:8086" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + - redis_password + - jwt_secret + deploy: + resources: + limits: + cpus: '1.5' + memory: 1.5G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:8086/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + profiles: + - masterdata + +# =================================================================== +# Secrets - Shared with main infrastructure +# =================================================================== +secrets: + postgres_user: + external: true + name: meldestelle_postgres_user + postgres_password: + external: true + name: meldestelle_postgres_password + redis_password: + external: true + name: meldestelle_redis_password + jwt_secret: + file: ./docker/secrets/jwt_secret.txt + +# =================================================================== +# Volumes - Service-specific Gradle Caches +# =================================================================== +volumes: + ping-service-gradle-cache: + driver: local + members-service-gradle-cache: + driver: local + horses-service-gradle-cache: + driver: local + events-service-gradle-cache: + driver: local + masterdata-service-gradle-cache: + driver: local + +# =================================================================== +# Networks - Use external network from main compose +# =================================================================== +networks: + meldestelle-network: + external: true + name: meldestelle_meldestelle-network diff --git a/docker-compose.yml.optimized b/docker-compose.yml.optimized new file mode 100644 index 00000000..60e44481 --- /dev/null +++ b/docker-compose.yml.optimized @@ -0,0 +1,522 @@ +# =================================================================== +# Docker Compose - Basis-Infrastruktur (OPTIMIZED & SECURED) +# Meldestelle Project - Essentielle Services +# =================================================================== +# Security & Performance Improvements: +# - Secrets management for sensitive data +# - Resource limits and reservations +# - Security hardening (read-only volumes, non-root users) +# - Optimized health checks and startup +# - Production-ready configurations +# =================================================================== + +version: '3.9' + +services: + # =================================================================== + # Datenbank - PostgreSQL with Security Hardening + # =================================================================== + postgres: + image: postgres:16-alpine + container_name: meldestelle-postgres + environment: + POSTGRES_USER_FILE: /run/secrets/postgres_user + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + POSTGRES_DB: ${POSTGRES_DB:-meldestelle} + # Security hardening + POSTGRES_INITDB_ARGS: "--auth-local=trust --auth-host=scram-sha-256" + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + - ./docker/services/postgres:/docker-entrypoint-initdb.d:ro + networks: + - meldestelle-network + secrets: + - postgres_user + - postgres_password + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $(cat /run/secrets/postgres_user) -d ${POSTGRES_DB:-meldestelle}"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s + restart: unless-stopped + # Security: Run as postgres user (built into image) + security_opt: + - no-new-privileges:true + + # =================================================================== + # Cache - Redis with Authentication + # =================================================================== + redis: + image: redis:7-alpine + container_name: meldestelle-redis + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis-data:/data + command: > + redis-server + --appendonly yes + --requirepass "$(cat /run/secrets/redis_password)" + --maxmemory 1gb + --maxmemory-policy allkeys-lru + networks: + - meldestelle-network + secrets: + - redis_password + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M + healthcheck: + test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "$(cat /run/secrets/redis_password)", "ping"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 20s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + # =================================================================== + # Authentifizierung - Keycloak with Enhanced Security + # =================================================================== + keycloak: + image: quay.io/keycloak/keycloak:${DOCKER_KEYCLOAK_VERSION:-26.4.0} + container_name: meldestelle-keycloak + environment: + # Admin Configuration - Using secrets + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD_FILE: /run/secrets/keycloak_admin_password + + # Database Configuration - Using secrets + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://postgres:5432/meldestelle + KC_DB_USERNAME_FILE: /run/secrets/postgres_user + KC_DB_PASSWORD_FILE: /run/secrets/postgres_password + KC_DB_SCHEMA: keycloak + + # HTTP Configuration - Production settings + KC_HTTP_ENABLED: true + KC_HOSTNAME_STRICT: false + KC_PROXY: edge + + # Security Configuration + KC_FEATURES: token-exchange,admin-fine-grained-authz + KC_LOG_LEVEL: ${KEYCLOAK_LOG_LEVEL:-INFO} + + ports: + - "${KEYCLOAK_PORT:-8180}:8080" + depends_on: + postgres: + condition: service_healthy + volumes: + - ./docker/services/keycloak:/opt/keycloak/data/import:ro + - keycloak-data:/opt/keycloak/data + command: + # Development mode with realm import enabled + - start-dev + - --import-realm + networks: + - meldestelle-network + secrets: + - keycloak_admin_password + - postgres_user + - postgres_password + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 1G + healthcheck: + test: ['CMD-SHELL', 'curl -fsS --max-time 5 http://localhost:8080/health/ready || exit 1'] + interval: 15s + timeout: 10s + retries: 5 + start_period: 90s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + # =================================================================== + # Service Discovery - Consul + # =================================================================== + consul: + image: hashicorp/consul:1.15 + container_name: meldestelle-consul + ports: + - "${CONSUL_PORT:-8500}:8500" + command: > + agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0 + -encrypt=$(consul keygen) + -datacenter=meldestelle-dc + networks: + - meldestelle-network + volumes: + - consul-data:/consul/data + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + reservations: + cpus: '0.25' + memory: 128M + healthcheck: + test: ["CMD", "consul", "members", "-http-addr=localhost:8500"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 20s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + # =================================================================== + # Messaging - Zookeeper & Kafka with Resource Limits + # =================================================================== + zookeeper: + image: confluentinc/cp-zookeeper:7.4.0 + container_name: meldestelle-zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: ${ZOOKEEPER_CLIENT_PORT:-2181} + ZOOKEEPER_TICK_TIME: 2000 + ZOOKEEPER_INIT_LIMIT: 5 + ZOOKEEPER_SYNC_LIMIT: 2 + KAFKA_OPTS: "-Xmx512m -Xms256m" + ports: + - "${ZOOKEEPER_CLIENT_PORT:-2181}:2181" + volumes: + - zookeeper-data:/var/lib/zookeeper/data + - zookeeper-logs:/var/lib/zookeeper/log + networks: + - meldestelle-network + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M + healthcheck: + test: ["CMD", "bash", "-c", "echo 'ruok' | nc localhost 2181 | grep imok"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + kafka: + image: confluentinc/cp-kafka:7.4.0 + container_name: meldestelle-kafka + environment: + KAFKA_BROKER_ID: ${KAFKA_BROKER_ID:-1} + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:${KAFKA_PORT:-9092} + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR:-1} + # JVM Settings + KAFKA_HEAP_OPTS: "-Xmx1G -Xms512m" + KAFKA_JVM_PERFORMANCE_OPTS: "-XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35" + ports: + - "${KAFKA_PORT:-9092}:9092" + depends_on: + zookeeper: + condition: service_healthy + volumes: + - kafka-data:/var/lib/kafka/data + networks: + - meldestelle-network + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "kafka-broker-api-versions", "--bootstrap-server", "localhost:9092"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + + # =================================================================== + # Monitoring - Prometheus with Security + # =================================================================== + prometheus: + image: prom/prometheus:${DOCKER_PROMETHEUS_VERSION:-v2.54.1} + container_name: meldestelle-prometheus + ports: + - "${PROMETHEUS_PORT:-9090}:9090" + volumes: + - prometheus-data:/prometheus + - ./docker/monitoring/prometheus:/etc/prometheus:ro + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=15d' + - '--storage.tsdb.retention.size=20GB' + - '--web.enable-lifecycle' + - '--web.enable-admin-api' + networks: + - meldestelle-network + deploy: + resources: + limits: + cpus: '1.0' + memory: 2G + reservations: + cpus: '0.25' + memory: 512M + healthcheck: + test: ["CMD", "promtool", "query", "instant", "localhost:9090", "up"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 30s + restart: unless-stopped + security_opt: + - no-new-privileges:true + user: "65534:65534" # nobody:nobody + + # =================================================================== + # Monitoring - Grafana with Enhanced Security + # =================================================================== + grafana: + image: grafana/grafana:${DOCKER_GRAFANA_VERSION:-11.3.0} + container_name: meldestelle-grafana + environment: + # Use secrets for admin credentials + GF_SECURITY_ADMIN_USER__FILE: /run/secrets/grafana_admin_user + GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password + GF_USERS_ALLOW_SIGN_UP: false + GF_INSTALL_PLUGINS: grafana-piechart-panel + # Security settings + GF_SECURITY_COOKIE_SECURE: true + GF_SECURITY_COOKIE_SAMESITE: strict + GF_SECURITY_DISABLE_GRAVATAR: true + GF_ANALYTICS_REPORTING_ENABLED: false + GF_ANALYTICS_CHECK_FOR_UPDATES: false + GF_SNAPSHOTS_EXTERNAL_ENABLED: false + ports: + - "${GRAFANA_PORT:-3000}:3000" + volumes: + - grafana-data:/var/lib/grafana + - ./docker/monitoring/grafana:/etc/grafana/provisioning:ro + depends_on: + - prometheus + networks: + - meldestelle-network + secrets: + - grafana_admin_user + - grafana_admin_password + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:3000/api/health"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 30s + restart: unless-stopped + security_opt: + - no-new-privileges:true + user: "472:0" # grafana user + + # =================================================================== + # API Gateway - Enhanced Security & Resource Management + # =================================================================== + api-gateway: + build: + context: . + dockerfile: dockerfiles/infrastructure/gateway/Dockerfile + args: + # Global build arguments + GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.0.0} + JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21} + BUILD_DATE: ${BUILD_DATE:-unknown} + VERSION: ${DOCKER_APP_VERSION:-1.0.0} + # Infrastructure-specific arguments + SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DEFAULT:-default} + container_name: meldestelle-api-gateway + volumes: + # Mount Gradle cache for better build performance + - api-gateway-gradle-cache:/home/gradle/.gradle + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev,keycloak} + CONSUL_HOST: consul + CONSUL_PORT: ${CONSUL_PORT:-8500} + CONSUL_ENABLED: "true" + GATEWAY_PORT: ${GATEWAY_PORT:-8081} + # Keycloak OAuth2 Integration - Using internal network + KEYCLOAK_SERVER_URL: http://keycloak:8080 + KEYCLOAK_ISSUER_URI: http://keycloak:8080/realms/meldestelle + KEYCLOAK_JWK_SET_URI: http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs + KEYCLOAK_REALM: meldestelle + KEYCLOAK_CLIENT_ID: api-gateway + # Security: Client secret via file + KEYCLOAK_CLIENT_SECRET_FILE: /run/secrets/keycloak_client_secret + # Custom JWT filter disabled - using oauth2ResourceServer instead + GATEWAY_SECURITY_KEYCLOAK_ENABLED: "false" + # Database connection via secrets + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB:-meldestelle} + DB_USERNAME_FILE: /run/secrets/postgres_user + DB_PASSWORD_FILE: /run/secrets/postgres_password + # Redis connection with auth + REDIS_HOST: redis + REDIS_PORT: 6379 + REDIS_PASSWORD_FILE: /run/secrets/redis_password + ports: + - "${GATEWAY_PORT:-8081}:8081" + depends_on: + consul: + condition: service_healthy + postgres: + condition: service_healthy + redis: + condition: service_healthy + keycloak: + condition: service_started + networks: + - meldestelle-network + secrets: + - keycloak_client_secret + - postgres_user + - postgres_password + - redis_password + deploy: + resources: + limits: + cpus: '2.0' + memory: 2G + reservations: + cpus: '0.5' + memory: 1G + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:${GATEWAY_PORT:-8081}/actuator/health/readiness"] + interval: 15s + timeout: 10s + retries: 3 + start_period: 60s + restart: unless-stopped + security_opt: + - no-new-privileges:true + +# =================================================================== +# Secrets Management - Production Ready +# =================================================================== +secrets: + postgres_user: + file: ./docker/secrets/postgres_user.txt + postgres_password: + file: ./docker/secrets/postgres_password.txt + redis_password: + file: ./docker/secrets/redis_password.txt + keycloak_admin_password: + file: ./docker/secrets/keycloak_admin_password.txt + keycloak_client_secret: + file: ./docker/secrets/keycloak_client_secret.txt + grafana_admin_user: + file: ./docker/secrets/grafana_admin_user.txt + grafana_admin_password: + file: ./docker/secrets/grafana_admin_password.txt + +# =================================================================== +# Volumes - Enhanced with Better Drivers +# =================================================================== +volumes: + postgres-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/postgres + redis-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/redis + prometheus-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/prometheus + grafana-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/grafana + keycloak-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/keycloak + consul-data: + driver: local + driver_opts: + type: none + o: bind + device: ${DATA_PATH:-./data}/consul + zookeeper-data: + driver: local + zookeeper-logs: + driver: local + kafka-data: + driver: local + api-gateway-gradle-cache: + driver: local + +# =================================================================== +# Networks - Enhanced Security +# =================================================================== +networks: + meldestelle-network: + driver: bridge + driver_opts: + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.driver.mtu: "1500" + ipam: + config: + - subnet: 172.20.0.0/16 + gateway: 172.20.0.1 diff --git a/docker/secrets/grafana_admin_password.txt b/docker/secrets/grafana_admin_password.txt new file mode 100644 index 00000000..e37f4acd --- /dev/null +++ b/docker/secrets/grafana_admin_password.txt @@ -0,0 +1 @@ +TiB6FRRYW4gjM7xie17mKtTYFOp \ No newline at end of file diff --git a/docker/secrets/grafana_admin_user.txt b/docker/secrets/grafana_admin_user.txt new file mode 100644 index 00000000..f77b0040 --- /dev/null +++ b/docker/secrets/grafana_admin_user.txt @@ -0,0 +1 @@ +admin \ No newline at end of file diff --git a/docker/secrets/jwt_secret.txt b/docker/secrets/jwt_secret.txt new file mode 100644 index 00000000..6c466b67 --- /dev/null +++ b/docker/secrets/jwt_secret.txt @@ -0,0 +1 @@ +ba960b899f72d5ed192b5597d7f4b5b8853d9d641a2dc23c6b1a4b692b20211c \ No newline at end of file diff --git a/docker/secrets/keycloak_admin_password.txt b/docker/secrets/keycloak_admin_password.txt new file mode 100644 index 00000000..d85823d0 --- /dev/null +++ b/docker/secrets/keycloak_admin_password.txt @@ -0,0 +1 @@ +XASb7AzVy7G5fEKulE1mNPTy2Sw6pHi \ No newline at end of file diff --git a/docker/secrets/keycloak_auth_client_secret.txt b/docker/secrets/keycloak_auth_client_secret.txt new file mode 100644 index 00000000..24b090bc --- /dev/null +++ b/docker/secrets/keycloak_auth_client_secret.txt @@ -0,0 +1 @@ +s8N3r59JwS0lFsJobKWFJXh9qvdbHgcC6S3fYXYdXFM6eMKkRMtQbxHo0NJKFJC \ No newline at end of file diff --git a/docker/secrets/keycloak_client_secret.txt b/docker/secrets/keycloak_client_secret.txt new file mode 100644 index 00000000..9f2f28b1 --- /dev/null +++ b/docker/secrets/keycloak_client_secret.txt @@ -0,0 +1 @@ +lRo7W15UNy60EFRlvk1XP99MmgrgK2Z97QK9btl9ZPVIVzWcY81Bebp9hpB \ No newline at end of file diff --git a/docker/secrets/metrics_auth_password.txt b/docker/secrets/metrics_auth_password.txt new file mode 100644 index 00000000..73e896b2 --- /dev/null +++ b/docker/secrets/metrics_auth_password.txt @@ -0,0 +1 @@ +pON4NxxsKPWseVg1gw5PyLNN4YYrj8h \ No newline at end of file diff --git a/docker/secrets/metrics_auth_username.txt b/docker/secrets/metrics_auth_username.txt new file mode 100644 index 00000000..6f8aca1a --- /dev/null +++ b/docker/secrets/metrics_auth_username.txt @@ -0,0 +1 @@ +metrics \ No newline at end of file diff --git a/docker/secrets/postgres_password.txt b/docker/secrets/postgres_password.txt new file mode 100644 index 00000000..5726652e --- /dev/null +++ b/docker/secrets/postgres_password.txt @@ -0,0 +1 @@ +pGnDFSiwacGxfKCtb8VJmTQc9Emlcdow \ No newline at end of file diff --git a/docker/secrets/postgres_user.txt b/docker/secrets/postgres_user.txt new file mode 100644 index 00000000..81559440 --- /dev/null +++ b/docker/secrets/postgres_user.txt @@ -0,0 +1 @@ +meldestelle \ No newline at end of file diff --git a/docker/secrets/redis_password.txt b/docker/secrets/redis_password.txt new file mode 100644 index 00000000..eaab894a --- /dev/null +++ b/docker/secrets/redis_password.txt @@ -0,0 +1 @@ +p701HhKOnZJ4zbY9dGRvyH9kQTKcsUm \ No newline at end of file diff --git a/docker/secrets/setup-secrets.sh b/docker/secrets/setup-secrets.sh new file mode 100755 index 00000000..919f0a3e --- /dev/null +++ b/docker/secrets/setup-secrets.sh @@ -0,0 +1,345 @@ +#!/bin/bash + +# =================================================================== +# Docker Secrets Setup Script - Meldestelle Project +# =================================================================== +# This script generates secure secrets for all Docker services +# Security Features: +# - Generates cryptographically secure random passwords +# - Creates JWT secrets with proper length for HMAC512 +# - Sets appropriate file permissions (600) for security +# - Provides backup functionality +# - Validates secret file creation +# =================================================================== + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SECRETS_DIR="${SCRIPT_DIR}" + +# Logging function +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[WARNING] $1${NC}" +} + +error() { + echo -e "${RED}[ERROR] $1${NC}" + exit 1 +} + +# Function to generate secure random password +generate_password() { + local length=${1:-32} + openssl rand -base64 $((length * 3 / 4)) | tr -d "=+/" | cut -c1-${length} +} + +# Function to generate JWT secret (64 characters for HMAC512) +generate_jwt_secret() { + openssl rand -hex 32 +} + +# Function to create secret file with proper permissions +create_secret_file() { + local filename="$1" + local content="$2" + local filepath="${SECRETS_DIR}/${filename}" + + # Check if file already exists + if [[ -f "$filepath" ]]; then + warn "Secret file $filename already exists. Use --force to overwrite." + return 1 + fi + + # Create the secret file + echo -n "$content" > "$filepath" + chmod 600 "$filepath" + + log "Created secret file: $filename" + return 0 +} + +# Function to backup existing secrets +backup_secrets() { + local backup_dir="${SECRETS_DIR}/backup_$(date +%Y%m%d_%H%M%S)" + + if find "$SECRETS_DIR" -name "*.txt" -type f | grep -q .; then + log "Creating backup of existing secrets..." + mkdir -p "$backup_dir" + find "$SECRETS_DIR" -name "*.txt" -type f -exec cp {} "$backup_dir/" \; + log "Backup created in: $backup_dir" + fi +} + +# Function to validate secret file +validate_secret_file() { + local filepath="$1" + local min_length="$2" + + if [[ ! -f "$filepath" ]]; then + error "Secret file does not exist: $filepath" + fi + + local content_length=$(wc -c < "$filepath") + if [[ $content_length -lt $min_length ]]; then + error "Secret file $filepath is too short (${content_length} < ${min_length})" + fi + + local permissions=$(stat -c %a "$filepath") + if [[ "$permissions" != "600" ]]; then + warn "Secret file $filepath has incorrect permissions: $permissions (should be 600)" + chmod 600 "$filepath" + fi +} + +# Function to generate all secrets +generate_all_secrets() { + local force_overwrite=${1:-false} + + log "Starting secret generation for Meldestelle Docker infrastructure..." + + # Create backup if not forcing overwrite + if [[ "$force_overwrite" != "true" ]]; then + backup_secrets + fi + + # Database secrets + log "Generating database secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/postgres_user.txt" ]]; then + create_secret_file "postgres_user.txt" "meldestelle" + fi + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/postgres_password.txt" ]]; then + create_secret_file "postgres_password.txt" "$(generate_password 32)" + fi + + # Redis secrets + log "Generating Redis secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/redis_password.txt" ]]; then + create_secret_file "redis_password.txt" "$(generate_password 32)" + fi + + # Keycloak secrets + log "Generating Keycloak secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/keycloak_admin_password.txt" ]]; then + create_secret_file "keycloak_admin_password.txt" "$(generate_password 32)" + fi + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/keycloak_client_secret.txt" ]]; then + create_secret_file "keycloak_client_secret.txt" "$(generate_password 64)" + fi + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/keycloak_auth_client_secret.txt" ]]; then + create_secret_file "keycloak_auth_client_secret.txt" "$(generate_password 64)" + fi + + # Grafana secrets + log "Generating Grafana secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/grafana_admin_user.txt" ]]; then + create_secret_file "grafana_admin_user.txt" "admin" + fi + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/grafana_admin_password.txt" ]]; then + create_secret_file "grafana_admin_password.txt" "$(generate_password 32)" + fi + + # JWT secrets + log "Generating JWT secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/jwt_secret.txt" ]]; then + create_secret_file "jwt_secret.txt" "$(generate_jwt_secret)" + fi + + # VNC secrets (for desktop app) + log "Generating VNC secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/vnc_password.txt" ]]; then + create_secret_file "vnc_password.txt" "$(generate_password 16)" + fi + + # Monitoring secrets + log "Generating monitoring secrets..." + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/metrics_auth_username.txt" ]]; then + create_secret_file "metrics_auth_username.txt" "metrics" + fi + if [[ "$force_overwrite" == "true" ]] || ! [[ -f "${SECRETS_DIR}/metrics_auth_password.txt" ]]; then + create_secret_file "metrics_auth_password.txt" "$(generate_password 32)" + fi + + log "Secret generation completed successfully!" +} + +# Function to validate all secrets +validate_all_secrets() { + log "Validating all secret files..." + + # Define expected secrets with minimum lengths + declare -A secrets=( + ["postgres_user.txt"]=8 + ["postgres_password.txt"]=16 + ["redis_password.txt"]=16 + ["keycloak_admin_password.txt"]=16 + ["keycloak_client_secret.txt"]=32 + ["keycloak_auth_client_secret.txt"]=32 + ["grafana_admin_user.txt"]=4 + ["grafana_admin_password.txt"]=16 + ["jwt_secret.txt"]=64 + ["vnc_password.txt"]=8 + ["metrics_auth_username.txt"]=4 + ["metrics_auth_password.txt"]=16 + ) + + local all_valid=true + for secret_file in "${!secrets[@]}"; do + local filepath="${SECRETS_DIR}/${secret_file}" + local min_length=${secrets[$secret_file]} + + if validate_secret_file "$filepath" "$min_length" 2>/dev/null; then + log "✓ $secret_file is valid" + else + error "✗ $secret_file is invalid or missing" + all_valid=false + fi + done + + if [[ "$all_valid" == "true" ]]; then + log "All secret files are valid and properly secured!" + else + error "Some secret files are invalid. Please regenerate secrets." + fi +} + +# Function to create Docker secrets +create_docker_secrets() { + log "Creating Docker secrets..." + + # Get the project name (directory name) + local project_name=$(basename "$(dirname "$(dirname "$SCRIPT_DIR")")") + + # Define secrets to create + declare -A docker_secrets=( + ["postgres_user"]="postgres_user.txt" + ["postgres_password"]="postgres_password.txt" + ["redis_password"]="redis_password.txt" + ["keycloak_admin_password"]="keycloak_admin_password.txt" + ["keycloak_client_secret"]="keycloak_client_secret.txt" + ["grafana_admin_user"]="grafana_admin_user.txt" + ["grafana_admin_password"]="grafana_admin_password.txt" + ["jwt_secret"]="jwt_secret.txt" + ) + + for secret_name in "${!docker_secrets[@]}"; do + local secret_file="${docker_secrets[$secret_name]}" + local filepath="${SECRETS_DIR}/${secret_file}" + local docker_secret_name="${project_name}_${secret_name}" + + # Check if Docker secret already exists + if docker secret ls --format "{{.Name}}" | grep -q "^${docker_secret_name}$"; then + warn "Docker secret $docker_secret_name already exists" + else + # Create Docker secret + if docker secret create "$docker_secret_name" "$filepath"; then + log "Created Docker secret: $docker_secret_name" + else + error "Failed to create Docker secret: $docker_secret_name" + fi + fi + done +} + +# Function to show usage +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --help Show this help message" + echo " --generate Generate all secret files (default)" + echo " --force Force overwrite existing secret files" + echo " --validate Validate existing secret files" + echo " --docker-secrets Create Docker secrets from files" + echo " --all Generate files, validate, and create Docker secrets" + echo "" + echo "Examples:" + echo " $0 # Generate secrets (skip existing files)" + echo " $0 --force # Generate secrets (overwrite existing files)" + echo " $0 --validate # Validate existing secret files" + echo " $0 --all # Complete setup (generate, validate, docker secrets)" +} + +# Main execution +main() { + local action="generate" + local force_overwrite=false + + # Check dependencies + if ! command -v openssl &> /dev/null; then + error "openssl is required but not installed" + fi + + # Parse command line arguments + while [[ $# -gt 0 ]]; do + case $1 in + --help) + show_usage + exit 0 + ;; + --generate) + action="generate" + shift + ;; + --force) + force_overwrite=true + shift + ;; + --validate) + action="validate" + shift + ;; + --docker-secrets) + action="docker-secrets" + shift + ;; + --all) + action="all" + shift + ;; + *) + error "Unknown option: $1" + ;; + esac + done + + # Ensure secrets directory exists + mkdir -p "$SECRETS_DIR" + + # Execute requested action + case $action in + "generate") + generate_all_secrets "$force_overwrite" + ;; + "validate") + validate_all_secrets + ;; + "docker-secrets") + create_docker_secrets + ;; + "all") + generate_all_secrets "$force_overwrite" + validate_all_secrets + create_docker_secrets + ;; + *) + error "Invalid action: $action" + ;; + esac + + log "Operation completed successfully!" +} + +# Run main function with all arguments +main "$@" diff --git a/docker/secrets/vnc_password.txt b/docker/secrets/vnc_password.txt new file mode 100644 index 00000000..3fbc6e46 --- /dev/null +++ b/docker/secrets/vnc_password.txt @@ -0,0 +1 @@ +nrscAXfIoOKTAEt \ No newline at end of file diff --git a/docker/services/keycloak/meldestelle-realm.json.backup b/docker/services/keycloak/meldestelle-realm.json.backup new file mode 100644 index 00000000..07966db6 --- /dev/null +++ b/docker/services/keycloak/meldestelle-realm.json.backup @@ -0,0 +1,294 @@ +{ + "realm": "meldestelle", + "enabled": true, + "displayName": "Meldestelle Authentication", + "displayNameHtml": "
Meldestelle
", + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": true, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 5, + "defaultSignatureAlgorithm": "RS256", + "offlineSessionMaxLifespan": 5184000, + "offlineSessionMaxLifespanEnabled": false, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "refreshTokenMaxReuse": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "internationalizationEnabled": true, + "supportedLocales": ["de", "en"], + "defaultLocale": "de", + "roles": { + "realm": [ + { + "name": "ADMIN", + "description": "Administrator role with full system access", + "composite": false, + "clientRole": false + }, + { + "name": "USER", + "description": "Standard user role with limited access", + "composite": false, + "clientRole": false + }, + { + "name": "MONITORING", + "description": "Monitoring role for system health checks", + "composite": false, + "clientRole": false + }, + { + "name": "GUEST", + "description": "Guest role with minimal access", + "composite": false, + "clientRole": false + } + ] + }, + "clients": [ + { + "clientId": "api-gateway", + "name": "API Gateway Client", + "description": "OAuth2 client for the Meldestelle API Gateway", + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "http://localhost:8081/*", + "http://localhost:3000/*", + "https://app.meldestelle.at/*" + ], + "webOrigins": [ + "http://localhost:8081", + "http://localhost:3000", + "https://app.meldestelle.at" + ], + "protocol": "openid-connect", + "bearerOnly": false, + "publicClient": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "authorizationServicesEnabled": false, + "fullScopeAllowed": true, + "frontchannelLogout": true, + "attributes": { + "access.token.lifespan": "300", + "client.secret.creation.time": "0", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "protocolMappers": [ + { + "name": "realm-roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + }, + { + "name": "client-roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "full-name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "clientId": "web-app", + "name": "Web Application Client", + "description": "Public client for web frontend", + "enabled": true, + "publicClient": true, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "redirectUris": [ + "http://localhost:3000/*", + "https://app.meldestelle.at/*" + ], + "webOrigins": [ + "http://localhost:3000", + "https://app.meldestelle.at" + ], + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + } + } + ], + "users": [ + { + "username": "admin", + "enabled": true, + "emailVerified": true, + "firstName": "System", + "lastName": "Administrator", + "email": "admin@meldestelle.local", + "credentials": [ + { + "type": "password", + "value": "Change_Me_In_Production!", + "temporary": true + } + ], + "realmRoles": ["ADMIN", "USER"], + "clientRoles": { + "api-gateway": ["ADMIN"] + } + } + ], + "groups": [], + "defaultRoles": ["USER", "GUEST"], + "requiredCredentials": ["password"], + "passwordPolicy": "length(8)", + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], + "webAuthnPolicyRpEntityName": "meldestelle", + "webAuthnPolicySignatureAlgorithms": ["ES256", "RS256"], + "smtpServer": {}, + "eventsEnabled": true, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [ + "LOGIN", + "LOGIN_ERROR", + "LOGOUT", + "REGISTER", + "REGISTER_ERROR", + "UPDATE_PASSWORD", + "UPDATE_PASSWORD_ERROR" + ], + "adminEventsEnabled": true, + "adminEventsDetailsEnabled": true, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.keys.KeyProvider": [ + { + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": ["100"] + } + }, + { + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": ["100"], + "algorithm": ["HS256"] + } + }, + { + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": ["100"] + } + } + ] + }, + "authenticationFlows": [], + "authenticatorConfig": [], + "requiredActions": [], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "frontendUrl": "", + "acr.loa.map": "{}", + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientSessionMaxLifespan": "0", + "clientOfflineSessionIdleTimeout": "0" + } +} diff --git a/logs/gateway.log b/logs/gateway.log new file mode 100644 index 00000000..b35cb8ba --- /dev/null +++ b/logs/gateway.log @@ -0,0 +1,27 @@ +2025-10-05 21:49:40.525 [background-preinit] INFO [] o.h.validator.internal.util.Version - HV000001: Hibernate Validator 8.0.3.Final +2025-10-05 21:49:40.564 [main] INFO [] a.m.i.gateway.GatewayApplicationKt - Starting GatewayApplicationKt using Java 21.0.8 with PID 783949 (/home/stefan-mo/WsMeldestelle/Meldestelle/infrastructure/gateway/build/classes/kotlin/main started by stefan-mo in /home/stefan-mo/WsMeldestelle/Meldestelle) +2025-10-05 21:49:40.565 [main] DEBUG [] a.m.i.gateway.GatewayApplicationKt - Running with Spring Boot v3.5.5, Spring v6.2.10 +2025-10-05 21:49:40.565 [main] INFO [] a.m.i.gateway.GatewayApplicationKt - The following 1 profile is active: "dev" +2025-10-05 21:49:41.913 [main] INFO [] o.s.cloud.context.scope.GenericScope - BeanFactory id=548af07a-4d4c-30b3-8388-eaf5c0d95d23 +2025-10-05 21:49:44.188 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [After] +2025-10-05 21:49:44.188 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Before] +2025-10-05 21:49:44.188 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Between] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Cookie] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Header] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Host] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Method] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Path] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Query] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [ReadBody] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [RemoteAddr] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [XForwardedRemoteAddr] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [Weight] +2025-10-05 21:49:44.189 [main] INFO [] o.s.c.g.r.RouteDefinitionRouteLocator - Loaded RoutePredicateFactory [CloudFoundryRouteService] +2025-10-05 21:49:44.880 [main] INFO [] o.s.b.a.e.web.EndpointLinksResolver - Exposing 6 endpoints beneath base path '/actuator' +2025-10-05 21:49:45.585 [main] WARN [] o.s.c.l.c.LoadBalancerCacheAutoConfiguration$LoadBalancerCaffeineWarnLogger - Spring Cloud LoadBalancer is currently working with the default cache. While this cache implementation is useful for development and tests, it's recommended to use Caffeine cache in production.You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath. +2025-10-05 21:49:45.714 [main] INFO [] o.s.b.w.e.netty.NettyWebServer - Netty started on port 8080 (http) +2025-10-05 21:49:45.716 [main] INFO [] o.s.c.c.s.ConsulServiceRegistry - Registering service with consul: NewService{id='meldestelle-8080-2567c504-f48c-4f6e-918e-23b80c37675e', name='meldestelle', tags=[], address='10.0.0.18', meta={secure=false}, port=8080, enableTagOverride=null, check=Check{script='null', dockerContainerID='null', shell='null', interval='10s', ttl='null', http='http://10.0.0.18:8080/actuator/health', method='null', header={}, tcp='null', timeout='null', deregisterCriticalServiceAfter='null', tlsSkipVerify=null, status='null', grpc='null', grpcUseTLS=null}, checks=null} +2025-10-05 21:49:45.865 [main] INFO [] a.m.i.gateway.GatewayApplicationKt - Started GatewayApplicationKt in 5.753 seconds (process running for 6.365) +2025-10-05 21:53:53.365 [SpringApplicationShutdownHook] INFO [] o.s.b.w.e.netty.GracefulShutdown - Commencing graceful shutdown. Waiting for active requests to complete +2025-10-05 21:53:53.367 [netty-shutdown] INFO [] o.s.b.w.e.netty.GracefulShutdown - Graceful shutdown complete +2025-10-05 21:53:55.378 [SpringApplicationShutdownHook] INFO [] o.s.c.c.s.ConsulServiceRegistry - Deregistering service with consul: meldestelle-8080-2567c504-f48c-4f6e-918e-23b80c37675e diff --git a/logs/troubleshooting/compose-config.txt b/logs/troubleshooting/compose-config.txt new file mode 100644 index 00000000..87a5bac3 --- /dev/null +++ b/logs/troubleshooting/compose-config.txt @@ -0,0 +1,371 @@ +# docker compose config output +name: meldestelle +services: + api-gateway: + build: + context: /home/stefan-mo/WsMeldestelle/Meldestelle + dockerfile: dockerfiles/infrastructure/gateway/Dockerfile + args: + BUILD_DATE: unknown + GRADLE_VERSION: 9.0.0 + JAVA_VERSION: "21" + SPRING_PROFILES_ACTIVE: default + VERSION: 1.0.0 + container_name: meldestelle-api-gateway + depends_on: + consul: + condition: service_healthy + required: true + keycloak: + condition: service_healthy + required: true + postgres: + condition: service_healthy + required: true + redis: + condition: service_healthy + required: true + environment: + CONSUL_ENABLED: "true" + CONSUL_HOST: consul + CONSUL_PORT: "8500" + GATEWAY_PORT: "8081" + GATEWAY_SECURITY_KEYCLOAK_ENABLED: "false" + KEYCLOAK_CLIENT_ID: api-gateway + KEYCLOAK_ISSUER_URI: http://keycloak:8080/realms/meldestelle + KEYCLOAK_JWK_SET_URI: http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs + KEYCLOAK_REALM: meldestelle + KEYCLOAK_SERVER_URL: http://keycloak:8080 + SPRING_PROFILES_ACTIVE: dev,keycloak + healthcheck: + test: + - CMD + - curl + - --fail + - http://localhost:8081/actuator/health + timeout: 5s + interval: 15s + retries: 3 + start_period: 30s + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 8081 + published: "8081" + protocol: tcp + restart: unless-stopped + volumes: + - type: volume + source: api-gateway-gradle-cache + target: /home/gradle/.gradle + volume: {} + consul: + command: + - agent + - -server + - -ui + - -node=server-1 + - -bootstrap-expect=1 + - -client=0.0.0.0 + container_name: meldestelle-consul + healthcheck: + test: + - CMD + - curl + - -f + - http://localhost:8500/v1/status/leader + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: hashicorp/consul:1.15 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 8500 + published: "8500" + protocol: tcp + restart: unless-stopped + grafana: + container_name: meldestelle-grafana + depends_on: + prometheus: + condition: service_started + required: true + environment: + GF_INSTALL_PLUGINS: grafana-piechart-panel + GF_SECURITY_ADMIN_PASSWORD: admin + GF_SECURITY_ADMIN_USER: admin + GF_USERS_ALLOW_SIGN_UP: "false" + healthcheck: + test: + - CMD + - curl + - --fail + - http://localhost:3000/api/health + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: grafana/grafana:11.3.0 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 3000 + published: "3000" + protocol: tcp + restart: unless-stopped + volumes: + - type: volume + source: grafana-data + target: /var/lib/grafana + volume: {} + - type: bind + source: /home/stefan-mo/WsMeldestelle/Meldestelle/docker/monitoring/grafana + target: /etc/grafana/provisioning + read_only: true + bind: + create_host_path: true + kafka: + container_name: meldestelle-kafka + depends_on: + zookeeper: + condition: service_healthy + required: true + environment: + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_BROKER_ID: "1" + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1" + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + healthcheck: + test: + - CMD + - kafka-broker-api-versions + - --bootstrap-server + - localhost:9092 + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: confluentinc/cp-kafka:7.4.0 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 9092 + published: "9092" + protocol: tcp + restart: unless-stopped + keycloak: + command: + - start-dev + - --import-realm + container_name: meldestelle-keycloak + depends_on: + postgres: + condition: service_healthy + required: true + environment: + JAVA_OPTS_APPEND: -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+DisableExplicitGC -Djava.net.preferIPv4Stack=true -Duser.timezone=Europe/Vienna + KC_CACHE: ispn + KC_DB: postgres + KC_DB_PASSWORD: meldestelle + KC_DB_POOL_INITIAL_SIZE: "5" + KC_DB_POOL_MAX_SIZE: "20" + KC_DB_POOL_MIN_SIZE: "5" + KC_DB_SCHEMA: keycloak + KC_DB_URL: jdbc:postgresql://postgres:5432/meldestelle + KC_DB_USERNAME: meldestelle + KC_HEALTH_ENABLED: "true" + KC_HOSTNAME_STRICT: "false" + KC_HTTP_ENABLED: "true" + KC_HTTP_PORT: "8080" + KC_LOG_CONSOLE_COLOR: "false" + KC_LOG_CONSOLE_FORMAT: plain + KC_LOG_LEVEL: info + KC_METRICS_ENABLED: "true" + KC_PROXY_HEADERS: xforwarded + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + healthcheck: + test: + - CMD-SHELL + - 'if command -v curl >/dev/null 2>&1; then curl -fsS http://localhost:8080/health/ready | grep -q "\"status\":\"UP\""; elif command -v wget >/dev/null 2>&1; then wget -q -O - http://localhost:8080/health/ready | grep -q "\"status\":\"UP\""; else echo "Healthcheck: curl/wget not found, using bash /dev/tcp fallback" >&2; timeout 25 bash -lc "exec 3<>/dev/tcp/127.0.0.1/8080 && printf \"GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\" >&3 && head -n 1 <&3 | grep -q \"200 OK\""; fi' + timeout: 30s + interval: 15s + retries: 10 + start_period: 3m0s + image: quay.io/keycloak/keycloak:26.4.0 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 8080 + published: "8180" + protocol: tcp + restart: unless-stopped + volumes: + - type: bind + source: /home/stefan-mo/WsMeldestelle/Meldestelle/docker/services/keycloak + target: /opt/keycloak/data/import + bind: + create_host_path: true + - type: volume + source: keycloak-data + target: /opt/keycloak/data + volume: {} + postgres: + container_name: meldestelle-postgres + environment: + POSTGRES_DB: meldestelle + POSTGRES_PASSWORD: meldestelle + POSTGRES_USER: meldestelle + healthcheck: + test: + - CMD-SHELL + - pg_isready -U meldestelle -d meldestelle + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: postgres:16-alpine + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 5432 + published: "5432" + protocol: tcp + restart: unless-stopped + volumes: + - type: volume + source: postgres-data + target: /var/lib/postgresql/data + volume: {} + - type: bind + source: /home/stefan-mo/WsMeldestelle/Meldestelle/docker/services/postgres + target: /docker-entrypoint-initdb.d + bind: + create_host_path: true + prometheus: + command: + - --config.file=/etc/prometheus/prometheus.yml + - --storage.tsdb.path=/prometheus + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --storage.tsdb.retention.time=200h + - --web.enable-lifecycle + container_name: meldestelle-prometheus + healthcheck: + test: + - CMD + - wget + - --no-verbose + - --tries=1 + - --spider + - http://localhost:9090/-/healthy + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: prom/prometheus:v2.54.1 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 9090 + published: "9090" + protocol: tcp + restart: unless-stopped + volumes: + - type: volume + source: prometheus-data + target: /prometheus + volume: {} + - type: bind + source: /home/stefan-mo/WsMeldestelle/Meldestelle/docker/monitoring/prometheus + target: /etc/prometheus + read_only: true + bind: + create_host_path: true + redis: + command: + - redis-server + - --appendonly + - "yes" + container_name: meldestelle-redis + healthcheck: + test: + - CMD + - redis-cli + - ping + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: redis:7-alpine + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 6379 + published: "6379" + protocol: tcp + restart: unless-stopped + volumes: + - type: volume + source: redis-data + target: /data + volume: {} + zookeeper: + container_name: meldestelle-zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: "2181" + ZOOKEEPER_TICK_TIME: "2000" + healthcheck: + test: + - CMD + - bash + - -c + - echo 'ruok' | nc localhost 2181 + timeout: 5s + interval: 10s + retries: 3 + start_period: 20s + image: confluentinc/cp-zookeeper:7.4.0 + networks: + meldestelle-network: null + ports: + - mode: ingress + target: 2181 + published: "2181" + protocol: tcp + restart: unless-stopped +networks: + meldestelle-network: + name: meldestelle_meldestelle-network + driver: bridge +volumes: + api-gateway-gradle-cache: + name: meldestelle_api-gateway-gradle-cache + driver: local + grafana-data: + name: meldestelle_grafana-data + driver: local + keycloak-data: + name: meldestelle_keycloak-data + driver: local + postgres-data: + name: meldestelle_postgres-data + driver: local + prometheus-data: + name: meldestelle_prometheus-data + driver: local + redis-data: + name: meldestelle_redis-data + driver: local diff --git a/scripts/troubleshooting/logs/troubleshooting/compose-config.txt b/scripts/troubleshooting/logs/troubleshooting/compose-config.txt new file mode 100644 index 00000000..8ada3668 --- /dev/null +++ b/scripts/troubleshooting/logs/troubleshooting/compose-config.txt @@ -0,0 +1,2 @@ +# docker compose config output +open /home/stefan-mo/WsMeldestelle/Meldestelle/scripts/troubleshooting/docker-compose.yml: no such file or directory diff --git a/services/members/members-service/src/main/kotlin/at/mocode/members/service/config/SecurityConfiguration.kt b/services/members/members-service/src/main/kotlin/at/mocode/members/service/config/SecurityConfiguration.kt new file mode 100644 index 00000000..30d767a9 --- /dev/null +++ b/services/members/members-service/src/main/kotlin/at/mocode/members/service/config/SecurityConfiguration.kt @@ -0,0 +1,34 @@ +package at.mocode.members.service.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.web.SecurityFilterChain + +/** + * Security configuration for the Members Service. + * Enables method-level security for fine-grained authorization control. + */ +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true) +class SecurityConfiguration { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + return http + .csrf { it.disable() } + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } + .authorizeHttpRequests { auth -> + auth + // Allow health check endpoints + .requestMatchers("/actuator/**", "/health/**").permitAll() + // All other endpoints require authentication (handled by method-level security) + .anyRequest().authenticated() + } + .build() + } +}