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.roadrunner
→ 192.168.0.2
to reach the frontend Caddy service
nextcloud.roadrunner
→ 192.168.0.4
to reach Nextcloud service
bitwarden.roadrunner
→ 192.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.com
→ 192.168.0.2
to redirect to the Caddy frontend
bitwarden.example.com
→ 192.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.
- If not already, start Caddy on the frontend and backend with
caddy run
orcaddy start
(background). - 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:
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
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.