version 1
This commit is contained in:
18
.env
Normal file
18
.env
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# === Database (used for MariaDB container and Nextcloud install) ===
|
||||||
|
MYSQL_DB=nextcloud
|
||||||
|
MYSQL_USER=ncuser
|
||||||
|
MYSQL_PASSWORD=It9JWMWk0SoQ5T0FcfBljqffAgjXKJ4w
|
||||||
|
|
||||||
|
# === Nextcloud bootstrap admin (used by occ) ===
|
||||||
|
NC_ADMIN_USER=ncimperator
|
||||||
|
NC_ADMIN_PASS=yUFejP1fP3NqPGH9Y80FCKY3Qr8lNy2u
|
||||||
|
|
||||||
|
# === Public domain for trusted_domains & overwrite ===
|
||||||
|
NC_DOMAIN=nextcloud.knusperkerne.de
|
||||||
|
|
||||||
|
MYSQL_HOST=nextcloud-db-v1
|
||||||
|
MYSQL_DATABASE=nextcloud
|
||||||
|
BASE_URL=https://nextcloud.knusperkerne.de
|
||||||
|
|
||||||
|
TZ=Europe/Berlin
|
||||||
|
REDIS_HOST=nextcloud-redis-v1
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.TO_BE_DELETED
|
||||||
116
docker-compose.yml
Normal file
116
docker-compose.yml
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
name: nextcloud_v1
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nextcloud_v1_cloud:
|
||||||
|
driver: bridge
|
||||||
|
enable_ipv6: false
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
nextcloud_app_v1:
|
||||||
|
nextcloud_config_v1:
|
||||||
|
nextcloud_data_v1:
|
||||||
|
nextcloud_db_v1:
|
||||||
|
nextcloud_redis_v1:
|
||||||
|
nextcloud_custom_apps_v1:
|
||||||
|
nextcloud_themes_v1:
|
||||||
|
|
||||||
|
services:
|
||||||
|
nextcloud-db-v1:
|
||||||
|
image: mariadb:10.6
|
||||||
|
container_name: nextcloud-db-v1
|
||||||
|
restart: unless-stopped
|
||||||
|
command:
|
||||||
|
- --transaction-isolation=READ-COMMITTED
|
||||||
|
- --binlog-format=ROW
|
||||||
|
- --skip-log-bin
|
||||||
|
- --innodb_read_only_compressed=OFF
|
||||||
|
environment:
|
||||||
|
TZ: ${TZ}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DB}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
MYSQL_RANDOM_ROOT_PASSWORD: "true"
|
||||||
|
volumes:
|
||||||
|
- nextcloud_db_v1:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- nextcloud_v1_cloud
|
||||||
|
|
||||||
|
nextcloud-redis-v1:
|
||||||
|
image: redis:7.2-alpine
|
||||||
|
container_name: nextcloud-redis-v1
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["redis-server","--appendonly","no"]
|
||||||
|
environment:
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- nextcloud_redis_v1:/data
|
||||||
|
networks:
|
||||||
|
- nextcloud_v1_cloud
|
||||||
|
|
||||||
|
nextcloud-fpm-v1:
|
||||||
|
# image: nextcloud:31.0.2-fpm
|
||||||
|
# image: nextcloud:31.0.10-fpm
|
||||||
|
image: nextcloud:32.0.1-fpm
|
||||||
|
container_name: nextcloud-fpm-v1
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
TZ: ${TZ}
|
||||||
|
MYSQL_HOST: ${MYSQL_HOST}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DB}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
NEXTCLOUD_TRUSTED_DOMAINS: ${NC_DOMAIN}
|
||||||
|
PHP_MEMORY_LIMIT: 1024M
|
||||||
|
PHP_UPLOAD_LIMIT: 10G
|
||||||
|
volumes:
|
||||||
|
- nextcloud_app_v1:/var/www/html
|
||||||
|
- nextcloud_config_v1:/var/www/html/config
|
||||||
|
- nextcloud_data_v1:/var/www/html/data
|
||||||
|
- nextcloud_custom_apps_v1:/var/www/html/custom_apps
|
||||||
|
- nextcloud_themes_v1:/var/www/html/themes
|
||||||
|
depends_on:
|
||||||
|
- nextcloud-db-v1
|
||||||
|
- nextcloud-redis-v1
|
||||||
|
expose:
|
||||||
|
- "9000"
|
||||||
|
networks:
|
||||||
|
- nextcloud_v1_cloud
|
||||||
|
extra_hosts:
|
||||||
|
- "nextcloud.knusperkerne.de:89.58.0.1"
|
||||||
|
|
||||||
|
nextcloud-nginx-v1:
|
||||||
|
image: nginx:1.26-alpine
|
||||||
|
container_name: nextcloud-nginx-v1
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- nextcloud-fpm-v1
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:13005:8080"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
- nextcloud_app_v1:/var/www/html:ro
|
||||||
|
- nextcloud_custom_apps_v1:/var/www/html/custom_apps:ro
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- nextcloud_v1_cloud
|
||||||
|
|
||||||
|
nextcloud-cron-v1:
|
||||||
|
# image: nextcloud:31.0.2-fpm
|
||||||
|
# image: nextcloud:31.0.10-fpm
|
||||||
|
image: nextcloud:32.0.1-fpm
|
||||||
|
container_name: nextcloud-cron-v1
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: /cron.sh
|
||||||
|
environment:
|
||||||
|
TZ: ${TZ}
|
||||||
|
depends_on:
|
||||||
|
- nextcloud-fpm-v1
|
||||||
|
volumes:
|
||||||
|
- nextcloud_app_v1:/var/www/html
|
||||||
|
- nextcloud_config_v1:/var/www/html/config
|
||||||
|
- nextcloud_data_v1:/var/www/html/data
|
||||||
|
- nextcloud_custom_apps_v1:/var/www/html/custom_apps
|
||||||
|
- nextcloud_themes_v1:/var/www/html/themes
|
||||||
|
networks:
|
||||||
|
- nextcloud_v1_cloud
|
||||||
82
nginx/README.md
Normal file
82
nginx/README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Nextcloud NGINX Default Configuration – Documentation
|
||||||
|
This README describes the exact behavior and requirements of the NGINX configuration file `default.conf` used to serve a standalone Nextcloud instance behind a reverse proxy. The documentation is intentionally written so that an AI can regenerate the correct `default.conf` **from this README alone**. The output must always be a working Nextcloud NGINX configuration without deviation.
|
||||||
|
|
||||||
|
## PURPOSE
|
||||||
|
This configuration provides a secure, reverse-proxy-compatible NGINX frontend for Nextcloud with PHP-FPM. It ensures:
|
||||||
|
- Clean routing via `index.php`
|
||||||
|
- No rewrite loops
|
||||||
|
- Correct handling of `.mjs` MIME type
|
||||||
|
- Compatibility with `/ocs` API endpoints
|
||||||
|
- Secure static asset handling and caching
|
||||||
|
- Protection of internal folders
|
||||||
|
- No directory listing
|
||||||
|
- Works behind an existing TLS reverse proxy
|
||||||
|
- Fully functional Dashboard and Apps
|
||||||
|
|
||||||
|
## REQUIRED PROPERTIES
|
||||||
|
A correct generated configuration MUST:
|
||||||
|
- Include exactly ONE `server { ... }` block
|
||||||
|
- Use `listen 8080;`
|
||||||
|
- Use `server_name _;`
|
||||||
|
- Set `root /var/www/html;`
|
||||||
|
- Use `try_files $uri $uri/ /index.php?$request_uri;` for main routing
|
||||||
|
- Use `fastcgi_pass nextcloud-fpm-v1:9000;` for PHP handling
|
||||||
|
- Include PHP entry location for `index.php`, `remote.php`, `public.php`, `ocs.php`, `ocs-provider.php`, `cron.php`
|
||||||
|
- Include a separate `/ocs/` handler BEFORE the PHP blocks
|
||||||
|
- Include MIME fix for `.mjs`
|
||||||
|
- Deny access to `config`, `data`, `lib`, `3rdparty`, etc.
|
||||||
|
- Block generic `.php` execution
|
||||||
|
- Allow static files caching
|
||||||
|
- Work with rewrite base `/`
|
||||||
|
- NOT configure TLS (handled externally)
|
||||||
|
- NOT include proxy headers inside this config
|
||||||
|
- Be valid under `nginx -t`
|
||||||
|
|
||||||
|
## FILE LOCATION REQUIREMENTS
|
||||||
|
- Filename: `default.conf`
|
||||||
|
- Must be mounted into NGINX container as `/etc/nginx/conf.d/default.conf`
|
||||||
|
- File must NOT include other config files via `include`
|
||||||
|
|
||||||
|
## NO AUTO REDIRECTS TO PORT 8080
|
||||||
|
The configuration must not cause browsers to redirect to `https://host:8080`. It must work normally when served over reverse proxy.
|
||||||
|
|
||||||
|
## SECURITY REQUIREMENTS
|
||||||
|
- Deny access to internal folders:
|
||||||
|
`/config`, `/data`, `/templates`, `/tests`, `/lib`, `/build`, `/3rdparty`
|
||||||
|
- Deny access to CLI entrypoints like `/occ`
|
||||||
|
- Disable execution of arbitrary `.php` files
|
||||||
|
- Allow only approved PHP entry scripts
|
||||||
|
|
||||||
|
## MUST-HAVE SECTIONS
|
||||||
|
1. Basic server declaration
|
||||||
|
2. Security headers
|
||||||
|
3. Well-known redirects for CalDAV/CardDAV
|
||||||
|
4. Root route using `try_files`
|
||||||
|
5. Allowed PHP routing
|
||||||
|
6. `/ocs/` API passthrough
|
||||||
|
7. Static file handlers
|
||||||
|
8. `.mjs` MIME type fix
|
||||||
|
|
||||||
|
## ROUTING BEHAVIOR
|
||||||
|
| URL Example | Must Result |
|
||||||
|
|-------------------------------------------|--------------------------------------|
|
||||||
|
| `/apps/dashboard/` | Render dashboard, no 403 |
|
||||||
|
| `/ocs/v2.php/apps/user_status/api/v1/*` | Must NOT return 404 or 500 |
|
||||||
|
| `/remote.php/dav/` | Must work |
|
||||||
|
| `/index.php/...` | Must work |
|
||||||
|
| `/favicon.ico` | 200 or cached |
|
||||||
|
|
||||||
|
## NO CHANGES ALLOWED
|
||||||
|
These things MUST NOT be modified:
|
||||||
|
- No gzip or brotli here
|
||||||
|
- No `proxy_set_header` here
|
||||||
|
- No HTTPS config
|
||||||
|
- No HTTP → HTTPS redirects
|
||||||
|
|
||||||
|
## GUARANTEE
|
||||||
|
If an AI uses ONLY this README as input, the result MUST be a valid, production-ready NGINX `default.conf` for Nextcloud that passes all tests above and prevents:
|
||||||
|
- `rewrite or internal redirection cycle` errors
|
||||||
|
- `403 forbidden` on `/apps/dashboard`
|
||||||
|
- `500` errors on `/ocs/v2.php`
|
||||||
|
- MIME type warnings for `.mjs`
|
||||||
|
- Looping on `/index.php/index.php`
|
||||||
218
nginx/default.conf
Normal file
218
nginx/default.conf
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Nextcloud - nginx (Docker) default.conf
|
||||||
|
# - Single file, no includes, no compose changes
|
||||||
|
# - Upstream: nextcloud-fpm-v1:9000
|
||||||
|
# - Well-known + OCS/OCM fixes
|
||||||
|
# - Collabora prepared (commented)
|
||||||
|
# - No HTTP/2 assumptions (host terminates TLS)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# Upstream PHP-FPM in Docker (keepalive for throughput)
|
||||||
|
upstream php_nextcloud {
|
||||||
|
server nextcloud-fpm-v1:9000 max_fails=3 fail_timeout=5s;
|
||||||
|
keepalive 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upgrade mapping for potential websocket use later (e.g., Collabora)
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
# App-nginx listens only inside the container
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Nextcloud docroot from the mounted volume
|
||||||
|
root /var/www/html;
|
||||||
|
|
||||||
|
# Log to stdout/stderr for `docker logs`
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
|
||||||
|
# --- Security headers (HSTS/OCSP etc. should be handled by host proxy) ---
|
||||||
|
add_header Referrer-Policy "no-referrer" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Download-Options "noopen" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
||||||
|
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
fastcgi_hide_header X-Powered-By;
|
||||||
|
|
||||||
|
# Fix für Nextcloud Self-Tests (PHP curl intern)
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
proxy_set_header X-Forwarded-Ssl on;
|
||||||
|
|
||||||
|
# --- Client / FastCGI tuning ---
|
||||||
|
# Match your PHP env (PHP_UPLOAD_LIMIT=10G)
|
||||||
|
client_max_body_size 10g;
|
||||||
|
fastcgi_buffers 64 4k;
|
||||||
|
fastcgi_read_timeout 3600s;
|
||||||
|
fastcgi_send_timeout 3600s;
|
||||||
|
fastcgi_request_buffering off;
|
||||||
|
gzip off; # per Nextcloud recommendation (preserve ETag)
|
||||||
|
|
||||||
|
# --- Docker internal resolver for self-connect checks ---
|
||||||
|
resolver 127.0.0.11 valid=30s;
|
||||||
|
resolver_timeout 5s;
|
||||||
|
|
||||||
|
# --- Real client IP from host reverse proxy ---
|
||||||
|
set_real_ip_from 0.0.0.0/0;
|
||||||
|
real_ip_header X-Forwarded-For;
|
||||||
|
|
||||||
|
# --- Minimal type additions for modern JS & sourcemaps ---
|
||||||
|
# (keine globalen mime.types nötig – wir ergänzen nur, was gebraucht wird)
|
||||||
|
types {
|
||||||
|
font/otf otf;
|
||||||
|
text/html html htm shtml;
|
||||||
|
text/css css;
|
||||||
|
text/javascript js;
|
||||||
|
application/javascript mjs;
|
||||||
|
application/json json;
|
||||||
|
application/manifest+json webmanifest;
|
||||||
|
application/wasm wasm;
|
||||||
|
application/octet-stream map;
|
||||||
|
font/woff woff;
|
||||||
|
font/woff2 woff2;
|
||||||
|
image/svg+xml svg;
|
||||||
|
image/png png;
|
||||||
|
image/jpeg jpg jpeg;
|
||||||
|
image/gif gif;
|
||||||
|
text/plain txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Serve .mjs and .js.map correctly (fixes admin checks) ---
|
||||||
|
location ~* \.mjs$ {
|
||||||
|
default_type application/javascript;
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
access_log off;
|
||||||
|
expires 1d;
|
||||||
|
}
|
||||||
|
location ~* \.js\.map$ {
|
||||||
|
default_type application/octet-stream;
|
||||||
|
try_files $uri =404;
|
||||||
|
access_log off;
|
||||||
|
expires 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- robots.txt ---
|
||||||
|
location = /robots.txt {
|
||||||
|
allow all;
|
||||||
|
log_not_found off;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- .well-known handling (webfinger + cal/carddav + generic) ---
|
||||||
|
location ~ ^/\.well-known/(acme-challenge/.*)$ { allow all; }
|
||||||
|
location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
|
||||||
|
location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }
|
||||||
|
location ^~ /.well-known { return 301 /index.php$uri; }
|
||||||
|
location = /.well-known/webfinger { return 301 /index.php$uri; }
|
||||||
|
|
||||||
|
# --- Deny sensitive dirs, as per Nextcloud docs ---
|
||||||
|
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ { deny all; }
|
||||||
|
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { deny all; }
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# OCS/OCM PROVIDER FIXES
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# 1) Some NC checks expect /ocs-provider/ to return a valid JSON.
|
||||||
|
# We provide a tiny static JSON (no PHP needed), satisfying the check.
|
||||||
|
location = /ocs-provider/ {
|
||||||
|
default_type application/json;
|
||||||
|
return 200 '{"version":"1.0","status":"ok"}';
|
||||||
|
}
|
||||||
|
location = /ocs-provider/index.php {
|
||||||
|
default_type application/json;
|
||||||
|
return 200 '{"version":"1.0","status":"ok"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2) Ensure ocs/ocm provider paths resolve to index.php for dynamic routes.
|
||||||
|
location ~ ^/(?:ocs-provider|ocm-provider)(?:$|/) {
|
||||||
|
try_files $uri /index.php$uri$is_args$args;
|
||||||
|
}
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# --- Main entry: route everything through front controller ---
|
||||||
|
location / {
|
||||||
|
rewrite ^ /index.php;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- PHP handling: main Nextcloud endpoints ---
|
||||||
|
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|oc[sm]-provider/.+|core/templates/40[34])\.php(?:$|/) {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||||
|
try_files $fastcgi_script_name =404;
|
||||||
|
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
|
||||||
|
# avoid duplicate security headers
|
||||||
|
fastcgi_param modHeadersAvailable true;
|
||||||
|
fastcgi_param front_controller_active true;
|
||||||
|
|
||||||
|
fastcgi_pass php_nextcloud;
|
||||||
|
fastcgi_intercept_errors on;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /apps/ {
|
||||||
|
try_files $uri /index.php$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Static assets caching (below PHP block) ---
|
||||||
|
location ~* \.(?:css|js)$ {
|
||||||
|
try_files $uri /index.php$uri$is_args$args;
|
||||||
|
add_header Cache-Control "public, max-age=7200";
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
access_log off;
|
||||||
|
expires 2h;
|
||||||
|
}
|
||||||
|
location ~ \.woff2?$ {
|
||||||
|
try_files $uri /index.php$request_uri;
|
||||||
|
expires 7d;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
location ~* \.(?:svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$ {
|
||||||
|
try_files $uri /index.php$uri$is_args$args;
|
||||||
|
access_log off;
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Collabora (prepared; keep commented until subdomain is live)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# location ^~ /loleaflet {
|
||||||
|
# proxy_pass https://collabora.knusperkerne.de;
|
||||||
|
# proxy_set_header Host $http_host;
|
||||||
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
# proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
# proxy_set_header Upgrade $http_upgrade;
|
||||||
|
# proxy_set_header Connection $connection_upgrade;
|
||||||
|
# proxy_read_timeout 3600s;
|
||||||
|
# proxy_buffering off;
|
||||||
|
# }
|
||||||
|
# location ^~ /lool {
|
||||||
|
# proxy_pass https://collabora.knusperkerne.de;
|
||||||
|
# proxy_set_header Host $http_host;
|
||||||
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
# proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
# proxy_set_header Upgrade $http_upgrade;
|
||||||
|
# proxy_set_header Connection $connection_upgrade;
|
||||||
|
# proxy_http_version 1.1;
|
||||||
|
# proxy_read_timeout 3600s;
|
||||||
|
# proxy_buffering off;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Simple health endpoint
|
||||||
|
location = /health {
|
||||||
|
return 200 'ok';
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user