Move nextcloud to sub url but put a public folder at root (with nextcloud + nginx + nginx-docker-gen all in docker)

I have nextcloud running on https://cloud.museumsstrasse.at but since we will use nextcloud primarily for opening data to the public I would like that this nextcloud root url leads to a public nextcloud folder directly, e.g. to use this one: https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL but so that the public would not need the url with the share sub-url but only type in https://cloud.museumsstrasse.at where this public folder would be opened.
As for logging in and synchronizing with nextcloud I would then like to use another sub url such as this for example: https://cloud.museumsstrasse.at/internal

Is this possible and how?

My setup

I got nextcloud running with docker on a Ubuntu 20.04 Server. Since other services are running on this server too, I set up a reverse proxy solution all with docker too including these images: nginx, jwilder/docker-gen:0.7.3, jrcs/letsencrypt-nginx-proxy-companion which do the reverse proxy and also generate the config files for reverse proxy. The docker-compose.yml for all this is this:

version: '3'

services:

  service_nginx:
    image: nginx:1.13.1
    container_name: container_nginx
    ports:
      - "80:80"
      - "443:443"
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
    labels:
      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"

  service_nginx-dockergen:
    image: jwilder/docker-gen:0.7.3
    container_name: container_nginx-dockergen
    depends_on:
      - service_nginx
    command: -notify-sighup container_nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
      - /var/run/docker.sock:/tmp/docker.sock:ro

  service_nginx-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: container_nginx-letsencrypt
    depends_on:
      - service_nginx
      - service_nginx-dockergen
    environment:
      NGINX_PROXY_CONTAINER: container_nginx
      NGINX_DOCKER_GEN_CONTAINER: container_nginx-dockergen
    networks:
      - network_nginx
    volumes:
      - ./volumes/nginx/conf:/etc/nginx/conf.d
      - ./volumes/nginx/vhost:/etc/nginx/vhost.d
      - ./volumes/nginx/html:/usr/share/nginx/html
      - ./volumes/nginx-letsencrypt/certs:/etc/nginx/certs
      - /var/run/docker.sock:/var/run/docker.sock:ro

  service_nc_db:
    image: mariadb
    container_name: container_nc_db
    volumes:
      - ./volumes/nc_db:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    networks:
      - network_nc_prod
    environment:
      - MYSQL_ROOT_PASSWORD=XXX
      - MYSQL_PASSWORD=XXX
      - MYSQL_DATABASE=XXX
      - MYSQL_USER=XXX
    restart: unless-stopped

  service_nc:
    image: nextcloud:latest
    container_name: container_nc
    depends_on:
      - service_nc_db
      - service_nginx
      - service_nginx-dockergen
      - service_nginx-letsencrypt
    networks:
      - network_nc_prod
      - network_nginx
    volumes:
      - ./volumes/nc/html:/var/www/html
      - ./volumes/nc/config:/var/www/html/config
      - ./volumes/nc/custom_apps:/var/www/html/custom_apps
      - ./volumes/nc/data:/var/www/html/data
      - ./volumes/nc/themes:/var/www/html/themes
      - /etc/localtime:/etc/localtime:ro
    environment:
      - VIRTUAL_HOST=cloud.museumsstrasse.at
      - LETSENCRYPT_HOST=cloud.museumsstrasse.at
      - LETSENCRYPT_EMAIL=XXX
    restart: unless-stopped

networks:
  network_ca_prod:
  network_nc_prod:
  network_nginx:

What I have tried so far is pushing the html code of nextcloud into a subfolder and fiddling around with settings such as overwritewebroot or overwritehost in config.php and various experimentation on nginx reverse proxying, but it all failed so far.
What I have tried to follow were discussions such as these for example:



But I could not get it working because whenever I restart the container, everything within the code in /var/www/html is deleted and reset.

Can someone help me here please?

Cheers,
Stefan

Sounds like you modified the files inside the Docker container and not mapped to a mount outside of the container.

Pretty sure not since the relevant folders are mounted as defined here:

  service_nc:
    image: nextcloud:latest
...
    volumes:
      - ./volumes/nc/html:/var/www/html
      - ./volumes/nc/config:/var/www/html/config
      - ./volumes/nc/custom_apps:/var/www/html/custom_apps
      - ./volumes/nc/data:/var/www/html/data
      - ./volumes/nc/themes:/var/www/html/themes
      - /etc/localtime:/etc/localtime:ro

I tried moving the code in ./volumes/nc/html to ./volumes/nc/html/internal but when starting the container everything in ./volumes/nc/html on the host and in /var/www/html within the container gets overwritten with some default nextcloud code. So it looks like that the nextcloud image checks on container creation time for valid nextcldoud code in /var/www/html and if not found, erases everything there and replaces it with defaults?

Regardless however I’m not sure if this is the desired way anyway, since even if that worked, how would I then manage to have the public folder (which is then a subfolder of /var/www/html/internal) mapped to the root url /var/www/html ?

Never seen a sample with the dots at the beginning of the hosts path.
But if the directories are empty the container adds the default files.

The thing is that the folder is not empty, just that the code is put into the subfolder internal and no changed settings as outlined in my post and in the linked discussions did help to let nextcloud know about this change of code location. And even if it did, I’m sceptical if it would achieve the desired result as I just explained in my previous message…

For example what doesn’t work is if I add to config.php these two lines:

  'overwritehost' => 'cloud.museumsstrasse.at',
  'overwritewebroot' => '/internal'

Then opening cloud.museumsstrasse.at the browser would get redirected to cloud.museumsstrasse.at/internal but in the meantime the code in /var/www/html/internal/<nextcloud_code> got deleted (and hence returns a 404) and default nextcloud code put into /var/www/html/<nextcloud_code> which I then can not open because the browser would again be redirected to cloud.museumsstrasse.at/internal.

However even if something with overwritewebroot were working, how could I avoid this redirection from cloud.museumsstrasse.at to cloud.museumsstrasse.at/internal? Because the former should be the public face and the latter our internal nextcloud side. Hence I want no redirection but two different urls for different purposes.

I assume if a pragmatic way would be to adapt nextcloud’s docker image logic to point it to /var/www/html/internal/ and then adapt the this docker’s internal apache to show cloud.museumsstrasse.at while serving content from the public foldercloud.museumsstrasse.at/s/Ytpcwos3o9o33kL ? However I’m at a loss of how to do this…

I think your idea is not really good. Your idea is stupid. Your idea makes no sense.

There are hundreds of nextcloud programmers and thousands of nextcloud users and nobody needs this feature. It is only a stupid sub-path “internal” and nobody needs it. It is no security feature. It is nothing. With every modification of your nextcloud you get more problems with maintenance and upgrades.

If you really want to differ between private and public i think you must implement two nextclouds (user can use federation between the nextclouds) and in the private nextcloud you do not allow sharing with public shares.

For the private nextcloud you can use https://internal.museumsstrasse.at and for the external nextcloud and https://cloud.museumsstrasse.at for the external nextcloud . Perhaps you can synchronize your user/passwords or using ldap. “internal” uses a different data-dir to “cloud”. In the internal nextcloud https://internal.museumsstrasse.at you can use external storage with “Local” to the public cloud data but not vice versa.

If you use futher apps use them primary in your internal nextcloud. For sharing with external people you can move the data from the internal to the external nextcloud with https://nextcloud.com/de/federation/

If you do not like it use only one nextcloud https://cloud.museumsstrasse.at and do not change anything. If the user wants to share secret data he/she shares them or send them with e-mail.

Hahaha … wow, you must be lovely to be around with.

Due to your language and lack of constructive feedback, I will not reply to your messages any further.

Reconsider your behavior. This is far from professional and by this you contribute to an already high toxicity in the dev community.

Ok. Sorry english ist not my first language. In german i think i can find better words :wink:
But i know a lot of companies who blend software although millions of user use it on the default and mostly right way. The most sofware blend lead to problems with maintenance, upgrades and security.

@steffres you have to configure the nginx router. not nextcloud.

you need two proxy settings. the first redirects all incoming request ending with
https://cloud.museumsstrasse.at/” to “http://service_nc/s/Ytpcwos3o9o33kL”
and the second one
https://cloud.museumsstrasse.at/internal” to “http://service_nc/”

but since the routing through your nginx setup is done by the jwilder/jrcs/letsencrypt container ensemble i can’t tell you how. the nginx conf for “cloud.museumsstrasse.at” is created automatically by using “VIRTUAL_HOST=cloud.museumsstrasse.at”

I agree with you that software should be used as close as possible to its intented purpose and that defaults are the way to go. Absolutely. However if customization is possible without interference into a healthy base setup, then it is reasonable to ask. And in this case, I assume that what I’m asking for should be solvable with some kind of nginx routing setup.

Of course, if the interference is too high, I would not do it. But so far I don’t see a major counter argument yet. I’m open however.

1 Like

Thank you for your input. I thought soo to, but I have not much knowledge on routing ngnix and apache together. I guess I better ask somewhere on a more nginx-specific forum rather than this nextcloud one?

However I’m afraid that nextcloud would still need to know about the routing since for example wouldn’t it need to know its own root url for link generation?

Ok i re-read your first post. Here is perhaps a better idea.
I think cloud.museumsstrasse.at is a good name for nextcloud. So we do not change it.

But you can create a new subdomain (with Lets Encrypt) e.g.

https://public.museumsstrasse.at
and/or use subdir from “www”:
https://www.museumsstrasse.at/public

and there you program a 403-redirect (.htaccess or easier):

public/index.php

<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: `https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL`");
header("Connection: close");
?>

or

public/.htaccess

RewriteEngine On
RewriteRule ^(.*)$ https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL [NC,L,QSA]

or direct in your /-path without subdir public:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule public$ https://cloud.museumsstrasse.at/s/Ytpcwos3o9o33kL [NC,L]

Now you can access it with:

https://public.museumsstrasse.at
and/or use subdir from “www”:
https://www.museumsstrasse.at/public

Ok the destination link is the old link. Sorry.
But you can use Iframes.
Perhaps it is possible with a direct rewrite.

Thank you devnull,

I tried your suggestions and the second one with modyfing public/.htaccess worked, but not as I wanted it to. Because it did correctly redirect but the target url towards which the redirections points is then visibile to the visitor. I want to hide this away so that when visiting the public folder the only url the visitor sees is and always will be only cloud.musuemsstrasse.at and nothing else.

I could get this working in my following comment. Thank you for your input!

Alright, I got it working - at the expense of quite some pain.

Firstly, what I tried and what did not work (or what I could not get working):

  • Moving the nextcloud code into sub-folder (the nc image would always overwrite)
  • Creating virtual host by the apache server within the nc image. I could get correct redirections, but the target urls were always visible, which I wanted to avoid.
  • Creating redirections on the nginx reverse proxy layer.

Now, what I did instead was:

  • 1.) “moved” nextcloud to subpath by symbolic linking
  • 2.) recreate the reverse proxy stack with traefik

Details:

1.) “moved” nextcloud to subpath

In order to reach nextcloud under the url cloud.museumsstrasse.at/intern (before I had it called ‘internal’) I did the following steps:

1.1.

Created symbolic linking within the container:

ln -s /var/www/html/ /var/www/html/intern
chown www-data:root -h /var/www/html/intern

1.2.

Then opened nextcloud in a browser, registered an admin user, provided db credentials.

This step is necessary since afterwards some config files are auto generated which will be modified in the next steps.

1.3.

In /var/www/html/.htaccess is the auto-generated code-block where RewriteBase must be modified:

Without this step I got an ERR_TOO_MANY_REDIRECTS error.

<IfModule mod_rewrite.c>
  ....
  RewriteBase /intern # <-- changed from auto generated `RewriteBase /`
  ....
</IfModule>

1.4.

Now it would work mostly already, except for the fact that the web login and client authentication form freeze (somehow related to the reverse proxying). This issue is discussed here: https://github.com/nextcloud/server/issues/19091 and to fix it, the following must be added to /var/www/html/config/config.php:

...
'overwriteprotocol' => 'https' 
...

After these steps, the nextcloud instance can be reached with the desired subpath cloud.museumsstrasse.at/intern

A helpful discussion can be found here: https://github.com/nextcloud/docker/issues/401

2.) traefik reverse proxy

Now in order to show a selected public folder under the root url of cloud.museumsstrasse.at traefik can be used to replace paths on the fly while hiding the targeted urls from the visitor so that no matter what subpath is returned under cloud.museumsstrasse.at the url will always remain.

For this in traefik v2 a middleware must be defined. Since I’m using docker-compose only, I did this via these labels on the proxied service:

# defines the middleware:
- "traefik.http.routers.service_nextcloud.middlewares=custom_repath" 

# the regex which the rule should match (double dollar signs for escaping):
- "traefik.http.middlewares.custom_repath.replacepathregex.regex=^/$$"

# the replacement value (the subpath of a selected public folder):
-"traefik.http.middlewares.custom_repath.replacepathregex.replacement=/intern/s/63KFWGkziffJR8j"

3.) The full docker-compose.yml

services:

  service_traefik:
    image: "traefik:v2.2"
    container_name: container_traefik
    networks:
      - network_traefik
    command:
      # an explicit reference to the docker network must be passed when multiple networks exist:
      - "--providers.docker.network=docker_network_traefik"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=XXX"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./volumes/traefik/letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  service_nc_db:
    image: mariadb
    container_name: container_nc_db
    volumes:
      - ./volumes/nc_db:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    networks:
      - network_nc
    environment:
      - MYSQL_ROOT_PASSWORD=XXX
      - MYSQL_PASSWORD=XXX
      - MYSQL_DATABASE=XXX
      - MYSQL_USER=XXX

  service_nc:
    image: nextcloud:latest
    container_name: container_nc
    depends_on:
      - service_traefik
      - service_nc_db
    networks:
      - network_traefik
      - network_nc
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service_nextcloud.rule=Host(`cloud.museumsstrasse.at`)"
      - "traefik.http.routers.service_nextcloud.entrypoints=websecure"
      - "traefik.http.routers.service_nextcloud.tls.certresolver=myresolver"
      - "traefik.http.routers.service_nextcloud.middlewares=custom_repath"
      - "traefik.http.middlewares.custom_repath.replacepathregex.regex=^/$$"
      - "traefik.http.middlewares.custom_repath.replacepathregex.replacement=/intern/s/63KFWGkziffJR8j"
    volumes:
      - ./volumes/nc/html:/var/www/html

networks:
  network_nc:
  network_traefik:

4.) versions

Currently I got this running with nextcloud 19.0.1 and as seen in the docker-compose.yml with traefik 2.2

Nice. But the old config was ok and not bad.

Does sharing works e.g.

Museum@cloud.museumsstrasse.at

because of subfolder /intern

Yes, everything works. :slight_smile:

1 Like