nginx/Collabora behind nginx reverse proxy

Let me try to describe my setup first

Nextcloud 18.03,

In front I’m running a reverse nginx proxy. On the backend I have two other VMs – nginx/nextcloud and nginx/collabora. The frontend reverse proxy really only proxies for the nginx/nextcloud installation. I don’t have any entries for the nginx/collabora on the front-end reverse proxy.

My frontend reverse proxy is known as test.example.com
Nginx/nextcloud is known as nextcloud.example.com
Nginx/collabora (docker version) is known as office.example.com

If I access nginx/nextcloud directly (without the front-end reverse proxy), all things work as expected. Within the nextcloud settings for collabora I set the the address as https://office.example.com. I’m able to open documents and view contents/edit.

My problem is when I introduce the reverse proxy. When going through the reverse proxy I am able to proxy to nextcloud.example.com. When selecting a document however I’ll see the collabora “frame” open but then I’ll receive a message:

The docker logs of collabora reveal the following:

wsd-00017-00110 2020-04-14 19:55:12.898583 [ docbroker_011 ] ERR  Cannot get file info from WOPI storage uri [https://test.example.com/index.php/apps/richdocuments/wopi/files/25_ocny42d5quk3?access_token=RScM8r0KN9bPGdnKNK6mjHddfZUKrZZO&access_token_ttl=0&permission=edit]. Error: SSL Exception: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version| wsd/Storage.cpp:504
wsd-00017-00110 2020-04-14 19:55:12.898851 [ docbroker_011 ] ERR  loading document exception: SSL Exception| wsd/DocumentBroker.cpp:1158
wsd-00017-00110 2020-04-14 19:55:12.898986 [ docbroker_011 ] ERR  Failed to add session to [/index.php/apps/richdocuments/wopi/files/25_ocny42d5quk3] with URI [https://test.gohilton.com/index.php/apps/richdocuments/wopi/files/25_ocny42d5quk3?access_token=RScM8r0KN9bPGdnKNK6mjHddfZUKrZZO&access_token_ttl=0&permission=edit]: SSL Exception| wsd/DocumentBroker.cpp:1120
wsd-00017-00110 2020-04-14 19:55:12.899151 [ docbroker_011 ] ERR  Error while loading : SSL Exception| wsd/LOOLWSD.cpp:2703
wsd-00017-00110 2020-04-14 19:55:13.481007 [ docbroker_011 ] WRN  Child session [0050] not found to forward message: load url=https://test.example.com/index.php/apps/richdocuments/wopi/files/25_ocny42d5quk3?access_token=RScM8r0KN9bPGdnKNK6mjHddfZUKrZZO&access_token_ttl=0&permission=edit readonly=0 lang=en| wsd/DocumentBroker.cpp:1770
kit-00107-00019 2020-04-14 19:55:13.900360 [ loolkit ] WRN  Kit connection lost without exit arriving from wsd. Setting TerminationFlag| kit/Kit.cpp:2240
wsd-00017-00110 2020-04-14 19:55:13.901864 [ docbroker_011 ] ERR  Invalid or unknown session [0050] to remove.| wsd/DocumentBroker.cpp:1194
wsd-00017-00110 2020-04-14 19:55:13.905973 [ docbroker_011 ] ERR  No socket associated with WebSocketHandler 0x7f5bb4003020| ./net/WebSocketHandler.hpp:125
wsd-00017-00027 2020-04-14 19:55:14.513976 [ websrv_poll ] WRN  Prisoner connection disconnected but without valid socket.| wsd/LOOLWSD.cpp:1799

Clearly there is a problem with placing a nginx reverse proxy in front of nginx/collabora since in the absence of the frontend reverse proxy things aren’t working.

FWIW here is my office.example.com setup:

server {
        listen [::]:443 ssl http2;
	listen      443 ssl http2;

        server_name office.example.com;

        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';

        access_log /var/log/nginx/office.example.com.access.log main buffer=32k;
        error_log /var/log/nginx/office.examlple.com.error.log;

        include snippets/office.examplecom.cert.conf;
        include snippets/ssl-params.conf;

	keepalive_timeout 65;

	# Allow large attachments
	client_max_body_size 128M;

	index index.html index.htm index.php /index.php;

	location / {
		root /var/www/office.example.com/html;
	}

 # static files
location ^~ /loleaflet {
    proxy_pass http://localhost:9980;
    proxy_set_header Host $http_host;
}

# WOPI discovery URL
location ^~ /hosting/discovery {
    proxy_pass http://localhost:9980;
    proxy_set_header Host $http_host;
}

# Capabilities
location ^~ /hosting/capabilities {
    proxy_pass http://localhost:9980;
    proxy_set_header Host $http_host;
}

# main websocket
location ~ ^/lool/(.*)/ws$ {
    proxy_pass http://localhost:9980;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $http_host;
    proxy_read_timeout 36000s;
}

# download, presentation and image upload
location ~ ^/lool {
    proxy_pass http://localhost:9980;
    proxy_set_header Host $http_host;
}

# Admin Console websocket
location ^~ /lool/adminws {
    proxy_pass http://localhost:9980;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $http_host;
    proxy_read_timeout 36000s;
}
}

Here is my docker-compose file for collabora

version: '3.3'

networks:
  net:
   driver: bridge

services:

  collabora:
    restart: always
    image: collabora/code:latest
    container_name: collabora
    networks:
      - net
    ports:
      - 127.0.0.1:9980:9980
#      - 9980:9980
    cap_add:
      - MKNOD
    environment:
      - TZ=America/Chicago
      - username=admin
      - password=dockercol
      - domain=nextcloud\\.example\\.com|test\\.example\\.com
      - DONT_GEN_SSL_CERT="True"
      - server_name=office.example.com
      - extra_params=--o:ssl.enable=false --o:ssl.termination=true
    volumes:
      - /etc/letsencrypt/office.example.com/privkey.pem:/etc/loolwsd/key.pem
      - /etc/letsencrypt/office.example.com/fullchain.pem:/etc/loolwsd/cert.pem

Did you add the dns entries for NextClound on your proxy server to the “Backend Storage” section of the /etc/loolwsd/loolwsd.xml?

I have both the local DNS and the external DNS listed in there.

You should probably add something similar to the following: in there.

<host allow="false" desc="Regex pattern of hostname to allow or deny.">nextcloud.example.com</host>

Hi thanks for responding. It wasn’t a nextcloud issue per say but a problem with the host headers being passed from the reverse proxy. I learned a lot about host headers in the process. (More than what I wanted to know).

WTF, then why aren’t you sharing what you have learned? What on earth are we, who have similar problems, going to do with your answer? Please write what you did to make it work so others who troubleshoot can try it out as well…

1 Like

I have a very similar setup and ran into a very similar problem. nginx reverse proxy on one VM and one VM each for nextcloud and collabora. They each have one subdomain assigned each. I want users outside my network to be able to use collabora on nextcloud as well as users inside my network. I found this thread and my initial reaction was similar to that of “dec0de”. As I recently figured out a solution that does what I want I thought I would post so that other users actually will find this search hit useful. I’m not sure this is the correct way of doing things but at least it works and runs " SSL on both ends".

png

On my LAN I have a dns resolver (unbound on pfsense) running for local users that maps “nextcloud . example . com” and “office . example . com” to their corresponding local IPs/VMs.
On the nginx reverse proxy VM I have one “normal” entry for “nextcloud . example . com” roughly like this:

server {
        listen 443 ssl http2;
        server_name nextcloud.example.com;
        ssl_certificate /path/to/cert.fullchain;
        ssl_certificate_key /path/to/cert.key;
        location / {
                proxy_pass https://<local FQDN of the nextcloud VM>;
                access_log off;
                proxy_set_header Host $host;
        }
}

This config also has an entry for the collabora VM like this (only included the first location as an example. The full config is posted here: Setting up Nginx reverse proxy - Collabora Office and Collabora Online

server {
    listen 443 ssl http2;
    server_name office.example.com;

    ssl_certificate /path/to/cert.fullchain
    ssl_certificate_key /path/to/cert.key

# static files
location ^~ /loleaflet {
    proxy_pass https://office.example.com:9980;
    proxy_set_header Host $http_host;
}

On the collabora VM another nginx reverse proxy runs and the settings are exactly like the “SSL on both ends” example linked above. This means the loolwsd config has ssl=true and ssl termination=false and fullchain+key certs are configured.

In conclusion. The external users hits the WAN-accessible reverse proxy first using HTTPS and are proxied via HTTPS locally to the collabora VM on port 9980. Internal users on the other hand hits the collabora VM directly via HTTPS and are proxied on the collabora VM itself to HTTPS localhost:9980.

I hope the explanation is understandable and that someone might find it useful. Keep in mind, I’m no HTTPS expert. Nor am I an authority on collabora+nextcloud setup.

Hi @StarkJohan

I used to have my setup similar to yours but honestly I found it very fragile. Seems like it would work for awhile then something would break. Honestly Collabora is a huge pain in the A$$ at least when using the docker image. I’ve currently found the latest docker colllabora broken (as confirmed with others on reddit – took about 2 hours to realize it was a broken container and not something on my setup).

Anyway I think your configuration looks pretty good except I don’t understand one part of your setup:

With this proxy_pass statement I suppose this would work b/c by default nginx with it’s proxy_pass methods does not perform any verification of the backend SSL certificate. Although yes this setup technically works because of lack of validation – SSL certs should in theory be issued to an actual domain name rather than an IP address. If using Let’s Encrypt certificates for the backend server, there is no way to have Let’s Encrypt issue you a certificate for an IP address. On the other hand – if using self-signed openssl certs (as I do in some instances), on can clearly specify both DNS names and IP addresses within the SAN specification for the generated server cert. I guess without enough detail of your setup, I really can’t comment on what your doing other than say it’s customary for a https proxy_pass statement to use domain names rather than IP addresses.

I eventually just switched to traefik for my reverse proxy and it seems a little less fragile.

I have a similar setup as yours

VM#1 - Reverse Proxy

VM#2 - Reverse Proxy (trafeik) which then http forwards to the docker collabora instance via http. Traefik and collabora run on the same docker network so I’m not exactly certain if encryption is of any value here honestly. Collabora is configured or an SSL terminating proxy with - “extra_params=–o:ssl.enable=false --o:ssl.termination=true”.

WAN clients --https–> VM/Reverse Proxy#1 --https–> VM/ReverseProxy#2 --http–> Collabora docker

LAN clients —https—>VM/ReverseProxy#2—http—>Collabora Docker

I believe inside of the container there is also server than can handle tls connections, however I’m not sure how that all works.

keydog, you are correct regarding the IP. It works just fine and as long as the setup is in a trusted LAN i wouldn’t worry too much about it. FQDN is the way to go (and is actually what I’m using right now, your post reminded me of this fact).
I’m running Letsencrypt with wildcard and subdomain certs all around so normally everything is adressed using FQDN both externally and internally.
I’m not running any docker images at all. Everything is on a proxmox host running in full ubuntu 20.04 VMs. I haven’t had any issues so far but this setup does not see any extensive use. It’s a family thing with a handful of users at the most.
I’ve tinkered a bit since I wrote my initial post and actually got HTTPS working all the way, finally. (i.e. “extra_params=–o:ssl.enable=true --o:ssl.termination=false”)
I will update my initial post to reflect your input and my progress.