Nextcloud protection via Let's Encrpyt behind a reverse proxy?

Hello Nextcloudcomunity,
iā€™m new here and only recently started working on Linux, servers, nextcloud and more.
But I am very curious and want to host my own cloud. Iā€™ve been working on that for many days and weeks now, but Iā€™m having some problems that make me delete my LXCs again and again and have to start from scratch.
I would like to run my Nextcloud on an LEMP stack (i.e. with Nginx) behind a reverse proxy (also with Nginx).

But I seem to have a problem of understanding, because it just does not want to work. So first my simple question:
If I have Letā€™s Encrypt with certbot on my reverse proxy (LXC 1), i.e. the communication up to my reverse proxy in my local network is SSL encrypted, how important is it to set this up on my Nextcloud LXC (2) as well? Or can the communication between Reverse Proxy and Nextcloud be unencrypted ? What are the advantages and disadvantages ?

Otherwise I obviously have some problem to set up the nginx configuration of the virtual host correctly.

Iā€™m pretty desperate because I donā€™t know where the error is.
It would be great if you could help me.

Thanks a lot.
Greetings

Reverse proxies by nature can be

  1. SSL terminating
  2. SSL passthrough
  3. SSL terminating and re-encrypting to the backend

Most examples youā€™ll see deal with #1 where the reverse proxy terminates the SSL connection and then uses http to the backend. This technically is going to be the fastest way to server many users. The backend connection however will be unencrypted. (Just for reference ā€“ nginx calls the backend ā€œupstreamā€ so if googling for examples from the nginx documentation ā€“ use the word ā€œupstreamā€). If you donā€™t trust your internal LAN ā€“ or there are potential weakness in your LAN then this might be a problem.

SSL terminating and re-encrypting can also be performed. Usually the backend certificates are self-signed, however there is no reason you couldnā€™t use LE certs for backend communication as well. Since you control the reverse proxy and the nextcloud server ā€“ if you really wanted to get fancy/smancy with the encryption you could use client SSL certificates as well as server-side SSL certificates (LE certs are exclusively server-side SSL certificates). Computationally having to decrypt/re-encrypt is more expensive and itā€™s likely slower if you are serving a lot of users. Iā€™ve seen however some debate about this theory particularly if using CPUs (Intelā€™s) that have AES-NI built-in (which are most modern processors). I havenā€™t run any tests which would demonstrate the speed or computational cost of this setup, however if you are only going to serve a handful of users, youā€™ll likely not notice any difference. This setup could be used if you donā€™t really trust your LAN or if itā€™s required by law ā€“ for example dealing with HIPPA-compliant data or banking data.

To take things a step farther you could also setup a collabora docker or native installation that is also protected by SSL certificates ā€“ so itā€™s always possible to do a triple-SSL connected stream -->reverse proxyā€“>nextcloudā€”>collabora (or open office).

Iā€™ve implemented both #1 and #3 in my setup and usually just do #3 for security purposes ā€“ itā€™s likely overkill but I just wanted to see if I could set everything up.

4 Likes

Hello, kevdog,
Thank you for your explanation.
Option #3 sounds like what I actually want to do. It sounds more straightforward and if Iā€™m going for SSL encryption, Iā€™d like to go all the way. Overkill or not.

The thing with the certificates is not quite clear to me yet.
To be a bit more concrete, I had the following setup:
First I secured my Nextcloud LXC with LE and created SSL certificates for my Nextcloud subdomain with certbot. This worked so far.
After that I tried to connect the reverse proxy, created again a SSL certificate for my Nextcloud subdomain with certbot and tried to forward the traffic to the internal IP address of my Nextcloud LXC. In this case I always got a redirection error when I tried to access the Nextcloud URL.
Can, must, should the certificates be identical on both the reverse proxy (LXC1) and on the Nextcloud (LXC2) and go to my subdomain both times ?

Those were my configs:
Sorry if the formatting of my Configs is not perfect :wink:

On nextcloud-LXC
/etc/nginx/sites-enabled/nextcloud

upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/var/run/php/php7.4-fpm.sock;
}

server {
    listen 80;
    listen [::]:80;
    server_name subdomain.domain.tld;
    # enforce https
    return 301 https://$server_name:443$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name subdomain.domain.tld;

    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;

    # Use Mozilla's guidelines for SSL/TLS settings
    # https://mozilla.github.io/server-side-tls/ssl-config-generator/
    # NOTE: some settings below might be redundant
    ssl_certificate /etc/letsencrypt/live/subdomain.domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.tld/privkey.pem;

    # Add headers to serve security related headers
    # Before enabling Strict-Transport-Security headers please read into this
    # topic first.
    #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
    #
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    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 "none" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Path to the root of your installation
    root /var/www/nextcloud;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # The following 2 rules are only needed for the user_webfinger app.
    # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

    # The following rule is only needed for the Social app.
   # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

    location = /.well-known/carddav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }
    location = /.well-known/caldav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }

    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json ap>

    # Uncomment if your server is build with the ngx_pagespeed module
    # This module is currently not supported.
    #pagespeed off;

    location / {
        rewrite ^ /index.php;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
        fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
        set $path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;
        # Avoid sending the security headers twice
        fastcgi_param modHeadersAvailable true;
        # Enable pretty urls
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

    # Adding the cache control header for js, css and map files
    # Make sure it is BELOW the PHP block
   location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
        #
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        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 "none" always;
        add_header X-XSS-Protection "1; mode=block" always;

        # Optional: Don't log access to assets
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }
}

And here is the config on my reverse proxy:

server {

  server_name subdomain.domain.tld;

  location / {
      proxy_pass https://192.168.1.120/; #internal IP address of the LXC where my Nextcloud runs
  }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/subdomain.domain.tld/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.tld/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = subdomain.domain.tld) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  listen 80;
  listen [::]:80;

  server_name subdomain.domain.tld;
    return 404; # managed by Certbot


}
1 Like

Nobody but kevdog uses a reverse proxy and full SSL encryption and can help me with this problem ? :astonished: :cry:
I would never have thought that my problem was so unique and difficult. How did you solve it alternatively, so that such problems donā€™t arise at all ? :thinking:

Have you tried #1 as an interim solution to verify that you can get it working without SSL? I have the same setup (but with FAMP stacks) and it has worked without any trouble for years. I canā€™t help specifically since I donā€™t knwo nginx, but I have a general tip. It can be problematic to have two things that can fail as a novice. Try to get the simple working and then add complexity. So I would do the following if I where you

  1. Try to get #1 working, so you know that the reverse proxy part works
  2. Check your logs. There should be clues in the error logs for nginx, on either the proxy or nextcloud container. Set the log level to debug, try to load the site and look in the logs for clues what went wrong.

Hello, Kebba,
a good suggestion. I tried it once. But I donā€™t even have the possibility to get the IP address via http :cry:
Whenever I type http://IP-Adresse of my Nextcloud installation in my browser, it automatically changes it to https and I get the message ā€œConnection failedā€.
I still think your idea is good to start with the simple things, but obviously nothing works without SSL already here.
Iā€™m getting desperate about all these problems :rage:

I can help you but not until next week. Traveling however I have a reverse re-encrypting proxy with SSL server certs for my setup. I donā€™t have access to my setup till then. Looking at setup quickly it doesnā€™t seem you have headers setup correctly. Take a look at this post as this was my starting point:

Hi kevdog,
nice to hear from you and thanks for your help. That would be great.
You seem to know a lot about the subject :sunglasses: :+1:
Parallel to your link I found a ā€œsolutionā€ which is obviously not quite clean yet, but at least it works.
I have now encrypted both my Nextcloud LXC and my Reverse Proxy -LXC with my subdomain SSL, and I have concocted a reverse proxy configuration that runs on a warning, but otherwise works.

server {
    listen 80;
    listen [::]:80;
    server_name  subdomain.domain.tld;
    # enforce https
    return 301 https://$server_name:443$request_uri;
}

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

        server_name subdomain.domain.tld;

        ssl_certificate /etc/letsencrypt/live/subdomain.domain.tld/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.tld/privkey.pem;

ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;

location / {

proxy_set_header Host $host;
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_pass https://subdomain.domain.tld;
proxy_read_timeout 90;
proxy_redirect https://internal-IP-Adress https://subdomain.domain.tld;

}

}

Outside of my network everything seems to work by calling my URI and the access via reverse proxy to my nextcloud works as well.
But if I call the same URI from my internal network, I can access my Nextcloud as well, but my internal IP address will be displayed.
I have also just configured fail2ban. If I logged in incorrectly in the past (before the reverse proxy setting), my external router IP was displayed as the banned IP address. Now the internal IP address of my end device. So -why- the internal access seems to work only internally. Very confusing the whole thing :thinking:
I also tried to add the headers from your link in my Reverse Proxy Config, but then I get a ā€œ500 Internal Server Errorā€ message.

Thanks again in advance for your help :blush:

Iā€™m not sure what you mean by this: ā€œBut if I call the same URI from my internal network, I can access my Nextcloud as well, but my internal IP address will be displayedā€

Is this what you are doing:
externalā€”>Rv proxyā€”>nextcloud

And also attempting this:
internalā€”>Rv proxyā€”>nextcloud

of
internalā€”>nextcloud?

Honestly Iā€™ve never seen a statement like this: (Iā€™m no expert)
proxy_redirect https://internal-IP-Adress https://subdomain.domain.tld;

Why not just:
proxy_redirect https://subdomain.domain.tld;

Where is the DNS server on your LAN?

Hi kevdog,
yes, thatā€™s exactly what I meant. I would have expected that the way is the same (no matter if internal or external) when I call my subdomain.

If I change the proxy_redirect from the current config to your suggested one, I get the following error message during the syntax check:

nginx: [warn] the ā€œsslā€ directive is deprecated, use the ā€œlisten ā€¦ sslā€ directive instead in /etc/nginx/sites-enabled/subdomain.domain.tld.conf:23
nginx: [emerg] invalid parameter ā€œhttps://subdomain.domain.tldā€ in /etc/nginx/sites-enabled/subdomain.domain.tld.conf:40
nginx: configuration file /etc/nginx/nginx.conf test failed

I usually get the warning message, but it still works. With the error I now get when I remove the ip-address from the config, it doesnā€™t work anymore.

About DNS: I use the DynDNS service of my domain operator. In addition, my router has the function that I can connect the DynDNS service of my domain operator directly with it.

@Mr.Groves
No idea why this thread popped up but do you still need help with this issue?

Kevdog,

Option 1 for your first post is exactly what I am looking for. I would like to use http within my LAN but https from outside. Is this possible? Could you point me at a tutorial? Thanks in advance.

John

I can point you in right direction for sure. Whatā€™s your nextcloud setup looking like ā€“ OS host, is it docker, native, etc? What reverse proxy were you thinking about using?

Thanks. Itā€™s installed directly on a Raspberry Pi4 running Raspberry Pi OS and the server is Nginx with php-fpm 7.3.

Ok I donā€™t know anything about the RPi, however I donā€™t think that really is too important. You have a base Nextcloud installation with Nginx Webserver using fpm. Was there a particular reverse proxy you were looking at? Would the reverse proxy be located on the same machine or different location?

Sorry for the slow reply.

The reverse proxy would be on the same machine. I have my Pi open to the world on ports 80 and 443. Port 80 automatically redirects to 443 on https and is currently serving a Wordpress site. I have the nextcloud on a different port that is only available on the LAN. I used the nginx reverse proxy example config file from the Nextcloud documenation but with all the ssl parts removed.

That worked fine for initial installation, but then I couldnā€™t log in as the login page was trying to return the form using https, which violated the content security policy.

I changed overwrite.cli.url to http instead of https and added a line:
ā€˜overwriteprotocolā€™ => ā€˜httpā€™ in the config.php file.

Since doing that (and installing php.intl) everything has worked fine on the LAN, but was that the most efficient and safe way of doing things?

As for the reverse proxy, I was going to add a ā€˜location ^nextcloud/ {}ā€™ block to my port 443 server. I presume I will have to change the host to match the LAN host before the proxy pass (or add a new trusted host to Nextcloud), but do I have to do anything on the way back? Iā€™ve not tried yet, as Iā€™ve spent the last couple of days checking that my LAN installation was actually working properly.

Thanks

Ok here is how I would set it up.

  1. Iā€™d ideally have an indendent reverse proxy. This could be on the same machine or different machine. Yes you could repurpose your nginx webserver running your nextcloud to be both a webserver and reverse proxy ā€“ however I kind of like the modular concept that you have one tool do one job ā€“ its easier to debug, and its easier to replace or move to a different machine in the future. Youā€™re on a RPie. Can that do docker?
  2. Depending on your situation you are going to have to pick a reverse proxy ā€“ either nginx (or nginx proxy manager ā€“ which provides GUI and will do Letsencrypt renewals automatically but requires docker), traefik (which I think it also requires docker ā€“ I run it in docker but there maybe a native option), or caddy. All these products will do automated LetsEncrypt renewals (not nginx without npm).
  3. Iā€™d recommend using subdomains rather than subdirectories if possible. So for example nextcloud.domain.com rather than domain.com/nextcloud. Reverse proxies in general seem to function better and less prone to error with subdomains rather than subdirectories.

Hey guys
Just saw this thread and Iā€™m hoping kevdog is still listening to it and willing to give further advice.
So, I have a Nextcloud server up on Apache and I can get to it with a separate Nginx reverse proxy server via http. Nextcloud really balks though in the system status check, and some things arenā€™t even useable like passwords, if youā€™re not using https.
That said, I think I need to do option #3 (https to Nginx-reencrypt and forward to Nextcloud) Barring an issue I think Iā€™m having with Letsencrypt not downloading certs to the ā€œLiveā€ directory what is the proper way to set this up?
First, Can I use the same Subdomain that I get to use from NoIP or Dynu from LetsEncrypt on both machines? If not, what the alternative (self signed? - and if self signed which machine should that go on?). I think I read something that Letsencrypt doesnā€™t let you download multiple certs for the same subdomain/domain. Also, if itā€™s self signed how can I set it up to be nonexpiring?
Second, I want to do this in pieces to reduce the troubleshooting variables of where it may not be working. So first I want to bring it up on the reverse proxy default html. Actually I donā€™t even care if it goes to Apacheā€™s default http html as long as I can see that the Nginx certs are working correctly. Than Iā€™d like to know the ā€œcodeā€ and where to put it to do the reencryption and forward to Apacheā€™s https (that way I limit it to either something being wrong with the reencryption and forward code or something wrong with the Apache cert setup).
Any help is greatly appreciated.

I think I have it from what Iā€™m reading from the Nextcloud documentation. I think I need to use X-FORWARD and config my reverse proxy machine in Nextcloud as a ā€œtrusted proxyā€. Weā€™ll see how it goes