nginx-reverse-proxynextcloud

Nextcloud Office can't connect to CODE behind reverse proxy: Requesting address is denied


I'm having a lot of trouble with setting up the docker image: collabora/code:latest behind my reverse proxy. My nextcloud instance (which has a similar setup and is working fine) can't connect to the server. However inside admin settings of Nextcloud Office I get Collabora Online server is reachable. when I connect through https://code.foo.tld. The first docker log shows the error I get inside my code docker container. If I try to create/open any files with Nextcloud Office I just get the error: Document loading failed - Failed to load Nextcloud Office - please try again later. I already did a lot of research and found multiple related topics stating exactly the same problem. Most of them however are multiple years old and I couldn't find a solution that worked for me, which is why I am opening a new topic on this. All relevant logs, configs and docker files should be listed below. If you have any additional questions, feel free to let me know! I would be glad if someone could help me with this because I've been struggling setting it up for 2 days now!

I can reach https://code.foo.tld/hosting/discovery and https://code.foo.tld/hosting/capabilities, but https://code.foo.tld/cool or https://code.foo.tld/cool/adminws show me a blank page (it is reachable tho). https://code.foo.tld shows me a 404 as expected.

Unnecessary/Private information in the following snippets is either left out or replaced with random words/letters.

docker log (code)

Ready to accept connections on port 9980.
dateTtime.num0Z
wsd-num1-num2 date time.num3 +0000 [ websrv_poll ] WRN  convert-to: Requesting address is denied: z.z.z.z| wsd/COOLWSD.cpp:3507
wsd-num1-num1 date time.num4 +0000 [ coolwsd ] WRN  Waking up dead poll thread [HttpSynReqPoll], started: false, finished: false| net/Socket.hpp:727

docker-compose.yml

version: "3.9"

services:
  reverse-proxy:
    image: "nginx:stable-alpine"
    container_name: "reverse-proxy"
    networks:
      frontend:
        ipv4_address: "x.x.x.x"
      backend:
        ipv4_address: "y.y.y.y"
    hostname: "reverse-proxy"
    "..."
  nextcloud-webserver:
    image: "nginx:stable-alpine"
    "..."
  nextcloud:
    image: "nextcloud:stable-fpm-alpine"
    "..."
    networks:
      frontend:
        ipv4_address: "x.x.x.x"
      backend:
        ipv4_address: "y.y.y.y"
    "..."
  nextcloud-database:
    image: "yobasystems/alpine-mariadb"
    "..."
  code-web:
    image: "nginx:stable-alpine"
    container_name: "code-web"
    networks:
      backend:
        ipv4_address: "y.y.y.y"
    hostname: "code-web"
    depends_on:
      - "code"
    "..."
  code:
    image: "collabora/code:latest"
    container_name: "code"
    networks:
      frontend:
        ipv4_address: "x.x.x.x"
      backend:
        ipv4_address: "y.y.y.y"
    hostname: "code"
    restart: "always"
    env_file:
      - "~/dock/code/code.env"

networks:
  frontend:
    internal: false
    ipam:
      config:
        - subnet: "x.x.x.x/x"
          gateway: "x.x.x.x"
  backend:
    internal: true
    ipam:
      config:
        - subnet: "y.y.y.y/y"
          gateway: "y.y.y.y"

~/dock/code/code.env

aliasgroup1="https://nextcloud.foo.tld:443"
DONT_GEN_SSL_CERT="true"
extra_params="--o:ssl.enable=false --o:ssl.termination=true"
password='password'
server_name="hostname-app"
username="username"

These are the relevant nginx configs. A lot of stuff is left out and any include statements are replaced with the files that they include.

reverse-proxy.conf

# https://nginx.org/en/docs/ngx_core_module.html#worker_processes
worker_processes auto;
# https://nginx.org/en/docs/ngx_core_module.html#error_log
error_log /var/log/nginx/error.log;
# https://nginx.org/en/docs/ngx_core_module.html#pid
pid /run/nginx.pid;
# https://nginx.org/en/docs/ngx_core_module.html#include
include /usr/share/nginx/modules/*.conf;

# https://nginx.org/en/docs/ngx_core_module.html#events
events {
    # https://nginx.org/en/docs/ngx_core_module.html#worker_connections
    worker_connections 1024;
}

# https://nginx.org/en/docs/http/ngx_http_core_module.html#http
http {
    # https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
    access_log /var/log/nginx/access.log combined;
    # https://nginx.org/en/docs/ngx_core_module.html#include
    include /etc/nginx/mime.types;
    # https://nginx.org/en/docs/http/ngx_http_core_module.html#default_type
    default_type application/octet-stream;
    # https://nginx.org/en/docs/http/ngx_http_core_module.html#server
    server {
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols
        ssl_protocols TLSv1.3;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers
        ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM';
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ecdh_curve
        ssl_ecdh_curve secp384r1;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers
        ssl_prefer_server_ciphers on;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
        ssl_dhparam /etc/nginx/certs/dhparam.pem;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
        ssl_session_cache shared:SSL:10m;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_timeout
        ssl_session_timeout 10m;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_tickets
        ssl_session_tickets off;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling
        ssl_stapling on;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling_verify
        ssl_stapling_verify on;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client
        ssl_verify_client on;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate
        ssl_client_certificate /etc/nginx/certs/authenticated_origin_pull_ca.pem;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate
        ssl_trusted_certificate /etc/nginx/certs/origin_ca_ecc_root.pem;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate
        ssl_certificate /etc/nginx/certs/foo.tld.pem;
        # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key
        ssl_certificate_key /etc/nginx/certs/foo.tld.key;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
        server_name code.foo.tld;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location / {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version
            proxy_http_version 1.1;
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_bypass
            proxy_cache_bypass $http_upgrade;
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
            proxy_read_timeout 90;
            # https://docs.oracle.com/en-us/iaas/Content/Balance/Reference/httpheaders.htm
            proxy_set_header X-Real-IP $remote_addr;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
            proxy_set_header X-Forwarded-Proto $scheme;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $host;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
            proxy_set_header X-Forwarded-Host $host;
            # https://docs.oracle.com/en-us/iaas/Content/Balance/Reference/httpheaders.htm
            proxy_set_header X-Forwarded-Port $server_port;
            # https://nginx.org/en/docs/http/websocket.html
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade
            proxy_set_header Upgrade $http_upgrade;
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
            proxy_set_header Connection 'upgrade';
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-web:80/;
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect
            proxy_redirect http://hostname-web:80 https://code.foo.tld;
        }
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
        add_header Strict-Transport-Security "max-age=15780000; includeSubDomains; preload" always;
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
        add_header X-Frame-Options SAMEORIGIN;
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
        add_header X-XSS-Protection "1; mode=block";
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
        add_header X-Content-Type-Options nosniff;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens
        server_tokens off;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip
        gzip off;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#sendfile
        sendfile on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nopush
        tcp_nopush on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nodelay
        tcp_nodelay on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
        keepalive_timeout 65;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#types_hash_max_size
        types_hash_max_size 4096;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
        client_max_body_size 0;
    }
    # https://nginx.org/en/docs/http/ngx_http_core_module.html#server
    server {
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
        listen 80;
        listen [::]:80;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
        server_name _;
        # https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#return
        return 301 https://$host$request_uri;
    }
}

web-server.conf

# https://nginx.org/en/docs/ngx_core_module.html#worker_processes
worker_processes auto;
# https://nginx.org/en/docs/ngx_core_module.html#error_log
error_log /var/log/nginx/error.log;
# https://nginx.org/en/docs/ngx_core_module.html#pid
pid /run/nginx.pid;
# https://nginx.org/en/docs/ngx_core_module.html#include
include /usr/share/nginx/modules/*.conf;

# https://nginx.org/en/docs/ngx_core_module.html#events
events {
    # https://nginx.org/en/docs/ngx_core_module.html#worker_connections
    worker_connections 1024;
}

# https://nginx.org/en/docs/http/ngx_http_core_module.html#http
http {
    # https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
    access_log /var/log/nginx/access.log combined;
    # https://nginx.org/en/docs/ngx_core_module.html#include
    include /etc/nginx/mime.types;
    # https://nginx.org/en/docs/http/ngx_http_core_module.html#default_type
    default_type application/octet-stream;
    #https://sdk.collaboraonline.com/docs/installation/Proxy_settings.html#reverse-proxy-settings-in-nginx-config-ssl-termination
    ########## START collabora ##########
    # https://nginx.org/en/docs/http/ngx_http_upstream_module.html#server
    server {
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
        listen 80 default_server;
        listen [::]:80 default_server;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
        server_name sub.foo.tld;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        ## STATIC FILES ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /browser {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
        }
        ## WOPI DISCOVERY URL ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /hosting/discovery {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
        }
        ## CAPABILITIES ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /hosting/capabilities {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
        }
        ## DOWNLOAD, PRESENTATION & IMAGE UPLOAD ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ~ ^/(c|l)ool {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
        }
        ## MAIN WEBSOCKET ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ~ ^/cool/(.*)/ws$ {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
            # https://nginx.org/en/docs/http/websocket.html
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade
            proxy_set_header Upgrade $http_upgrade;
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
            proxy_set_header Connection "Upgrade";
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
            proxy_read_timeout 36000s;
        }
        ## ADMIN CONSOLE WEBSOCKET ##
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /cool/adminws {
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
            proxy_pass http://hostname-app:9980;
            # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host
            proxy_set_header Host $http_host;
            # https://nginx.org/en/docs/http/websocket.html
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade
            proxy_set_header Upgrade $http_upgrade;
            ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
            proxy_set_header Connection "Upgrade";
            # https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
            proxy_read_timeout 36000s;
        }
    ########## END collabora ##########
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
        add_header X-Frame-Options SAMEORIGIN;
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
        add_header X-XSS-Protection "1; mode=block";
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
        add_header X-Content-Type-Options nosniff;
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
        add_header Referrer-Policy "no-referrer";
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
        add_header X-Download-Options "noopen";
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
        add_header X-Permitted-Cross-Domain-Policies none;
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
        add_header X-Robots-Tag none;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens
        server_tokens off;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip
        gzip on;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_vary
        gzip_vary on;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_comp_level
        gzip_comp_level 4;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_min_length
        gzip_min_length 256;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_proxied
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        # https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_types
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#sendfile
        sendfile on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nopush
        tcp_nopush on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nodelay
        tcp_nodelay on;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
        keepalive_timeout 65;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#types_hash_max_size
        types_hash_max_size 4096;
        # https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
        client_max_body_size 512M;
        # https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffers
        fastcgi_buffers 64 4K;
        # https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_hide_header
        fastcgi_hide_header X-Powered-By;
    }
}

Note: I already asked this question on the Collabora Online Forum , but I don't know if it's actually active enough or will help me in any reasonable time.

I looked at following articles:

and some more, but I'd assume that they are outdated/don't apply to my setup because nothing I've tried worked!


Solution

  • So I kind of found the issue and multiple fixes to it.

    The problem is that the Collabora and Nextcloud-Servers are being proxied by Cloudflare. Specifying the Nextcloud-Server in aliasgroup1 therefore doesn't allow the actual requesting IP.

    Meaning

    a1.a2.a3.a4 maps to example.com, which is listed in aliasgroup1.

    The outgoing requests from that server are coming from b1.b2.b3.b4, which is not mapped to example.com, and is also not allowed to make WOPI requests, just like any Cloudflare IPs aren't allowed, except the one that maps to example.com.

    Probably insecure solution

    This, especially configuring the Allow list for WOPI requests, did solve my problem.

    Just adding all Cloudflare IP-Ranges to Allow list for WOPI requests in the Nextcloud-Web-UI fixed the problem for me. I am not sure if this is secure (at all).

    @Red's answer (stackoverflow.com) is still kind of correct and would work for static IPs. And I would consider it (more) secure depending on the resulting Allow list for WOPI requests in the Nextcloud-Web-UI.

    A more secure solution would be following this

    Things you have to change regardless of Full (strict) Cloudflare ssl configuration

    As stated here, you would then specify in the Allow list for WOPI requests, which can be found in the Nextcloud-Web-UI:

    <Docker-hostname-that-both-Nextcloud-and-Collabora-share-as-IPv4,Docker-hostname-that-both-Nextcloud-and-Collabora-share-as-IPv6>
    

    So for example:

    10.1.0.1/16,fe80::.../64
    

    You will also have to select Disable certificate verification (insecure). Since requests will be made locally, this shouldn't be a problem.

    Things to consider when using Full (strict) Cloudflare ssl configuration - Adding a local nginx server block

    The local server block can't contain the following:

    server {
        ...
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_verify_client on;
        ssl_client_certificate [...];
        ssl_trusted_certificate [...];
        ...
    }
    

    Your Cloudflare configuration can stay the same, since requests from Nextcloud to Collabora and vice versa will be made locally

    Other things I have changed for the local server block:

    server {
        listen 127.0.0.1:443 ssl;
        listen [::1]:443 ssl;
        listen <Docker-hostname-that-both-Nextcloud-and-Collabora-share>:443 ssl;
        ...
    }
    

    Other than that both the local and the public server blocks are exactly the same.

    The public server block however only listens on the following:

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ...
    }
    

    You might have to add the following to other server blocks that aren't your public Collabora or Nextcloud blocks if they are also on <Docker-hostname-that-both-Nextcloud-and-Collabora-share>:

    server {
        ...
        listen <Docker-hostname-that-both-Nextcloud-and-Collabora-share>:443 ssl;
        ...
    }
    

    Things to consider when using Podman

    This also works if all the services concerned are in the same Podman pod, and you are using Podman, NOT Docker:

    You would then have to specify as flags for podman pod create:

    --network-alias=collabora.example.com --network-alias=nextcloud.example.com
    

    I also removed the extra Web-Server from my original configuration and just moved it to the reverse proxy for anyone trying to follow that.

    NOTE: I did a lot of edits and hope that this is finally done and at least kind of well-structured. NOTE2: Read your edits before submitting :)