diff --git a/docker-compose.yml b/docker-compose.yml index 4e2e2cc..fe5ea7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -91,7 +91,7 @@ services: - backups cron: - image: nextcloud:apache + image: nextcloud:32-apache restart: always volumes: - nextcloud:/var/www/html @@ -99,6 +99,12 @@ services: depends_on: - db - redis + environment: + - MYSQL_HOST=db + - REDIS_HOST=redis + networks: + - backups + - default proxy: build: ./proxy @@ -114,11 +120,27 @@ services: - vhost.d:/etc/nginx/vhost.d - html:/usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro + - ./proxy/conf/forwarded_headers.conf:/etc/nginx/conf.d/forwarded_headers.conf:ro environment: - ENABLE_IPV6=true networks: - proxy-tier + forwarded-for-headers-init: + image: nextcloud:${NEXTCLOUD_VERSION} + depends_on: + - app + volumes: + - nextcloud:/var/www/html + - ./scripts/forwarded-for-headers-init.sh:/usr/local/bin/nextcloud-init.sh:ro + entrypoint: ["/bin/bash","/usr/local/bin/nextcloud-init.sh"] + restart: no + networks: + - proxy-tier + - default + - backups + + letsencrypt-companion: image: nginxproxy/acme-companion restart: always @@ -146,4 +168,4 @@ volumes: networks: proxy-tier: - backups: + backups:i diff --git a/proxy/conf/forwarded_headers.conf b/proxy/conf/forwarded_headers.conf new file mode 100644 index 0000000..8c7a500 --- /dev/null +++ b/proxy/conf/forwarded_headers.conf @@ -0,0 +1,10 @@ +# Ensure NGINX sets the real client IP headers itself and does NOT pass through +# client-controlled X-Forwarded-For values. Using $remote_addr prevents header +# spoofing by clients. This file should be placed in /etc/nginx/conf.d so it's +# included by the nginx configuration. +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +# Avoid appending client-supplied X-Forwarded-For. Set it to the connecting IP. +proxy_set_header X-Forwarded-For $remote_addr; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-Host $host; diff --git a/scripts/forwarded-for-headers-init.sh b/scripts/forwarded-for-headers-init.sh new file mode 100644 index 0000000..9a83e22 --- /dev/null +++ b/scripts/forwarded-for-headers-init.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -euo pipefail + +log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; } + +# Maximum time to wait for Nextcloud/occ to become available (seconds) +MAX_WAIT=300 +SLEEP_INTERVAL=5 +WAITED=0 + +log "nextcloud-init: waiting for Nextcloud (occ) to become available..." +while ! php /var/www/html/occ status >/dev/null 2>&1; do + sleep $SLEEP_INTERVAL + WAITED=$((WAITED + SLEEP_INTERVAL)) + if [ "$WAITED" -ge "$MAX_WAIT" ]; then + log "timeout waiting for Nextcloud occ to be available after ${MAX_WAIT}s" + exit 1 + fi +done +log "occ is available" + +# Resolve proxy IP. If you want to override detection, supply PROXY_IP env var. +proxy_ip="" +if [ -n "${PROXY_IP:-}" ]; then + proxy_ip="$PROXY_IP" + log "Using PROXY_IP from environment: $proxy_ip" +else + # Resolve the 'proxy' service hostname on the proxy-tier network + proxy_ip=$(getent hosts proxy | awk '{print $1}' || true) + if [ -n "$proxy_ip" ]; then + log "Resolved proxy service IP: $proxy_ip" + else + log "Could not resolve 'proxy' hostname. You may set PROXY_IP environment variable in docker-compose if automatic discovery fails." + exit 1 + fi +fi + +# Function to run occ commands and capture output +occ() { + php /var/www/html/occ "$@" +} + +# Configure trusted_proxies (append if not present) +existing_trusted=$(occ config:system:get trusted_proxies 2>/dev/null || true) +if echo "$existing_trusted" | grep -F "$proxy_ip" >/dev/null 2>&1; then + log "Proxy IP $proxy_ip already present in trusted_proxies; skipping add." +else + # If no existing value, set to array with proxy_ip; otherwise set to existing + proxy_ip. + if [ -z "$existing_trusted" ]; then + log "Setting trusted_proxies to [\"$proxy_ip\"]" + occ config:system:set trusted_proxies --value="[\"$proxy_ip\"]" --type=array + else + # Best-effort: attempt to preserve existing values by reading them and creating a merged JSON array. + # existing_trusted sometimes prints a human-readable array; try to extract lines with IPs. + ips=$(echo "$existing_trusted" | sed -n 's/^[[:space:]]*-\s*\(.*\)$/\1/p' | tr '\n' ',' | sed 's/,$//') + # If extraction failed, fall back to set only the proxy IP (safer than leaving proxy untrusted). + if [ -z "$ips" ]; then + log "Could not parse existing trusted_proxies reliably; setting to [\"$proxy_ip\"] to ensure proxy is trusted." + occ config:system:set trusted_proxies --value="[\"$proxy_ip\"]" --type=array + else + # Build JSON array merging existing IPs and proxy_ip if missing + # Note: this is simple textual merging; for complex existing values adjust manually. + merged="[" + IFS=',' read -r -a arr <<< "$ips" + for v in "${arr[@]}"; do + v_trim=$(echo "$v" | sed 's/^[[:space:]\"]*//;s/[[:space:]\"]*$//') + [ -n "$v_trim" ] && merged="$merged\"$v_trim\"," + done + # Add the detected proxy IP unless present + if ! echo "$merged" | grep -F "\"$proxy_ip\"" >/dev/null 2>&1; then + merged="$merged\"$proxy_ip\"," + fi + merged="${merged%,}]" + log "Setting trusted_proxies to $merged" + occ config:system:set trusted_proxies --value="$merged" --type=array + fi + fi +fi + +# Restrict forwarded headers to a safe subset (X-Forwarded-For) +log "Setting forwarded_for_headers to [\"HTTP_X_FORWARDED_FOR\"]" +occ config:system:set forwarded_for_headers --value='["HTTP_X_FORWARDED_FOR"]' --type=array + +log "nextcloud-init: finished successfully"