89 lines
3.5 KiB
Nix
89 lines
3.5 KiB
Nix
{ config, lib, pkgs, homeyConfig, ... }:
|
|
|
|
# Cloudflare Tunnel (cloudflared) — remote access without open inbound ports.
|
|
#
|
|
# Architecture:
|
|
# Internet → Cloudflare edge → cloudflared tunnel (outbound from Pi)
|
|
# → Caddy on localhost → service containers
|
|
#
|
|
# The tunnel is configured to route each hostname to Caddy's HTTPS listener.
|
|
# Caddy handles TLS and forward_auth; cloudflared just carries the traffic.
|
|
#
|
|
# Setup steps (one-time, done from the Cloudflare dashboard):
|
|
# 1. Go to Zero Trust → Networks → Tunnels → Create a tunnel
|
|
# 2. Name it (e.g. "pi-main")
|
|
# 3. Copy the tunnel token — add it to secrets.yaml as cloudflare/tunnel_token
|
|
# 4. In the tunnel's "Public Hostnames" config, add routes:
|
|
# auth.home.zakobar.com → http://localhost:80 (or https://localhost:443)
|
|
# git.home.zakobar.com → https://localhost:443
|
|
# nextcloud.home.zakobar.com → https://localhost:443
|
|
# ldapadmin.home.zakobar.com → https://localhost:443
|
|
# jellyfin.home.zakobar.com → https://localhost:443
|
|
# torrent.home.zakobar.com → https://localhost:443
|
|
# Set "No TLS Verify" = true (Caddy's cert is from Let's Encrypt but
|
|
# the hostname seen by cloudflared is localhost, so hostname verification
|
|
# would fail without this flag).
|
|
#
|
|
# The tunnel_token approach (--token) is the simplest: one secret, no config
|
|
# file needed on the Pi.
|
|
#
|
|
# Secrets consumed from sops:
|
|
# cloudflare/tunnel_token
|
|
|
|
let
|
|
cfg = config.homey.cloudflared;
|
|
in
|
|
{
|
|
options.homey.cloudflared = {
|
|
enable = lib.mkEnableOption "Cloudflare Tunnel for remote access";
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# -----------------------------------------------------------------------
|
|
# Secrets
|
|
# -----------------------------------------------------------------------
|
|
sops.secrets."cloudflare/tunnel_token" = { owner = "cloudflared"; };
|
|
|
|
# -----------------------------------------------------------------------
|
|
# cloudflared service
|
|
#
|
|
# We use the token-based tunnel approach (cloudflared tunnel run --token).
|
|
# This needs no credentials file and no local tunnel config — just the
|
|
# token from the Cloudflare dashboard.
|
|
#
|
|
# Rather than using services.cloudflared.tunnels (which requires a
|
|
# credentialsFile), we create a plain systemd service that runs cloudflared
|
|
# directly with the token read from the sops secret.
|
|
# -----------------------------------------------------------------------
|
|
users.users.cloudflared = {
|
|
isSystemUser = true;
|
|
group = "cloudflared";
|
|
description = "cloudflared tunnel daemon";
|
|
};
|
|
users.groups.cloudflared = {};
|
|
|
|
systemd.services."cloudflared-tunnel" = {
|
|
description = "Cloudflare Tunnel (token-based)";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-online.target" "caddy.service" ];
|
|
wants = [ "network-online.target" "caddy.service" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
User = "cloudflared";
|
|
Group = "cloudflared";
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
ExecStart = pkgs.writeShellScript "cloudflared-start" ''
|
|
exec ${pkgs.cloudflared}/bin/cloudflared tunnel \
|
|
--no-autoupdate \
|
|
run \
|
|
--token "$(cat ${config.sops.secrets."cloudflare/tunnel_token".path})"
|
|
'';
|
|
# Hardening
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
};
|
|
};
|
|
};
|
|
}
|