10 KiB
title, description
| title | description |
|---|---|
| Cloudflare Zero Trust | Use Cloudflare Tunnels and Zero Trust to expose homelab services without opening ports, configure SWAG and manage multiple tunnels. |
:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}
Introduction
The Zero Trust architecture is the practice of designing systems based on the principle of "never trust, always verify", as opposed to the traditional principle of "trust, but verify". This concept has become increasingly popular recently due to the growing number of attacks targeting user data. It’s a broad concept, but we’ll focus on how to apply Zero Trust to the web services we host.
Cloudflare tunnels offer a simple way to implement Zero Trust, using SWAG and Authentik.
Simply put, Cloudflare Tunnels allow you to:
- Hide your server’s IP (and your home IP if it's self-hosted)
- Authenticate traffic
- Benefit from Cloudflare protections (DDoS attacks, blacklists, malicious requests, etc.)
- Use Cloudflare's CDN to cache and speed up your websites
- Avoid opening router ports for services exposed by SWAG
Here we’ll explain how to integrate SWAG with Cloudflare tunnels.
::warning Warning:
- Do not use Cloudflare tunnels to expose a mail server
- Do not use Cloudflare tunnels to expose a video service like Jellyfin. Unlike Plex, Jellyfin has no cloud relay and is exposed directly through SWAG in this guide, so make sure it stays behind plain port forwarding rather than a Cloudflare tunnel
- Do not use Cloudflare tunnels for the BitTorrent protocol (if you followed this guide, everything is fine) ::
Cloudflare Configuration
DNS Zone
First, you need to set Cloudflare as your DNS zone manager. If you bought your domain from Cloudflare, that’s already done. Otherwise, check with your registrar how to add external DNS servers. Cloudflare provides step-by-step documentation on how to configure a DNS Zone, whether your domain is external or registered with Cloudflare.
If you only have one server to protect behind Cloudflare, you can delete all existing DNS records. By default, your domain and all its subdomains will be redirected to the tunnel.
If you have subdomains pointing to other servers, you can still define them in the DNS zone using A records.
If you have several servers and tunnels under one domain, see here.
API Key
Start by creating a new Cloudflare API token and retrieving your zone and account IDs.
On your Cloudflare dashboard, on your domain overview page, you’ll see the zone and account IDs at the bottom right. Save both securely.
Just below that is a link titled Get your API token. Click it. The token scope must include Zone:DNS:Edit and Account:Cloudflare Tunnel:Edit. Your page should look like the screenshot below.
Once created, your token will only be shown once. Save it securely, as it cannot be viewed again later.
Cloudflare Zero Trust
You must register for Cloudflare Teams to access the Zero Trust dashboard that manages tunnels and access policies. This is a premium service, but there’s a free plan for up to 50 users, perfect for a home lab. Keep in mind that a valid credit card is required to register, but the free plan incurs no charges.
Register via this link.
SWAG Configuration
::note
This guide assumes you own mondomaine.fr and that its DNS is correctly pointing to Cloudflare, as described above.
::
SWAG supports two Docker Mods:
- Cloudflared, the container used to create and manage tunnels
- Cloudflared Real IP, which allows SWAG to receive the true source IP of incoming requests instead of Docker’s internal IP (important for IP geolocation mods like DBIP).
These two mods, merged into the SWAG container, require some configuration.
::steps{level="3"}
Configure the tunnel
Create a file tunnelconfig.yml to reference in your SWAG compose.yaml.
::tip{icon=""} ✨ Tip: Use File Browser to navigate and edit files instead of using the terminal. ::
sudo nano /docker/swag/config/tunnelconfig.yml
Paste:
ingress:
- hostname: mondomaine.fr
service: https://mondomaine.fr
- hostname: "*.mondomaine.fr"
service: https://mondomaine.fr
- service: http_status:404
Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.
Configure Cloudflare Real IP
Now configure Cloudflare Real IP.
Open the nginx.conf file:
sudo nano /docker/swag/config/nginx/nginx.conf
Add the following at the end of the http section:
real_ip_header X-Forwarded-For;
real_ip_recursive on;
include /config/nginx/cf_real-ip.conf;
set_real_ip_from 127.0.0.1;
Save with :kbd{value="Ctrl+O"}, then :kbd{value="Enter"}, and exit with :kbd{value="Ctrl+X"}.
Deploy the SWAG stack
In Dockge, edit your SWAG stack with this:
---
services:
swag:
image: lscr.io/linuxserver/swag:latest
container_name: swag
cap_add:
- NET_ADMIN
env_file:
- .env
environment:
- DOCKER_MODS=linuxserver/mods:swag-dbip|linuxserver/mods:swag-dashboard|linuxserver/mods:swag-auto-reload|linuxserver/mods:universal-cloudflared|linuxserver/mods:swag-cloudflare-real-ip
- PUID=${PUID}
- PGID=${PGID}
- TZ=Europe/Paris
- URL=${DOMAIN}
- SUBDOMAINS=wildcard
- VALIDATION=dns
- DNSPLUGIN=${PLUGIN}
- EMAIL=${EMAIL}
- CF_ZONE_ID=${ZONE_ID}
- CF_ACCOUNT_ID=${ACCOUNT_ID}
- CF_API_TOKEN=${API_TOKEN}
- CF_TUNNEL_NAME=${TUNNEL_NAME}
- CF_TUNNEL_PASSWORD=${TUNNEL_PW}
- FILE__CF_TUNNEL_CONFIG=/config/tunnelconfig.yml
extra_hosts:
- ${DOMAIN}:127.0.0.1
ports:
- 81:81
volumes:
- /docker/swag/config:/config
- /docker/swag/config/fail2ban/fail2ban.sqlite3:/dashboard/fail2ban.sqlite3:ro
restart: unless-stopped
::tip{icon=""} ✨ Tip: Add a Watchtower label to automate updates:
labels:
- com.centurylinklabs.watchtower.enable=true
::
Fill in your .env file:
PUID=
PGID=
DOMAIN=
PLUGIN=
EMAIL=
ZONE_ID=
ACCOUNT_ID=
API_TOKEN=
TUNNEL_NAME=
TUNNEL_PW=
| Variable | Value | Example |
|---|---|---|
PUID |
User ID (id username) |
1000 |
GUID |
Group ID (id username) |
1000 |
DOMAIN |
Your reserved domain | mondomaine.fr |
PLUGIN |
DNS provider (also configure cloudflare.ini) |
cloudflare |
EMAIL |
Email for the certificate | [email protected] |
ZONE_ID |
Cloudflare Zone ID | aNhcz1l3JfWbFZo2XMpzQlP2iOqk |
ACCOUNT_ID |
Cloudflare Account ID | buKsjNHLyzKMM1qYnzOy4s7SHfly |
API_TOKEN |
API token | 53ydYus9TFFk1DOXNdP87iIcJtQjoW |
TUNNEL_NAME |
Tunnel name | my_tunnel |
TUNNEL_PW |
Strong, random password | iSzKRmP4VbnlsMvdSdgBEJiJi |
Once done, deploy the stack. Check the logs: you should reach server ready.
Then confirm your tunnel appears under Networks > Tunnels in Cloudflare Zero Trust. By default, all subdomains will be routed through the tunnel, no need to define them in your DNS zone.
::tip{icon=""}
✨ Tip: If you want to expose a service without a tunnel, just define an A record in your DNS zone. If resolution fails, disable the proxy function for that record, e.g. for sub.mondomaine.fr.
::
Done !
::
Managing Multiple Tunnels for Multiple Servers
By default, all subdomains of your domain are routed through the single tunnel. But if you have a second server, just change the tunnel name in that SWAG instance, and redirect subdomains to the correct tunnel in your DNS zone.
::steps{level="3"}
Change the tunnel name
In the second server's SWAG stack, set a different TUNNEL_NAME in the .env file, then redeploy.
Find the tunnel IDs
Go to Networks > Tunnels in Cloudflare Zero Trust and note the tunnel IDs:
Add CNAME records
In the Cloudflare DNS dashboard, click your domain name, then Add Record and add these two CNAME records (include .cfargotunnel.com):
| Type | Name | Target |
|---|---|---|
CNAME |
subdomain1 |
yourtunnelid1.cfargotunnel.com |
CNAME |
subdomain2 |
yourtunnelid2.cfargotunnel.com |
Done !
::
If you have many subdomains, point them to the above reference subdomains.
This way, if a tunnel ID changes, you only update one DNS record.
Example:
sub1andsub2also point to the server behindsubdomain1:
| Type | Name | Target |
|---|---|---|
CNAME |
sub1 |
subdomain1 |
CNAME |
sub2 |
subdomain1 |
sub3andsub4point to the server behindsubdomain2:
| Type | Name | Target |
|---|---|---|
CNAME |
sub3 |
subdomain2 |
CNAME |
sub4 |
subdomain2 |


