263 lines
9.9 KiB
Markdown
263 lines
9.9 KiB
Markdown
---
|
||
navigation: true
|
||
title: Cloudflare Zero Trust
|
||
main:
|
||
fluid: false
|
||
---
|
||
:ellipsis{left=0px width=40rem top=10rem blur=140px}
|
||
# Cloudflare Zero Trust
|
||
|
||
::alert{type="info"}
|
||
🎯 __Goals:__
|
||
- Understand the concept of Cloudflare Tunnels
|
||
- Configure your Cloudflare account
|
||
- Configure SWAG
|
||
- Manage multiple tunnels
|
||
::
|
||
|
||

|
||
|
||
## 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](/serveex/core/swag) and [Authentik](/serveex/security/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.
|
||
|
||
::alert{type="warning"}
|
||
:::list{type="warning"}
|
||
- __Warning:__
|
||
:::
|
||
- Do not use Cloudflare tunnels to expose a mail server
|
||
- Do not use Cloudflare tunnels to expose a video service like Plex (if you followed [this guide](/serveex/media/plex), Plex is not exposed, so it’s fine)
|
||
- Do not use Cloudflare tunnels for the BitTorrent protocol (if you followed [this guide](/serveex/media/qbittorrent), everything is fine)
|
||
::
|
||
|
||
## Cloudflare Configuration
|
||
---
|
||
### DNS Zone
|
||
|
||
First, you need to set Cloudflare as your [DNS zone](/general/networking/dns) 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](https://developers.cloudflare.com/dns/zone-setups/full-setup/setup/) 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](http://192.168.7.80:8005/serveex/cloudflare/#gerer-plusieurs-tunnels-pour-plusieurs-serveurs).
|
||
|
||
### 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](https://dash.teams.cloudflare.com/).
|
||
|
||
## SWAG Configuration
|
||
---
|
||
::alert{type="info"}
|
||
:::list{type="info"}
|
||
- 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.
|
||
|
||
### Tunnel Configuration
|
||
|
||
Create a file `tunnelconfig.yml` to reference in your SWAG `compose.yaml`.
|
||
|
||
::alert{type="success"}
|
||
✨ __Tip:__ Use [File Browser](/serveex/files/file-browser) to navigate and edit files instead of using the terminal.
|
||
::
|
||
|
||
```shell
|
||
sudo vi /docker/swag/config/tunnelconfig.yml
|
||
```
|
||
|
||
Press `i` to enter insert mode and paste:
|
||
|
||
```yaml
|
||
ingress:
|
||
- hostname: mondomaine.fr
|
||
service: https://mondomaine.fr
|
||
- hostname: "*.mondomaine.fr"
|
||
service: https://mondomaine.fr
|
||
- service: http_status:404
|
||
```
|
||
|
||
Press `Esc`, then save and exit with `:x` and `Enter`.
|
||
|
||
### Cloudflare Real IP Configuration
|
||
|
||
Now configure _Cloudflare Real IP_.
|
||
|
||
Open the `nginx.conf` file:
|
||
|
||
```shell
|
||
sudo vi /docker/swag/config/nginx/nginx.conf
|
||
```
|
||
|
||
Press `i` and add the following at the end of the `http` section:
|
||
|
||
```nginx
|
||
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 and exit with `:x`.
|
||
|
||
### Docker Compose
|
||
|
||
In Dockge, edit your SWAG stack with this:
|
||
|
||
```yaml
|
||
---
|
||
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
|
||
```
|
||
|
||
::alert{type="success"}
|
||
✨ __Tip:__ Add a Watchtower label to automate updates:
|
||
|
||
```yaml
|
||
labels:
|
||
- com.centurylinklabs.watchtower.enable=true
|
||
```
|
||
::
|
||
|
||
Fill in your `.env` file:
|
||
|
||
```properties
|
||
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 | `you@email.com` |
|
||
| `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](https://one.dash.cloudflare.com/). By default, all subdomains will be routed through the tunnel—no need to define them [in your DNS zone](/general/networking/dns).
|
||
|
||
::alert{type="success"}
|
||
✨ __Tip:__ If you want to expose a service without a tunnel, just define an A record [in your DNS zone](/general/networking/dns). If resolution fails, disable the proxy function for that record—e.g., for `sub.mondomaine.fr`.
|
||

|
||
::
|
||
|
||
## 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.
|
||
|
||
In your DNS zone, redirect subdomains to the correct tunnel.
|
||
|
||
Go to _Networks > Tunnels_ in [Cloudflare Zero Trust](https://one.dash.cloudflare.com/).
|
||
|
||
Note the tunnel IDs:
|
||
|
||

|
||
|
||
Then in the [Cloudflare DNS dashboard](https://dash.cloudflare.com/), click your domain name.
|
||
|
||
Click `Add Record` and add these two CNAME records (include `.cfargotunnel.com`):
|
||
|
||
| Type | Name | Target |
|
||
|---------|--------------|----------------------------------------|
|
||
| `CNAME` | `subdomain1` | `yourtunnelid1.cfargotunnel.com` |
|
||
| `CNAME` | `subdomain2` | `yourtunnelid2.cfargotunnel.com` |
|
||
|
||
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:
|
||
|
||
- `sub1` and `sub2` also point to the server behind `subdomain1`:
|
||
|
||
| Type | Name | Target |
|
||
|---------|--------|---------------|
|
||
| `CNAME` | `sub1` | `subdomain1` |
|
||
| `CNAME` | `sub2` | `subdomain1` |
|
||
|
||
- `sub3` and `sub4` point to the server behind `subdomain2`:
|
||
|
||
| Type | Name | Target |
|
||
|---------|--------|---------------|
|
||
| `CNAME` | `sub3` | `subdomain2` |
|
||
| `CNAME` | `sub4` | `subdomain2` | |