commit ab278da5fef7902731bb5760eb0c4a974c351d31 Author: elpatron Date: Sat Feb 14 11:19:07 2026 +0100 Add nginx reverse proxy with Let's Encrypt SSL - nginx as reverse proxy for dawarich frontend - Let's Encrypt certificate support with automatic renewal - HTTP to HTTPS redirect, gzip compression - Bootstrap config for initial certificate request Co-authored-by: Cursor diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..24370ee --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# E-Mail fuer Let's Encrypt Zertifikatsanforderung (erforderlich fuer SSL) +CERTBOT_EMAIL=elpatron@mailbox.org diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a548c42 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,179 @@ +networks: + dawarich: + frontend: + external: true +services: + dawarich_redis: + image: redis:7.0-alpine + container_name: dawarich_redis + command: redis-server + networks: + - dawarich + volumes: + - dawarich_shared:/data + restart: always + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 10s + retries: 10 + start_period: 30s + timeout: 10s + dawarich_db: + image: postgis/postgis:14-3.5-alpine + shm_size: 1G + container_name: dawarich_db + volumes: + - dawarich_db_data:/var/lib/postgresql/data + - dawarich_shared:/var/shared + # - ./postgresql.conf:/etc/postgresql/postgresql.conf # Optional, uncomment if you want to use a custom config + networks: + - dawarich + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + restart: always + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres -d dawarich_development" ] + interval: 10s + retries: 30 + start_period: 30s + timeout: 30s + # command: postgres -c config_file=/etc/postgresql/postgresql.conf # Use custom config, uncomment if you want to use a custom config + nginx: + build: ./nginx + container_name: dawarich_nginx + ports: + - "80:80" + - "443:443" + volumes: + - certbot_etc:/etc/letsencrypt + - certbot_www:/var/www/certbot + networks: + - dawarich + environment: + CERTBOT_EMAIL: ${CERTBOT_EMAIL:-} + depends_on: + dawarich_app: + condition: service_healthy + restart: always + dawarich_app: + image: freikin/dawarich:latest + container_name: dawarich_app + volumes: + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + - dawarich_storage:/var/app/storage + networks: + - dawarich + # Port 3000 nicht exponiert - Traffic laeuft ueber nginx Reverse Proxy + # - 9394:9394 # Prometheus exporter, uncomment if needed + stdin_open: true + tty: true + entrypoint: web-entrypoint.sh + command: ['bin/rails', 'server', '-p', '3000', '-b', '::'] + restart: on-failure + environment: + RAILS_ENV: development + REDIS_URL: redis://dawarich_redis:6379 + DATABASE_HOST: dawarich_db + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + DATABASE_NAME: dawarich_development + MIN_MINUTES_SPENT_IN_CITY: 60 + APPLICATION_HOSTS: "location.butenostfreesen.de,127.0.0.1" + TIME_ZONE: Europe/Berlin + APPLICATION_PROTOCOL: httpss + DISTANCE_UNIT: km + PROMETHEUS_EXPORTER_ENABLED: false + PROMETHEUS_EXPORTER_HOST: 0.0.0.0 + PROMETHEUS_EXPORTER_PORT: 9394 + ENABLE_TELEMETRY: false # More on telemetry: https://dawarich.app/docs/tutorials/telemetry + SELF_HOSTED: "true" + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "5" + healthcheck: + test: [ "CMD-SHELL", "wget -qO - http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"\\s*:\\s*\"ok\"'" ] + interval: 10s + retries: 30 + start_period: 30s + timeout: 10s + depends_on: + dawarich_db: + condition: service_healthy + restart: true + dawarich_redis: + condition: service_healthy + restart: true + deploy: + resources: + limits: + cpus: '0.50' # Limit CPU usage to 50% of one core + memory: '4G' # Limit memory usage to 4GB + dawarich_sidekiq: + image: freikin/dawarich:latest + container_name: dawarich_sidekiq + volumes: + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + - dawarich_storage:/var/app/storage + networks: + - dawarich + stdin_open: true + tty: true + entrypoint: sidekiq-entrypoint.sh + command: ['sidekiq'] + restart: on-failure + environment: + RAILS_ENV: development + REDIS_URL: redis://dawarich_redis:6379 + DATABASE_HOST: dawarich_db + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + DATABASE_NAME: dawarich_development + APPLICATION_HOSTS: "location.butenostfreesen.de,127.0.0.1" + BACKGROUND_PROCESSING_CONCURRENCY: 10 + APPLICATION_PROTOCOL: httpss + DISTANCE_UNIT: km + PROMETHEUS_EXPORTER_ENABLED: false + PROMETHEUS_EXPORTER_HOST: dawarich_app + PROMETHEUS_EXPORTER_PORT: 9394 + ENABLE_TELEMETRY: false # More on telemetry: https://dawarich.app/docs/tutorials/telemetry + SELF_HOSTED: "true" + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "5" + healthcheck: + test: [ "CMD-SHELL", "bundle exec sidekiqmon processes | grep $${HOSTNAME}" ] + interval: 10s + retries: 30 + start_period: 30s + timeout: 10s + depends_on: + dawarich_db: + condition: service_healthy + restart: true + dawarich_redis: + condition: service_healthy + restart: true + dawarich_app: + condition: service_healthy + restart: true + deploy: + resources: + limits: + cpus: '0.50' # Limit CPU usage to 50% of one core + memory: '4G' # Limit memory usage to 4GB + +volumes: + dawarich_db_data: + dawarich_shared: + dawarich_public: + dawarich_watched: + dawarich_storage: + certbot_etc: + certbot_www: \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..0b514a1 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine +RUN apk add --no-cache certbot +COPY nginx.conf /etc/nginx/templates/nginx.conf +COPY nginx-bootstrap.conf /etc/nginx/templates/nginx-bootstrap.conf +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] diff --git a/nginx/entrypoint.sh b/nginx/entrypoint.sh new file mode 100644 index 0000000..393fb82 --- /dev/null +++ b/nginx/entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/sh +set -e + +DOMAIN="location.butenostfreesen.de" +EMAIL="${CERTBOT_EMAIL:-}" + +# Entferne Standard-Config +rm -f /etc/nginx/conf.d/default.conf + +# Bootstrap-Config verwenden, wenn Zertifikate noch nicht existieren +if [ ! -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then + echo "Zertifikate nicht gefunden. Starte mit Bootstrap-Config..." + cp /etc/nginx/templates/nginx-bootstrap.conf /etc/nginx/conf.d/default.conf + nginx -g "daemon off;" & + NGINX_PID=$! + + # Warte auf Backend + echo "Warte auf Backend..." + sleep 10 + + if [ -n "$EMAIL" ]; then + echo "Fordere Let's Encrypt Zertifikat an..." + if certbot certonly --webroot -w /var/www/certbot \ + --email "$EMAIL" \ + --agree-tos \ + --no-eff-email \ + --non-interactive \ + -d "$DOMAIN"; then + echo "Zertifikat erfolgreich erstellt. Starte nginx mit HTTPS..." + kill $NGINX_PID 2>/dev/null || true + sleep 2 + cp /etc/nginx/templates/nginx.conf /etc/nginx/conf.d/default.conf + else + echo "Zertifikatsanforderung fehlgeschlagen. Laeufe mit HTTP (Port 80)." + echo "Stelle sicher, dass location.butenostfreesen.de auf diesen Server zeigt." + exec wait $NGINX_PID + fi + else + echo "HINWEIS: CERTBOT_EMAIL nicht gesetzt. Starte ohne SSL." + echo "Fuer Let's Encrypt: CERTBOT_EMAIL=deine@email.de setzen und Container neu starten." + exec wait $NGINX_PID + fi +else + cp /etc/nginx/templates/nginx.conf /etc/nginx/conf.d/default.conf +fi + +# Zertifikatserneuerung alle 12 Stunden im Hintergrund +(while true; do sleep 12h; certbot renew --webroot -w /var/www/certbot --deploy-hook "nginx -s reload" 2>&1 | tee -a /var/log/certbot-renew.log; done) & + +exec nginx -g "daemon off;" diff --git a/nginx/nginx-bootstrap.conf b/nginx/nginx-bootstrap.conf new file mode 100644 index 0000000..8060c17 --- /dev/null +++ b/nginx/nginx-bootstrap.conf @@ -0,0 +1,19 @@ +# Bootstrap-Konfiguration für initiale Zertifikatsanforderung (ohne SSL) +server { + listen 80; + listen [::]:80; + server_name location.butenostfreesen.de; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://dawarich_app:3000/; + } +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..991a9bc --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,72 @@ +server { + listen 80; + listen [::]:80; + server_name location.butenostfreesen.de; + + # ACME-Challenge für Let's Encrypt Zertifikatserneuerung + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name location.butenostfreesen.de; + + # Let's Encrypt Zertifikate + ssl_certificate /etc/letsencrypt/live/location.butenostfreesen.de/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/location.butenostfreesen.de/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/css + text/plain + text/xml + text/x-component + text/javascript + application/x-javascript + application/javascript + application/json + application/manifest+json + application/vnd.api+json + application/xml + application/xhtml+xml + application/rss+xml + application/atom+xml + application/vnd.ms-fontobject + application/x-font-ttf + application/x-font-opentype + application/x-font-truetype + image/svg+xml + image/x-icon + image/vnd.microsoft.icon + font/ttf + font/eot + font/otf + font/opentype; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header Host $http_host; + proxy_redirect off; + + proxy_pass http://dawarich_app:3000/; + } +}