Overview
This post explains how to set up a Matrix Server using Docker-Compose, turn it into a hub for encrypted notifications, and make it accessible on the internet via a Cloudflare tunnel using Cloudflare’s Zero Trust platform.
Short Cybersecurity Note
Matrix Servers can enhance homelab or business cybersecurity, particularly through encryption. Encryption is crucial for anyone studying for the CISSP exam.
Encryption safeguards confidentiality, one of the core principles of the CIA triad. It transforms readable data (plaintext) into unreadable ciphertext using an algorithm and a secret key. Decryption, or making the data readable again, requires this key, ensuring that only authorized individuals with the key can access the original information. Encryption is a cornerstone of security for email, online banking, file sharing, and more.
What is a Matrix Server?
Matrix is a communication protocol designed for decentralized and federated chat and real-time communication applications. It offers a secure and versatile messaging platform that allows users to connect irrespective of their app, device, or service provider.
Matrix servers form a decentralized network, enabling communication between users on different servers regardless of their chosen applications or providers. This interconnected system is called “federation.”
Security is paramount: Matrix servers employ end-to-end encryption, encrypting messages from sender to recipient. This means that not even server administrators can read the content, guaranteeing user privacy.
Users can choose between self-hosting their Matrix server or utilizing a third-party provider. The Matrix protocol enjoys broad support from various applications, including Element, the author’s preferred choice.
What is a “Cloudflared” Tunnel?
A Cloudflared tunnel, a Cloudflare-developed command-line tool, allows users to securely expose HTTP and HTTPS services running on their servers to the internet. It achieves this by establishing a secure, encrypted tunnel between the server and Cloudflare’s edge network, which acts as a reverse proxy.
Users can access services on private networks through a public URL, eliminating the need to directly expose the service to the internet. This approach enhances security by channeling all access through the encrypted tunnel, shielding the service from direct exposure.
What is Cloudflare’s Zero Trust Interface?
Cloudflare’s Zero Trust solution is a security framework that assumes all network traffic, users, and devices are untrustworthy. It revolves around a Zero Trust Access (ZTA) interface, providing a secure method for managing and controlling access to organizational resources.
The ZTA interface ensures that only verified and authorized users, devices, and applications can interact with resources. It accomplishes this through stringent authentication and customizable access control policies tailored to each organization’s specific requirements.
The ZTA interface operates by establishing a secure, encrypted, and authenticated tunnel between the user’s device and the organization’s network, permitting traffic flow only between approved endpoints. This safeguards data even in the event of a compromised device or intercepted traffic.
Furthermore, Cloudflare’s ZTA interface boasts sophisticated monitoring and analytics tools. These tools empower administrators to track user behavior, identify potential threats, and respond promptly to security incidents, minimizing potential damage.
Setting Up the Environment
This project utilizes an overprovisioned (4 core/4GB) Ubuntu 22.10 virtual machine running on an ESXi server. The Matrix server stack comprises several Docker containers: Synapse (the Matrix server), Postgres DB (for enhanced performance), Element (the user interface), Webhooks (for receiving webhooks), Maubot (for GitHub and RSS integration), another Postgres DB (for Maubot), Nginx Proxy Manager (and its database), and Postfix (for sending emails through GMail SMTP).
While only the Synapse container and a user interface like Element are strictly necessary, the author includes additional containers for a customized setup. Note that these configurations are specific to the author’s stack and may require adjustments for different setups.
Requirements
- An Ubuntu Host
- Docker and Docker-Compose
- A domain name
- A Cloudflare instance
- A working Cloudflare tunnel
For convenience, all required files and a secrets directory are available on GitHub. Download both the files and the secrets directory to your Ubuntu host.
Remember to generate and use secure passwords throughout the tutorial. A password manager is highly recommended.
Modify the docker-compose.yml
The downloaded docker-compose.yml file requires modifications. Initially, the Maubot, Maubot Postgres, and Webhooks containers are commented out and are best configured after the Matrix server is up and running.
Start by editing the Synapse container section. Replace YOURSERVERNAME (Lines 16 and 30) with your domain name and YOURSECRETPASSWORD (Line 22) with a secure database password.
Next, in the postgres section below synapse, replace YOURSECRETPASSWORD (Line 39) with the same database password used above.
If using Maubot, replace YOURSECRETPASSWORD (Line 108) with a new secure password, which you’ll need later for the maubot/config.yaml file.
For those using postfix for email relay, adjust Lines 120-124 to match your SMTP relay service. The author uses GMail with an application-specific password. If you don’t have an SMTP relay, consider a containerized SMTP server.
The Webhooks container configuration can be skipped for now as it requires channel IDs.
Comment out or remove any unwanted containers, save the docker-compose.yml file, and exit.
Modify the element-config.json
Replace YOURDOMAIN.COM on Lines 4 and 5 with your domain name, save, and exit.
Modify the homeserver.yaml
This file is the most intricate and allows for extensive customization. This guide will cover the author’s specific settings.
Replace all instances of YOURDOMAIN.COM with your actual domain name.
On Line 33, substitute SAME-PASSWORD-DEFINED-IN-DOCKERCOMPOSE with the password used for the postgres database in the docker-compose.yml file.
Generate strong, unique passwords for Lines 61, 70, and 71.
If not using postfix, adjust Lines 88-103 according to your email configuration.
Save and close the file.
Modify secrets/matrix_admin_pass.txt
Within the secrets directory, open matrix_admin_pass.txt and replace YOUR_VERY_SECURE_PASSWORD with a strong, secure password.
Modify secrets/synapse_secrets.env
In the synapse_secrets.env file within the secrets directory, replace YOURDOMAIN.COM with your domain name.
Modify maubot/config.yaml
In maubot/config.yaml, replace THEPOSTGRESDB_PASSWORD_FROM_DOCKERCOMPOSE on Line 6 with the Maubot Postgres database password you saved earlier.
Set strong passwords for Lines 63, 72, and 75.
Replace YOURSERVERNAME.TLD on Lines 53 and 71 with your domain. Save and close the file.
Start the Matrix Server
Navigate to the directory with the docker-compose.yml file in your terminal and run:
|
|
Configure Cloudflare
The Cloudflared Tunnel
This section assumes you have a working Cloudflare tunnel. In the Cloudflare Zero Trust Dashboard, navigate to Access > Tunnels. Choose your tunnel and click Configure.
Add your matrix.yourdomain.tld to the tunnel. When adding the hostname, use “matrix” for the subdomain and select your domain. Input the following path:
|
|
Choose HTTP as the Service Type and enter your Matrix Server’s local IP address with port 8008 (e.g., 10.99.100.50:8008). Save the hostname.
Duplicate this hostname entry, but this time, use your root domain (e.g., yourdomain.tld) without any subdomain.
Repeat the process for element.yourdomain.tld, and if applicable, maubot.yourdomain.tld and webhooks.yourdomain.tld. Note that these don’t require the specific path mentioned earlier. The correct ports for each service are:
|
|
The Cloudflare Worker
To enable federation with the Cloudflared tunnel, a Cloudflare Worker is necessary to serve the required .well-known file.
In the Cloudflare Dashboard, go to Workers and create a new service (e.g., matrix). Use the quick edit function and replace the default code with the following, replacing matrix.yourdomain.tld with your domain:
|
|
Save and deploy the worker. Return to the worker overview, go to “Custom Domains,” and add a custom domain like federation.yourdomain.tld.
Under “Routes,” add the following routes, making sure they point to the zone corresponding to your domain:
|
|
(Optional) Nginx Proxy Manager (NPM) Configuration
NPM configuration is optional if using split-brain DNS for local access. If you already have NPM, configure matrix.yourdomain.tld and element.yourdomain.tld to point to it.
Running a New NPM Instance
If you need NPM, uncomment the relevant lines in the docker-compose.yml and restart the stack with docker-compose up -d
. Refer to the author’s previous post on NPM for setup instructions.
Configuring NPM
In NPM, create proxy hosts for matrix.yourdomain.tld, element.yourdomain.tld, and yourdomain.tld. Add hosts for webhooks.yourdomain.tld and maubot.yourdomain.tld if you are using those services.
For each host, configure it to forward to the corresponding local IP address and port:
|
|
For SSL, request a new certificate from Let’s Encrypt.
Accessing Your Server
You should now be able to access your Matrix server by visiting element.yourdomain.tld. Click Edit next to the auto-filled server name and input matrix.yourdomain.tld.
Finally, sign in with the admin username (default: admin) from the docker-compose.yml and the corresponding password from /synapse-secrets/matrix_admin_pass.txt.
You should now have access to your new Matrix server through Element.