Caddy reverse proxy + Nextcloud + Collabora + Vaultwarden with local HTTPS

1. TL;DR

This Wiki contains the info to setup a frontend Caddy reverse proxy service with a Let’s Encrypt authorized TLS certificate and a backend host running a Caddy reverse proxy / webserver which serves Nextcloud with Collabora integrated and Vaultwarden (formerly Bitwarden_rs). The frontend Caddy will also issue TLS certificates for the backend LAN connections and renew them periodically. With this setup the connection will be encrypted from A to Z. For quick references, just scroll down to the second post which contains final and complete configuration files.

2. Introduction

In the Wiki Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts I described how to add TLS between the downstream reverse proxy and the upstream hosts (services). This Wiki is a continuation, which results in a setup with Nextcloud and Collabora integrated and Vaultwarden running behind a Caddy reverse proxy with a Let’s Encrypt certificate. The connections upstream, are also encrypted with the help of the ACME server built into Caddy. This is specifically interesting for Vaultwarden because without, your passwords will cross your LAN unencrypted. There is no need for any other software as Caddy has everything build in to connect the pieces (services) together.

This Wiki assumes you have managed to setup a Caddy reverse proxy as explained in the Wiki Using Caddy as a reverse proxy in a home network by @matt. First reading the Wiki Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts is helpful but probably not necessary.

3. Definitions and Requirements

The definitions and requirements from the Wiki’s mentioned above apply to this Wiki too. Please note that the setup described in this setup requires a local DNS or split DNS (as described in the above Wiki).

This Wiki also assumes you have docker and docker-compose working and you know how to use this. I consider it out of scope for this Wiki to go over every docker(-compose) detail but I tried to include enough info for the less experienced readers.

4. Topology

The below figure shows a frontend (downstream) host and a backend (upstream) host. Each host can be virtual or physical. The frontend host is setup with Caddy as reverse proxy to the outside world and provides trusted certificates from Let’s Encrypt. The backend host act as a Caddy reverse proxy and webserver. Nextcloud, Collabora and Vaultwarden are installed as Docker containers on this host. Vaultwarden is an independent Bitwarden implementation, written in Rust, which requires much less resources than the official Bitwarden server stack, while also being compatible with all the official clients.

On the top of each host you see the IP and port number and FQDN that this host is listening to.
In the backend host, you see docker containers with inside the service and the port its running on. Under each docker container you see the port that is being exposed to the backend host.

Note that Caddy has not been installed as a Docker container for a reason. It complicates the local certification renewal.

5. DNS

To set up the DNS you can follow the same guidance as in Using Caddy as a reverse proxy in a home network. The result should be that you have a domain name setup for each service pointing to the public IP address leased to your network ie;

nextcloud.example.com
bitwarden.mycomain.com

6. Local (Split) DNS

Each host or service should be given a FQDN to resolve the IP of the corresponding host in the local network. This is required to make the local certificate mechanism to work. For this wiki I created:

caddy.roadrunner192.168.0.2 to reach the frontend Caddy service
nextcloud.roadrunner192.168.0.4 to reach Nextcloud service
bitwarden.roadrunner192.168.0 .4 to reach Bitwarden service

Collabora will be lifting on nextcloud.roadrunner and therefore it does not require its own FQDN like collabora.roadrunner.

It is also a good idea to setup split DNS for the public domains. That way you can access the services from you LAN without going outside the LAN. Ie

nextcloud.example.com192.168.0.2 to redirect to the Caddy frontend
bitwarden.example.com192.168.0.2 to redirect to the Caddy frontend

7. Port forwarding

To set up the port forwarding you can follow the same guidance as in Using Caddy as a reverse proxy in a home network.

8. Frontend (downstream)

This is where the connections from the internet come into your LAN. Caddy will issue a certificate to make the connection secure with Let’s Encrypt as Certificate Authority. Depending which service the user is trying to connect to ie Nextcloud, Caddy will forward the incoming connection to the backend where the actual service is running. To make this work, the Caddyfile needs to be configured.

8.1. Caddyfile

First, to enable the ACME server in the frontend, include the acme_server directive in the Global section of the Caddyfile. Optional (recommended) you can also add debug which will help with debugging if things don’t work.

# Global Option Block
{
	debug
}

# ACME Server
caddy.roadrunner {
	acme_server
	tls internal
}

Next is to configure to forward incoming connection for Nextcloud upstream:

## Nextcloud
nextcloud.example.com {
	reverse_proxy https://nextcloud.roadrunner {
		header_up Host {upstream_hostport}
	}
}

See the docs which explain the appropriate config to make an HTTPS connection to the upstream: reverse_proxy (Caddyfile directive) — Caddy Documentation

If you want more background info about this, read my previous Wiki or search my posts where @francislavoie helped me with this.

To forward incoming connections for Bitwarden upstream, include:

# Bitwarden
bitwarden.example.com {
	reverse_proxy https://bitwarden.roadrunner {
		header_up Host {upstream_hostport}
	}
	respond /admin* "The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it"
}

Vaultwarden comes with an optional admin web console. For security reasons I do not like to expose this to the world.

respond /admin* provides users that try to access the Bitwarden admin web console the same printed message as when the admin console is disabled. Hopefully this will make them give up. You can also present a 404 error. In the LAN the admin console can still be made available.

Additional settings for Collabora are not necessary because Collabora will be integrated with Nextcloud.

Start Caddy by running Caddy start and check the output for possible errors.

9. Backend (upstream)

This is the host where the services are running in docker containers. Caddy is installed directly (no docker to avoid certificate renewal issues) to forward the connections from the frontend to the docker containers and serve as a fileserver for Nextcloud.

9.1. Docker configuration

Although this Wiki is about Caddy, I’m including the docker-compose files that I composed too. I spend a lot of time to get a working setup and hope to provide users some guidance to a quick working setup. I’m quite sure the docker-compose files can be optimised and I am open for input for any comment or improvements.

There is one docker-compose file for Nextcloud and Collabora combined that also makes use of an .env file. The second docker-compose file is for Vaultwarden only.

9.2. Nextcloud and Collabora docker-compose

Below the docker-compose file to setup the docker containers for Nextcloud and Collabora.
Further down you also find an example of the required .env file. Adjust both files and save them in the same folder, ie ~/nextcloud/docker-compose.yml and ~/nextcloud/.env

version: '3.7'

networks:
  nextcloud:

services:
  nextcloud:
    # image: nextcloud:fpm
    build: ./nc_smb_image
    container_name: nextcloud
    restart: unless-stopped
    networks:
      - nextcloud
    ports:
      - "9000:9000"
    volumes:
      - ${NEXTCLOUD_ROOT}/html:/var/www/html
      - ${NEXTCLOUD_ROOT}/data:/srv/nextcloud/data
    depends_on:
      - mariadb
    environment:
      - 'NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_FQDN}'
      # - 'NEXTCLOUD_DATA_DIR=/srv/nextcloud/data'
      - 'MYSQL_DATABASE=nextcloud'
      - 'MYSQL_USER=nextcloud'
      - 'MYSQL_PASSWORD=${MYSQL_PASSWORD}'
      - 'MYSQL_HOST=nextcloud-mariadb'

  mariadb:
    image: mariadb
    container_name: nextcloud-mariadb
    restart: unless-stopped
    networks:
      - nextcloud
    volumes:
      - ${NEXTCLOUD_ROOT}/mariadb:/var/lib/mysql
    environment:
      - 'MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}'
      - 'MYSQL_PASSWORD=${MYSQL_PASSWORD}'
      - 'MYSQL_DATABASE=nextcloud'
      - 'MYSQL_USER=nextcloud'

  collabora:
    image: collabora/code
    container_name: nextcloud-collabora
    restart: unless-stopped
    networks:
      - nextcloud
    ports:
      - "9980:9980"
    volumes:
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      - username=admin
      - password=${COLLABORA_PASSWORD}
      - domain=${NEXTCLOUD_FQDN}
      - dictionaries=en nl du fr
      - extra_params=--o:ssl.enable=true --o:ssl.termination=false # Set SSL options
    cap_add:
      - MKNOD
    tty: true

9.2.1. Nextcloud references and notes:

https://hub.docker.com/_/nextcloud/

Either use the Nextcloud fpm image or when the smbclient is desired, build the fpm image with smb included. In that case, add a folder nc_smb_image with a dockerfile that contains:

FROM nextcloud:fpm

RUN apt-get update && apt-get install -y procps smbclient && rm -rf /var/lib/apt/lists/*

Since I use samba network drives, I did not configure the Nextcloud data dir. If you do want to use local storage, uncomment NEXTCLOUD_DATA_DIR=/srv/nextcloud/data and don’t forget to change the path.

9.2.2. Collabora references and notes:

https://www.collaboraoffice.com/code/apache-reverse-proxy/

Add your desired dictionaries
SSL has been enables without termination. Although this should not be necessary, it was the only way to get it working. Disabling SSL will break the setup.

9.2.3. .env file

The above docker-compose file us making use of the below .env file. Tweak it and save it in the same folder as the docker-compose.yml

NEXTCLOUD_ROOT=/var/www
NEXTCLOUD_IPADDRESS=nextcloud ip address
NEXTCLOUD_FQDN=nextcloud.example.com
COLLABORA_FQDN=nextcloud.example.com
MYSQL_ROOT_PASSWORD=your very difficult password
MYSQL_PASSWORD=your also very difficult password
COLLABORA_PASSWORD=your yet another difficult password

In this Wiki NEXTCLOUD_FQDN and COLLABORA_FQDN are both set the same because I integrated Collabora with Nextcloud.

9.2.4. Run Nextcloud and Collabora docker containers

Now that both the docker-compose.yml and .env files are completed, run docker-compose up in the folder where you saved the files or docker-compose up -d to run in the background.

Once the downloading and compiling is done, run docker ps to confirm you have 3 docker containers running ie:

CONTAINER ID   IMAGE                       COMMAND                  CREATED       STATUS                 PORTS                                          NAMES
5091616dd9cd   collabora/code              "/bin/sh -c 'bash st…"   5 weeks ago   Up 5 weeks             0.0.0.0:9980->9980/tcp                         nextcloud-collabora
a2ff358b07f2   nextcloud_nextcloud         "/entrypoint.sh php-…"   5 weeks ago   Up 36 minutes          0.0.0.0:9000->9000/tcp                         nextcloud
e0e55215f4c6   mariadb                     "docker-entrypoint.s…"   5 weeks ago   Up 5 weeks             3306/tcp                                       nextcloud-mariadb

9.2.5. Configure Nexcloud config.php

Once you confirmed the docker containers are running, locate the file /var/www/html/config/config.php and update the trusted domains, trusted proxies and the overwrite.cli.url, ie:

  'trusted_domains' =>
  array (
    0 => 'nextcloud.example.com',
  ),
  'trusted_proxies' =>
  array (
    0 => '<nextcloud IP address>',
…
…
'overwrite.cli.url' => 'https://nextcloud.example.com:443',

Restart Nextcloud with docker restart nextcloud to apply the changes.

9.3. Vaultwarden

I highlight again that in this Wiki I have used Vaultwarden which is an independent and unofficial implementation of version of the official Bitwarden project. Official Bitwarden clients will work out of the box. However, if you do think you encountered an issue with the server itself or any of the official clients, report them to https://github.com/dani-garcia/vaultwarden/issues, NOT to the official Bitwarden project!

Copy and adjust the below docker-compose file and save it in e.g. ~/vaultwarden/docker-compose.yml

version: "3"
services:
  vaultwarden:
    restart: unless-stopped
    image: "vaultwarden/server:latest" # https://github.com/dani-garcia/vaultwarden/wiki/Using-Docker-Compose
    container_name: vaultwarden
    environment:
      - TZ=Europe/Amsterdam                # Timezone settings, important for Fail2ban to work
      - LOG_FILE=/data/bitwarden.log       # Logging connection attemps
      - EXTENDED_LOGGING='true'
      - LOG_LEVEL=warn
      - ROCKET_WORKERS=20
      - WEBSOCKET_ENABLED='true'
      - SIGNUPS_ALLOWED='false'            # Hardening a bit
      - DISABLE_ADMIN_TOKEN='false'
      - ADMIN_TOKEN=’your random string’
      - SHOW_PASSWORD_HINT='false'
      - DISABLE_ICON_DOWNLOAD='true'
    networks:
      - bitwarden_net
    ports:
      - 8080:80
      - 3012:3012
    volumes:
      - /opt/vaultwarden/bw-data:/data

networks:
  bitwarden_net:

9.3.1. Vaultwarden references and notes:

In this example config for security reasons the signups are disabled. New users can only signup after an invitation.
The admin console is enabled and can be reached within the LAN only via bitwarden.roadrunner/admin. Access through bitwarden.example.com has been blocked in the frontend Caddyfile.

Please visit the github repository for additional info about the setup.

Run docker-compose up or docker-compose up -d to start the Vaultwarden container. Now you should have 4 docker containers running.

9.4. Caddyfile

Now that all the docker containers are up and running, continue to setup Caddy on the backend to request local certificates from the frontend Caddy. The easiest way is to do this in the global section so that it applies for the whole Caddyfile:

# Global Option Block
debug
# TLS Options
acme_ca https://caddy.roadrunner/acme/local/directory
acme_ca_root /etc/ssl/certs/root.crt

To request a certificate, a root certificate from the server is required. Once you have started the Caddy service on the frontend for the first time and there are no errors, a file named root.crt is generated in .local/share/caddy/pki/authorities/local

NOTE: The absolute path for the certificate depends on how you run Caddy. Typically when you use systemd the absolute path will be /var/lib/caddy/.local/share/caddy/pki/authorities/local but if you run caddy with caddy start the path .local/share/caddy/pki/authorities/local

You will have to copy this file to the backend and point to it in the Caddyfile. In the above example, the file root.crt has been copied to /etc/ssl/certs/root.crt

Once this is done, you can start adding the local domains that host the services:

# Nextcloud and Collabora
nextcloud.roadrunner {
	encode gzip

	# enable HSTS
	header Strict-Transport-Security max-age=31536000;

	@collabora {
		path /loleaflet/* # Loleaflet is the client part of LibreOffice Online
		path /hosting/discovery # WOPI discovery URL
		path /hosting/capabilities # Show capabilities as json
		path /lool/* # Main websocket, uploads/downloads, presentations
	}
	handle @collabora {
		reverse_proxy https://127.0.0.1:9980 {
			header_up Host "nextcloud.example.com"
			transport http {
				tls_insecure_skip_verify
			}
		}
	}

	# .htaccess / data / config / ... shouldn't be accessible from outside
	@forbidden {
		path /.htaccess
		path /data/*
		path /config/*
		path /db_structure
		path /.xml
		path /README
		path /3rdparty/*
		path /lib/*
		path /templates/*
		path /occ
		path /console.php
	}
	handle @forbidden {
		respond 404
	}

	handle {
		root * /var/www/html
		php_fastcgi 127.0.0.1:9000 {
			# Tells nextcloud to remove /index.php from URLs in links
			env front_controller_active true
		}
		file_server
	}
}

9.4.1. Nextcloud and Collabora notes and references:

I also used @Basil post here which was very helpful getting Collabora to work.

The Collabora docker container has its own web server and therefore we can only forward the connection instead of using Caddy to act as a web server. While discussing the docker-compose file I mentioned that the SSL options for Collabora had to be enabled. Because an untrusted certificate is used, we need add tls_insecure_skip_verify to have Caddy accept the connection.

# Bitwarden
bitwarden.roadrunner {
	header {
		# Enable cross-site filter (XSS) and tell browser to block detected attacks
		X-XSS-Protection "1; mode=block"
		# Disallow the site to be rendered within a frame (clickjacking protection)
		X-Frame-Options "DENY"
		# Prevent search engines from indexing (optional)
		X-Robots-Tag "none"
	}

	# Notifications proxied to the websockets server
	handle /notifications/hub {
		reverse_proxy /notifications/hub 127.0.0.1:3012 {
			header_up Host "bitwarden.example.com"
		}
	}

	# Proxy everything else to Rocket
	handle {
		reverse_proxy 127.0.0.1:8080 {
			header_up Host "bitwarden.example.com"
		}
	}
}

9.4.2. Bitwarden notes and references:

The above setup is mostly copied from the Github wiki with the addition of the header_up Host.

10. Running and testing

At this point al(most) everything has been configured. It is time to test things.

  1. If not already, start Caddy on the frontend and backend with caddy run or caddy start (background).
  2. If not already, start all the docker containers by running docker-compose up -d in ~/nextcloud and ~/vaultwarden.

Verify that all 4 docker containers are running with the command docker ps if the answer is yes, lets try to connect to the services directly on the backend.

Lets test Nextcloud:
Browse to https://nextcloud.roadrunner and if all goes well you should see the following warning:

That is correct because you didn’t add the hostname nextcloud.roadrunner to the trusted list.

Now lets test Collabora:
First run curl -k https://localhost:9980 on the host where the Collabora docker container is running. It should return OK

Next browse to https://nextcloud.roadrunner/hosting/capabilities If you use Firefox, you should see something like the following:

Collabora_test1

Now browse to https://nextcloud.roadrunner/hosting/discovery and you will see a big list that starts like this:

<wopi-discovery>
<net-zone name="external-http">
<!-- Writer documents -->
<app favIconUrl="https://nextcloud.example.com/loleaflet/44a46d7/images/x-office-document.svg" name="writer">
<action default="true" ext="sxw" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="odt" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="fodt" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<!-- Text template documents -->
<action default="true" ext="stw" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="ott" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<!-- MS Word -->
<action default="true" ext="doc" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="dot" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<!-- OOXML wordprocessing -->
<action default="true" ext="docx" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="docm" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="dotx" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="dotm" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<!-- Others -->
<action default="true" ext="wpd" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="pdb" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="hwp" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="wps" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="wri" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="lrf" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="mw" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="rtf" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="txt" name="edit" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="fb2" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="cwk" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="pages" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="abw" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
<action default="true" ext="602" name="view" urlsrc="https://nextcloud.example.com/loleaflet/44a46d7/loleaflet.html?"/>
</app>

Very important to note is the urlsrc. This should be pointing to your public domain. If not, something is wrong with the Caddy setup.

Another test is to browse to https://nextcloud.roadrunner/loleaflet/dist/admin/admin.html and enter user:admin and the password that you provided in the .env file. If all goes well you will see the admin console:

Last to test is Vaultwarden.
Browse to https://bitwarden.roadrunner to find the login console.

If you managed to access all the services directly on the backend host, it is time to test if you can also access them throught the frontend. If you setup split DNS for your public domains, you can now browse to https://nextcloud.example.com and you should get the setup page for Nextcloud. Here you can complete the Nextcloud installation.

When you go https://bitwarden.example.com you get the login page for Bitwarden.

If you didn’t setup split DNS, the above may not work, depending on your network setup. In that case you have to try connecting from outside the LAN ie with a cellphone or with another internet connection. If you cannot connect from outside the LAN, probably something is wrong with your DNS.

Only one thing left to configure. When the Nextcloud installation has finalized, login as admin and go to Settings, Administration, Collabora Online Development E...

In the following screen you have to enter the URL of the Collabora Online-server. This is the same URL as your Nextcloud public domain. Ie nextcloud.example.com.

After clicking save you should have the green check. Now when you open or create an office document in Nextcloud, Collabora will be started and you can start editing your document.

If Collabora is not starting, first make sure that the domain in the URL is not being changed in your session. If it does, something is wrong in your Caddyfiles.

That is it! Now you have a setup that provides an ecrypted connection to your own hosted Nextcloud + Collabora or Vaultwarden services from any device with internet.

12. Final word and disclaimer

Well, if you made it until here, I guess a few words more won’t hurt :blush:
It took me quite some time to gather all the info required to create this setup. I hope it will set you off quickly. I tried to link as much as possible to my info sources. I did however left too much time in between to write and finalize this Wiki and I may have left some (crucial) things out unintentionally. Sorry if that happened but please let me know and I will try to update this Wiki. I’m also sure the setup can be improved on some points and it would be great to receive inputs for this.

7 Likes

11. Configuration files (full)

Frontend Caddyfile

# Global Options Block
{
	# General Options
	debug
}

# ACME Server
acme.roadrunner {
	acme_server
	tls internal
}

### REVERSE PROXY

## Nextcloud
nextcloud.example.com {
	reverse_proxy https://nextcloud.roadrunner {
		header_up Host {upstream_hostport}
	}
}

## Bitwarden
bitwarden.example.com {
	reverse_proxy https://bitwarden.roadrunner {
		header_up Host {upstream_hostport}
	}
	respond /admin* "The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it"
}

Backend Caddyfile

# Global Options Block
{
	# General Options
	debug

	# TLS Options
	acme_ca https://acme.roadrunner/acme/local/directory
	acme_ca_root /etc/ssl/certs/root.crt
}

# Nextcloud and Collabora
nextcloud.roadrunner {
	encode gzip

	# enable HSTS
	header Strict-Transport-Security max-age=31536000;

	@collabora {
		path /loleaflet/* # Loleaflet is the client part of LibreOffice Online
		path /hosting/discovery # WOPI discovery URL
		path /hosting/capabilities # Show capabilities as json
		path /lool/* # Main websocket, uploads/downloads, presentations
	}
	handle @collabora {
		reverse_proxy https://127.0.0.1:9980 {
			header_up Host "nextcloud.example.com"
			transport http {
				tls_insecure_skip_verify
			}
		}
	}

	# .htaccess / data / config / ... shouldn't be accessible from outside
	@forbidden {
		path /.htaccess
		path /data/*
		path /config/*
		path /db_structure
		path /.xml
		path /README
		path /3rdparty/*
		path /lib/*
		path /templates/*
		path /occ
		path /console.php
	}
	handle @forbidden {
		respond 404
	}

	handle {
		root * /var/www/html
		php_fastcgi 127.0.0.1:9000 {
			# Tells nextcloud to remove /index.php from URLs in links
			env front_controller_active true
		}
		file_server
	}
}

# Bitwarden
bitwarden.roadrunner {
	header {
		# Enable cross-site filter (XSS) and tell browser to block detected attacks
		X-XSS-Protection "1; mode=block"
		# Disallow the site to be rendered within a frame (clickjacking protection)
		X-Frame-Options "DENY"
		# Prevent search engines from indexing (optional)
		X-Robots-Tag "none"
	}

	# Notifications proxied to the websockets server
	handle /notifications/hub {
		reverse_proxy /notifications/hub 127.0.0.1:3012 {
			header_up Host "bitwarden.example.com"
		}
	}

	# Proxy everything else to Rocket
	handle {
		reverse_proxy 127.0.0.1:8080 {
			header_up Host "bitwarden.example.com"
		}
	}
}

Backend docker-compose files

~/nextcloud/docker-compose.yml

version: '3.7'

networks:
  nextcloud:

services:
  nextcloud:
    # image: nextcloud:fpm
    build: ./nc_smb_image
    container_name: nextcloud
    restart: unless-stopped
    networks:
      - nextcloud
    ports:
      - "9000:9000"
    volumes:
      - ${NEXTCLOUD_ROOT}/html:/var/www/html
      - ${NEXTCLOUD_ROOT}/data:/srv/nextcloud/data
    depends_on:
      - mariadb
    environment:
      - NEXTCLOUD_TRUSTED_DOMAINS='${NEXTCLOUD_FQDN}'
      # - NEXTCLOUD_DATA_DIR=/srv/nextcloud/data
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_HOST=nextcloud-mariadb

  mariadb:
    image: mariadb
    container_name: nextcloud-mariadb
    restart: unless-stopped
    networks:
      - nextcloud
    volumes:
      - ${NEXTCLOUD_ROOT}/mariadb:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

  collabora:
    image: collabora/code
    container_name: nextcloud-collabora
    restart: unless-stopped
    networks:
      - nextcloud
    ports:
      - "9980:9980"
    volumes:
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      - username=admin
      - password=${COLLABORA_PASSWORD}
      - domain=${NEXTCLOUD_FQDN}
      - dictionaries=en nl du fr
      - extra_params=--o:ssl.enable=true --o:ssl.termination=false # Set SSL options
    cap_add:
      - MKNOD
    tty: true

~/nextcloud/nc_smb_image/Dockerfile

FROM nextcloud:fpm

RUN  apt-get update -y && apt-get install -y  smbclient

~/nextcloud/.env

NEXTCLOUD_ROOT=/var/www
NEXTCLOUD_IPADDRESS=nextcloud ip address
NEXTCLOUD_FQDN=nextcloud.example.com
COLLABORA_FQDN=nextcloud.example.com
MYSQL_ROOT_PASSWORD=your very difficult password
MYSQL_PASSWORD=your also very difficult password
COLLABORA_PASSWORD=your yet another difficult password

~/bitwarden_rs/docker-compose.yml

version: "3"
services:
  bitwardenrs:
    restart: unless-stopped
    image: "bitwardenrs/server:latest" # Dani Garcia image https://github.com/dani-garcia/bitwarden_rs
    container_name: bitwardenrs
    environment:
      - TZ=Europe/Amsterdam                # Timezone settings, important for Fail2ban to work
      - LOG_FILE=/data/bitwarden.log       # Logging connection attemps
      - EXTENDED_LOGGING='true'
      - LOG_LEVEL=warn
      - ROCKET_WORKERS=20
      - WEBSOCKET_ENABLED='true'
      - SIGNUPS_ALLOWED='false'            # Hardening a bit
      - DISABLE_ADMIN_TOKEN='false'
      - ADMIN_TOKEN=some very long and complicated super secret password string
      - SHOW_PASSWORD_HINT='false'
      - DISABLE_ICON_DOWNLOAD='true'
    networks:
      - bitwarden_net
    ports:
      - 8080:80
      - 3012:3012
    volumes:
      - /opt/bitwarden_rs/bw-data:/data

networks:
  bitwarden_net:
3 Likes

Thanks for this, I think it would be helpful in the Nextcloud section to add a configuration for a notify_push reverse proxy.

That’s indeed interesting. I wasn’t aware of this function. I’ll look into it and add it as soon as I can.

Hi, I basically used your wiki as a baseline for my own home server setup.

Why have you decided to run your frontend (reverse proxy) caddy not as a Docker container? Why do you think it complicates the certification renewal?

Because, actually, this is how my setup looks like currently. I also have the frontend caddy running in a container. It works quite nice and I have no issues (more or less). It even simplifies the local DNS stuff, since within Docker networks all containers can be reached by their name.

However, I now realize that in this case I don’t see the real remote IP address of the clients connecting to any of my backend services via the fronted/proxy caddy.
Since the frontend caddy is running in a container in a Docker bridge network, the remote address is always the IP address of the bridge network gateway.

So maybe you or someone else knows how to overcome this issue with the bridged remote IP addresses?
I thought about using the host network mode but that does not seem to be the ideal solution (e.g. it weakens network security).

This is due to Docker’s userland proxy, which you can disable (there’s a daemon option to turn it off).

A post was split to a new topic: Help with getting Caddy working with Nextcloud Collabra Bitwarden

Digging up this old topic because I finally switched off the userland proxy today and that’s it. Totally simple and quickly done.

By the way, there’s a blog post on that topic explaining the background and containing detailed instructions:

2 Likes

Heads up here! Collabora recently changed their configuration, so this guide needs some tweaking.

  1. domain environment variable should be changed to server_name (although not sure if this var is needed)
  2. From Caddyfile loleaflet should be renamed to browser, lool should be renamed to cool

docker.compose.yml:

  collabora:
    image: collabora/code
    container_name: collabora
    restart: unless-stopped
    ports:
      - "9980:9980"
    volumes:
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      # Credentials for /browser/dist/admin/admin.html
      - username=${USER}
      - password=${PASSWORD}
      - server_name=${NEXTCLOUD_FQDN}
      - dictionaries=en_US
      - extra_params=--o:ssl.enable=true --o:ssl.termination=false # Set SSL options
    cap_add:
      - MKNOD
    tty: true

Caddyfile:

nextcloud.{$DOMAIN} {
	encode zstd gzip

	@collabora {
		path /browser/* # Browser is the client part of LibreOffice Online
		path /hosting/discovery # WOPI discovery URL
		path /hosting/capabilities # Show capabilities as json
		path /cool/* # Main websocket, uploads/downloads, presentations
	}

	reverse_proxy @collabora collabora:9980 {
		header_up Host "nextcloud.{$DOMAIN}"
		transport http {
			tls_insecure_skip_verify
		}
	}

	root * /var/www/html
	file_server

	php_fastcgi nextcloud:9000 {
		env front_controller_active true # Remove index.php form url
	}
	
	header {
		# enable HSTS
		Strict-Transport-Security "max-age=31536000;"
	}
	redir /.well-known/webfinger /public.php?service=webfinger 301
	redir /.well-known/carddav /remote.php/dav 301
	redir /.well-known/caldav /remote.php/dav 301
	@forbidden {
		path /.htaccess
		path /data/*
		path /config/*
		path /db_structure
		path /.xml
		path /README
		path /3rdparty/*
		path /lib/*
		path /templates/*
		path /occ
		path /console.php
	}
	respond @forbidden 403
}
1 Like

Thanks @arvigeus - you can edit the original post to make these improvements, since it’s a wiki post.

(If you don’t have permission to do so, let me know and I’ll bump your trust level)

I don’t see an edit button. But I also don’t see edit button for my own post…

Can you do the changes? I am just passing by here… :slight_smile:

Done! Hope that worked. Let me know if you still don’t see the button.

FYI, Looks like bitwarden_rs is now vaultwarden

1 Like