Skip to content

Commit 7fa2a1b

Browse files
davidamaceyclaude
andcommitted
feat(network): add Tailscale and Cloudflare Zero Trust WARP Connector
Two complementary remote-access options alongside the existing wg-easy VPN and cloudflared tunnel. Tailscale (services/network/tailscale/): - Runs as a subnet router using the official tailscale/tailscale image - Advertises your LAN subnet (TS_ROUTES) to all your Tailscale devices - Uses kernel-mode WireGuard (NET_ADMIN + /dev/net/tun) for best perf - host networking so it can route packets across the full LAN - MagicDNS enabled — devices reach the server by hostname automatically - Auth via reusable pre-authorized key (TS_AUTHKEY) — survives restarts - No port forwarding or firewall changes required Cloudflare Zero Trust WARP Connector (services/network/cloudflare-zero-trust/): - Distinct from infra/cloudflared (which exposes public HTTP hostnames) - WARP Connector gives devices running the Cloudflare WARP client access to private LAN IPs on any port, not just HTTP - Same cloudflared image but configured as a private network connector - ip_forward + NET_ADMIN enables subnet routing for WARP clients - Comprehensive compose header explains Zero Trust setup steps, Split Tunneling config, and comparison with Tailscale/wg-easy Also: - Add missing services/infra/cloudflared/.env.example with clear instructions distinguishing the tunnel token from the WARP token - Update README network table and directory tree Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f6e6eb5 commit 7fa2a1b

File tree

6 files changed

+221
-4
lines changed

6 files changed

+221
-4
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ These are custom-built applications with published Docker images. They require b
140140
|-------------------|---------------|-----|--------------------------------------------------|
141141
| AdGuard Home | 3080/5353 | - | Network-wide DNS ad and tracker blocking |
142142
| wg-easy | 51821/51820 | - | WireGuard VPN with a simple web UI |
143+
| Tailscale | - | - | Zero-config mesh VPN and subnet router (no port forwarding needed) |
144+
| Cloudflare Zero Trust | - | - | WARP Connector — private LAN access for WARP clients via Cloudflare |
143145
| Speedtest Tracker | 7900 | - | Scheduled internet speed monitoring with history |
144146

145147
### Dev Tools (`services/dev/`)
@@ -250,6 +252,8 @@ OpenHomeLab/
250252
│ ├── network/
251253
│ │ ├── adguard-home/ # DNS ad and tracker blocking
252254
│ │ ├── wg-easy/ # WireGuard VPN with web UI
255+
│ │ ├── tailscale/ # Mesh VPN subnet router (no port forwarding)
256+
│ │ ├── cloudflare-zero-trust/ # WARP connector for private LAN access
253257
│ │ └── speedtest-tracker/ # Scheduled internet speed monitoring
254258
│ ├── dev/
255259
│ │ ├── gitea/ # Self-hosted Git service
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
# ===========================================================================
2-
# Cloudflare Tunnel — Environment Variables
2+
# Cloudflare Tunnel (cloudflared) — Environment Variables
33
# Copy to .env and fill in real values. .env is gitignored.
4+
#
5+
# This tunnel exposes specific homelab services to the public internet
6+
# via public hostnames (e.g. immich.yourdomain.com). It is the HTTP-
7+
# focused public access layer.
8+
#
9+
# For private network access (reach LAN IPs from your devices), see:
10+
# services/network/cloudflare-zero-trust/ — WARP Connector
11+
# services/network/tailscale/ — Tailscale subnet router
12+
#
13+
# TOKEN: Zero Trust Dashboard → Networks → Tunnels → your tunnel
14+
# → Install and run a connector → copy the token
15+
# Docs: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
416
# ===========================================================================
517

6-
# Tunnel token from Cloudflare Zero Trust dashboard
7-
# Get one at: https://one.dash.cloudflare.com/ → Networks → Tunnels → Create
8-
CLOUDFLARE_TUNNEL_TOKEN=YOUR_TUNNEL_TOKEN_HERE
18+
# Cloudflare tunnel token — get this from the Zero Trust dashboard
19+
CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiREFDS0VSVE9LRU4iLCJ0IjoiUkVQTEFDRV9NRSJ9
920

1021
TZ=America/New_York
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ===========================================================================
2+
# Cloudflare Zero Trust WARP Connector — Environment Variables
3+
# Copy to .env and fill in real values. .env is gitignored.
4+
#
5+
# This is the WARP Connector tunnel (private network access for WARP clients).
6+
# It is separate from the public-facing tunnel in infra/cloudflared/.
7+
# You need a SEPARATE tunnel token created specifically for the WARP connector.
8+
#
9+
# See the docker-compose.yml header for the full setup walkthrough.
10+
#
11+
# IMPORTANT: Do NOT reuse the token from infra/cloudflared — each tunnel
12+
# must have its own unique token. Create a new tunnel in the Zero Trust
13+
# dashboard specifically for this WARP connector.
14+
#
15+
# TOKEN LOCATION: Zero Trust Dashboard → Networks → Tunnels
16+
# → Your connector tunnel → Click the tunnel → Overview tab
17+
# → Install and run a connector → Copy the token value
18+
# ===========================================================================
19+
20+
# Tunnel token for the WARP connector tunnel (NOT the same as infra/cloudflared)
21+
CF_TUNNEL_TOKEN=eyJhIjoiREFDS0VSVE9LRU4iLCJ0IjoiUkVQTEFDRV9NRSJ9
22+
23+
TZ=America/New_York
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# ===========================================================================
2+
# Cloudflare Zero Trust — WARP Connector (private network access)
3+
# Port: none (outbound-only to Cloudflare's network)
4+
# Docs: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/warp-connector/
5+
#
6+
# WHAT THIS IS (and how it differs from infra/cloudflared):
7+
#
8+
# infra/cloudflared = Cloudflare Tunnel: exposes specific HTTP services
9+
# to the public internet via public hostnames.
10+
# Anyone on the internet can reach immich.yourdomain.com.
11+
#
12+
# THIS SERVICE = WARP Connector: exposes your entire private network
13+
# to devices running the Cloudflare WARP client.
14+
# Only YOUR devices (enrolled in your Zero Trust org)
15+
# can reach 192.168.x.x addresses through it.
16+
# Works like Tailscale but via Cloudflare's network.
17+
#
18+
# USE CASE:
19+
# Install Cloudflare WARP on your phone/laptop. Once connected to your
20+
# Zero Trust org, you can SSH to your server by private IP, reach any
21+
# homelab service on any port (not just HTTP), and use Split Tunneling
22+
# to only route homelab traffic through Cloudflare.
23+
#
24+
# HOW IT COMPARES:
25+
# CF Zero Trust WARP — Cloudflare-managed mesh, Zero Trust policies,
26+
# device posture checks, identity-aware access
27+
# Tailscale — Self-managed mesh (via Tailscale infra), simpler,
28+
# faster setup, no Cloudflare dependency
29+
# wg-easy — Fully self-hosted WireGuard, needs open UDP port
30+
#
31+
# SETUP (one-time — do this before starting the container):
32+
# 1. Create a Zero Trust account at https://one.dash.cloudflare.com
33+
# (free for up to 50 users)
34+
# 2. Go to Settings → WARP Client → Device enrollment policies
35+
# Add a policy allowing your email address
36+
# 3. Go to Networks → Tunnels → Create a tunnel → Cloudflared
37+
# Name it e.g. "homelab-warp-connector"
38+
# 4. In the tunnel, go to Private Network tab
39+
# Add your LAN subnet: e.g. 192.168.1.0/24
40+
# 5. Enable WARP routing: Settings → WARP Client → Enable WARP to WARP
41+
# 6. Copy the tunnel token and add it to your .env as CF_TUNNEL_TOKEN
42+
# 7. make up SERVICE=network/cloudflare-zero-trust
43+
# 8. Install Cloudflare WARP on your devices:
44+
# https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/download-warp/
45+
# 9. Log in to your Zero Trust org in the WARP client
46+
# 10. Done — you can now reach 192.168.x.x from your phone
47+
#
48+
# SPLIT TUNNELING (recommended):
49+
# In Zero Trust dashboard → Settings → WARP Client → Profile settings
50+
# → Split Tunnels → Exclude IPs: add your LAN subnet
51+
# This routes only homelab traffic through WARP; all other internet
52+
# traffic goes directly (faster, privacy-preserving).
53+
# ===========================================================================
54+
55+
services:
56+
cloudflare-zero-trust:
57+
image: cloudflare/cloudflared:latest
58+
container_name: cloudflare-zero-trust
59+
restart: unless-stopped
60+
pull_policy: always
61+
# host networking lets the connector act as a subnet router and
62+
# forward packets to all LAN hosts on behalf of WARP clients.
63+
network_mode: host
64+
cap_add:
65+
- NET_ADMIN
66+
- SYS_MODULE
67+
sysctls:
68+
- net.ipv4.ip_forward=1 # required for subnet routing
69+
- net.ipv4.conf.all.src_valid_mark=1
70+
command: tunnel --no-autoupdate run --token ${CF_TUNNEL_TOKEN}
71+
environment:
72+
TZ: ${TZ:-America/New_York}
73+
healthcheck:
74+
test: ["CMD", "cloudflared", "tunnel", "info"]
75+
interval: 60s
76+
timeout: 10s
77+
retries: 3
78+
start_period: 30s
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# ===========================================================================
2+
# Tailscale — Environment Variables
3+
# Copy to .env and fill in real values. .env is gitignored.
4+
#
5+
# SETUP STEPS:
6+
# 1. Sign up at https://login.tailscale.com (free for personal use)
7+
# 2. Go to Admin Console → Settings → Keys → Generate auth key
8+
# - Check "Reusable" (so it survives container restarts)
9+
# - Check "Pre-authorized" (so the machine auto-approves)
10+
# - Expiry: 90 days is a good balance (rotate it occasionally)
11+
# 3. Paste the key below as TS_AUTHKEY
12+
# 4. Set TS_ROUTES to your LAN subnet (check your router: typically
13+
# 192.168.0.0/24, 192.168.1.0/24, or 10.0.0.0/24)
14+
# 5. After starting, go to Admin Console → Machines → approve subnet route
15+
# ===========================================================================
16+
17+
# Reusable auth key from https://login.tailscale.com/admin/settings/keys
18+
# Format: tskey-auth-kXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
19+
TS_AUTHKEY=tskey-auth-REPLACE_ME
20+
21+
# Hostname shown in your Tailscale admin console and used for MagicDNS
22+
TS_HOSTNAME=homelab
23+
24+
# Your homelab LAN subnet — adjust to match your router's network
25+
# Check with: ip route | grep -v tailscale | head -5
26+
TS_ROUTES=192.168.1.0/24
27+
28+
TZ=America/New_York
29+
DATA_PATH=/mnt/nas/appdata
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# ===========================================================================
2+
# Tailscale — Zero-config mesh VPN for secure remote access
3+
# Port: none (outbound WireGuard connections only)
4+
# Docs: https://tailscale.com/kb/1282/docker
5+
#
6+
# Tailscale creates a private WireGuard mesh between all your devices
7+
# (phone, laptop, work PC) and your homelab server. No port forwarding,
8+
# no static IPs, no certificate management. Every device gets a stable
9+
# 100.x.x.x address and a MagicDNS hostname.
10+
#
11+
# This container runs as a SUBNET ROUTER, which means it exposes your
12+
# entire homelab LAN (e.g. 192.168.1.0/24) to all your Tailscale devices.
13+
# You can reach any service by LAN IP or by the server's Tailscale hostname.
14+
#
15+
# How it compares to alternatives:
16+
# Tailscale — mesh VPN, no firewall changes, best for personal devices
17+
# wg-easy — self-hosted WireGuard, more control, needs port forwarding
18+
# Cloudflare Tunnel — HTTP only (no raw TCP/UDP), browser-focused access
19+
# CF Zero Trust — WARP connector, same mesh concept but via Cloudflare
20+
#
21+
# Quick start:
22+
# 1. Create a free Tailscale account at https://login.tailscale.com
23+
# 2. Generate an auth key: Admin Console → Settings → Keys → Auth keys
24+
# Check "Reusable" so the container can re-authenticate after restart.
25+
# 3. cp .env.example .env and fill in TS_AUTHKEY and TS_ROUTES
26+
# 4. make up SERVICE=network/tailscale
27+
# 5. In Tailscale Admin Console → Machines, approve the new "homelab" device
28+
# 6. Enable subnet routes: click the machine → "..." → Edit route settings
29+
# Toggle on the advertised subnet route
30+
# 7. Optionally enable as exit node for internet routing through the server
31+
#
32+
# After setup, from any device in your Tailnet:
33+
# ssh user@homelab # SSH via MagicDNS hostname
34+
# http://100.x.x.x:8096 # Jellyfin via Tailscale IP
35+
# http://homelab.tail1234.ts.net:8096 # Jellyfin via MagicDNS FQDN
36+
# ===========================================================================
37+
38+
services:
39+
tailscale:
40+
image: tailscale/tailscale:latest
41+
container_name: tailscale
42+
hostname: ${TS_HOSTNAME:-homelab}
43+
restart: unless-stopped
44+
pull_policy: always
45+
cap_add:
46+
- NET_ADMIN
47+
- NET_RAW
48+
# host networking gives the subnet router access to all LAN interfaces
49+
# and lets it advertise routes on behalf of the entire server.
50+
network_mode: host
51+
environment:
52+
TZ: ${TZ:-America/New_York}
53+
TS_AUTHKEY: ${TS_AUTHKEY} # REQUIRED — from Tailscale admin console
54+
TS_HOSTNAME: ${TS_HOSTNAME:-homelab}
55+
TS_STATE_DIR: /var/lib/tailscale
56+
TS_USERSPACE: "false" # kernel-mode WireGuard (faster, requires NET_ADMIN)
57+
TS_ACCEPT_DNS: "true" # use Tailscale MagicDNS
58+
# Advertise your homelab LAN as a subnet route (adjust to your subnet).
59+
# After startup, approve the route in the Tailscale Admin Console.
60+
TS_ROUTES: ${TS_ROUTES:-192.168.1.0/24}
61+
# Uncomment to also advertise this server as a Tailscale exit node
62+
# (routes all internet traffic from other devices through this server):
63+
# TS_EXTRA_ARGS: --advertise-exit-node
64+
volumes:
65+
- ${DATA_PATH:-/mnt/nas/appdata}/tailscale/state:/var/lib/tailscale
66+
- /dev/net/tun:/dev/net/tun # kernel TUN device for WireGuard
67+
healthcheck:
68+
test: ["CMD", "tailscale", "status"]
69+
interval: 60s
70+
timeout: 10s
71+
retries: 3
72+
start_period: 30s

0 commit comments

Comments
 (0)