The further you get into the madness of the home lab, the more you wonder how to access your carefully orchestrated services from outside your home. One quick, secure, and easy way to do this is with a Cloudflare Tunnel, a Zero Trust portal between your private network and the internet, without opening any firewall ports. That also gives your home lab the protection of Cloudflare's DDoS mitigation and obfuscates your IP address, making you safer.
What you don't get is user authentication for your apps, at least not by default. While you could use Cloudflare Access for user authentication, I prefer running local auth, and we will use Authelia for this purpose. This gives us multi-layered security; we all know that the more layers, the better. Plus, it means user authentication is forced even from the local network or any other access point, since the Cloudflare Tunnel could be a single point of failure.
I use Docker Desktop with no coding experience and here's what I've learned
It's possible, but there are some hurdles
Cloudflare Tunnel is a handy tool
With one big problem for using them long-term
Networking is hard enough when it's a small home network, but since the same concepts apply to WAN connections, it's incredibly technical all the way around. For the longest time, VPNs were the best bet for accessing a LAN while outside it, but anyone who's used a VPN for work environments knows that they're hard to set up, harder to keep running smoothly, and harder to troubleshoot when something goes wrong.
Modern networking tools are different, whether it's a WireGuard-powered VPN or a Cloudflare Tunnel, as they're designed to be transparent to the user, making the setup of what used to be a complex tool a matter of a few clicks. You do need a domain name for Cloudflare Tunnel to work, but that could be a local domain or one you've purchased from Cloudflare or another domain registrar.
Cloudflare Tunnel has no authentication by default
Setting up a Cloudflare Tunnel gives you a publicly accessible URL to access your home network (or wherever you pointed it to) with. That's good for ease of use, but not so good for security as, by default, anyone with that link can access your private network. You can set up Cloudflare Access for SSO and authentication to use the Tunnel, but that doesn't protect the other end, so if someone gets access to your Tunnel, they can use your private information and apps.
How are Cloudflare tunnels different from a VPN?
Both let you access your services remotely but they do it in very different ways.
Adding an authentication server was the missing piece
Authelia adds Single Sign-On and authentication for your self-hosted containers
User authentication is essential to the security and safety of your data and network, and unfortunately, it's often an afterthought for self-hosted projects. Many guides tend to give you just enough to get running, and the few that handle authentication or identity management are hard to find. That's partly why I like Netbird, because when you self-host the process is to build the identity management layer first, so the remote access tool is secure from the start.
But we're not using that today. It's time to build a custom version with Cloudflare, Authelia, SWAG (for reverse proxy duties) and a few random Docker services, for example's sake. Docker containers are fun because you can stack multiple services into one compose file, and the stack will build itself when you bring the container up. For this we're adding the following together:
- Multiple services reverse proxied by SWAG
- Authentication handled by Authelia (with integration into SWAG)
- 2FA/MFA handled by Duo
Now everything going down the Cloudflare Tunnel will have to authenticate with MFA before they can use the other Docker containers running on my NAS.
Time for some compose magic
Setting up Zero Trust links to your home network is one of the safest ways to do things, but there's a lot that can go wrong here. You'll need your Cloudflare Zone, Account, and API tokens, and if anyone has those they can use the API to control your Cloudflare account, so keep those secret. You'll want a Cloudflare API Token with Zone:DNS:Edit and Account:Cloudflare Tunnel:Edit permissions.
I'm not sharing my completed compose file for that reason, but I'll walk through the steps.
version: "1.0"
services:
swag:
image: lscr.io/linuxserver/swag
container_name: swag
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- URL=your-url.com
- VALIDATION=dns
- SUBDOMAINS=wildcard
- DNSPLUGIN=cloudflare
- DOCKER_MODS=linuxserver/mods:swag-auto-proxy|linuxserver/mods:universal-docker|linuxserver/mods:universal-cloudflared
- DOCKER_HOST=dockerproxy
- CF_ZONE_ID=YOUR_CLOUDFLARE_ZONE_ID
- CF_ACCOUNT_ID=YOUR_CLOUDFLARE_ACCOUNT_ID
- CF_API_TOKEN=YOUR_CLOUDFLARE_API_KEY
- CF_TUNNEL_NAME=your-url.com
- CF_TUNNEL_PASSWORD=your_long_password_goes_here
- FILE__CF_TUNNEL_CONFIG=/config/tunnelconfig.yml
extra_hosts:
- your-url.com:127.0.0.1
volumes:
- /volume1/docker/swag:/config
restart: unless-stopped
dockerproxy:
image: lscr.io/linuxserver/socket-proxy
container_name: dockerproxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- POST=0
restart: unless-stopped
authelia:
image: ghcr.io/authelia/authelia:4.34.6
container_name: authelia
user: "1000:1000"
environment:
- TZ=America/New_York
volumes:
- /volume1/docker/authelia:/config
restart: unless-stopped
overseerr:
image: lscr.io/linuxserver/overseerr
container_name: overseerr
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /volume1/docker/overseerr:/config
labels:
- swag=enable
restart: unless-stopped
tautulli:
image: lscr.io/linuxserver/tautulli
container_name: tautulli
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /volume1/docker/tautulli:/config
labels:
- swag=enable
- swag_auth=authelia
restart: unless-stopped
Fill in your details for things like your-url.com and your Cloudflare details, and as we're using an external file for the Cloudflare Tunnel config, go and make /docker/swag/tunnelconfig.yml with the following content:
ingress:
- hostname: your-url.com
service: https://your-url.com
- hostname: "*.your-url.com"
service: https://your-url.com
- service: http_status:404
While you're here, there are two other things that are unique to running this on Synology DSM that you won't have issues with on Linux or Windows. First is that Synology doesn't like you doing Docker things with your admin account, so you'll want to set up a dockergroup, give it permissions to the docker and data shared folders that you'll make to store containers in, deny it permissions to every app, and then create a dockerlimited user that gets added to the dockergroup we created.
id dockerlimited
Then you want to SSH into the NAS, and run the above command, which will give you uid= and gid= numbers. For me this was 1028 and 65537 (the other two GID numbers are the normal user and groups setting).
uid=1028(dockerlimited) gid=100(users)groups=100(users),65537(dockergroup)
You'll want these numbers to put into your docker compose file wherever PUID and PGID are found, so that Docker has the necessary permissions to create the new files it needs.
Also, you'll need to manually add the /volume1/docker/overseerr/config folders for each container, Synology is hit-and-miss whether it works otherwise.
Be warned—Authelia is more involved than many other SSO providers
We've used our fair share of authentication servers at XDA, and the one I'm most familiar with is Zitadel, as that's the default identity provider used by Netbird. Authentik is another good sign-in solution, and the UI-based management might be a little easier than Authelia for those who aren't as deeply knowledgeable about Docker and coding.
I wanted to share the configuration.yml and users_database.yml here, but something in the code doesn't want to embed, so I'm going to link to the Linuxserver.io site which has a good explainer of how this connects. It's a lot, including generating alphanumeric secret keys, setting up 2FA through Duo Mobile to get the api hostname, integration key, and secret key, setting up Gmail SMTP so you can get emails for password resets, and a second file with a hashed password for the user. You'll want an Argon2id hash generator for the user password, and match the settings in the Authelia configuration.yml file.
-
Cloudflare Tunnels
-
Authelia
Success
Now we're cooking with gas
Now when we do docker compose up -d or use the Build command in Synology Container Manager, it will pull all the Docker images, start the containers, and SWAG will download the mods it needs. Once the SWAG container is running, we need to stop it for a minute and go uncomment two lines in the default.conf file so that the Authelia server is used by SWAG.
#include /config/nginx/authelia-server.conf;
We've already set up the Authelia user, so it's a question of logging in and handling the 2FA we set up through Duo. And then we can use our services through the Tunnel, with authentication and two factor, from anywhere.
I feel safer knowing the apps I access via Cloudflare Tunnel also need authentication
Honestly, this was a lot, but security should never be done in half-measures, and I feel better knowing the ingress of my network is through a Tunnel, and not a gaping hole. Now what to watch, since I've got Plex and a couple of my fav tools running.
