diff --git a/projects/caddy/.gitignore b/projects/caddy/.gitignore new file mode 100644 index 0000000..7b8eeeb --- /dev/null +++ b/projects/caddy/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.vscode/ diff --git a/projects/caddy/Caddyfile b/projects/caddy/Caddyfile new file mode 100644 index 0000000..1301d0e --- /dev/null +++ b/projects/caddy/Caddyfile @@ -0,0 +1,488 @@ +# Auth HTTP Snippet +(auth_http) { + @allowed_ips { + remote_ip 192.168.1.100/32 + remote_ip 10.0.0.0/8 + remote_ip 172.16.0.0/16 + } + + handle @allowed_ips { + reverse_proxy {args[0]} { + header_up X-Real-IP {remote_host} + } + } + + @auth { + path /caddy-security/* + } + route @auth { + authenticate with myportal + } + route /* { + authorize with mypolicy + reverse_proxy {args[0]} { + header_up X-Real-IP {remote_host} + } + } +} + +# Auth HTTPS Snippet +(auth_https) { + @allowed_ips { + remote_ip 192.168.1.100/32 + remote_ip 10.0.0.0/8 + remote_ip 172.16.0.0/16 + } + + handle @allowed_ips { + reverse_proxy {args[0]} { + header_up X-Real-IP {remote_host} + transport http { + tls_insecure_skip_verify + } + } + } + + @auth { + path /caddy-security/* + } + route @auth { + authenticate with myportal + } + route /* { + authorize with mypolicy + reverse_proxy {args[0]} { + header_up X-Real-IP {remote_host} + transport http { + tls_insecure_skip_verify + } + } + } +} + +{ + # Cloudflare DDNS Updater + dynamic_dns { + provider cloudflare {env.CLOUDFLARE_API_TOKEN} + domains { + {$DOMAIN} + } + } + # Define Trusted Proxy IPs + servers { + trusted_proxies cloudflare + } + + # Global configuration for Pocket-ID + order authenticate before respond + order authorize before reverse_proxy + + security { + oauth identity provider generic { + delay_start 3 + realm generic + driver generic + client_id {env.POCKET_ID_CLIENT_ID} + client_secret {env.POCKET_ID_CLIENT_SECRET} + scopes openid email profile + base_auth_url https://pocketid.{$DOMAIN} + metadata_url https://pocketid.{$DOMAIN}/.well-known/openid-configuration + } + + authentication portal myportal { + crypto default token lifetime 86400 # Seconds until you have to re-authenticate + enable identity provider generic + cookie domain {$DOMAIN} + cookie insecure off + transform user { + match realm generic + action add role user + } + } + + authorization policy mypolicy { + set auth url /caddy-security/oauth2/generic + allow roles user + inject headers with claims + } + } +} + +# Get Wildcard Certificates to avoid leakage of sub domains. +*.{$DOMAIN}, {$DOMAIN} { + tls { + dns cloudflare {$CLOUDFLARE_API_TOKEN} + } + + # Return 404 for the base domain + @base host {$DOMAIN} + handle @base { + respond "Not Found" 404 + } + + # Portainer - HTTPS + @portainer host portainer.{$DOMAIN} + handle @portainer { + import auth_https https://192.168.1.100:9443 + } + + # Grafana + @grafana host grafana.{$DOMAIN} + handle @grafana { + import auth_http 192.168.1.100:3000 + } + + # NGINX Manager + @nginx host nginx.{$DOMAIN} + handle @nginx { + import auth_http 192.168.1.100:81 + } + + # Homer Dashboard + @homer host homer.{$DOMAIN} + handle @homer { + import auth_http 192.168.1.100:8051 + } + + # Prometheus + @prometheus host prometheus.{$DOMAIN} + handle @prometheus { + import auth_http 192.168.1.100:9090 + } + + # Wazuh - HTTPS + @wazuh host wazuh.{$DOMAIN} + handle @wazuh { + import auth_https https://192.168.1.100:8082 + } + + # OpenSearch Dashboard + @opensearch host opensearch.{$DOMAIN} + handle @opensearch { + import auth_http 192.168.1.100:5601 + } + + # TeslaMate + @teslamate host teslamate.{$DOMAIN} + handle @teslamate { + import auth_http 192.168.1.100:3001 + } + + # Pi-hole + @pihole host pihole.{$DOMAIN} + handle @pihole { + import auth_http 192.168.1.100:8053 + } + + # TeslaMate Service + @teslamateservice host teslamateservice.{$DOMAIN} + handle @teslamateservice { + import auth_http 192.168.1.100:4000 + } + + # WordPress + @wordpress host wordpress.{$DOMAIN} + handle @wordpress { + import auth_http 192.168.1.100:32768 + } + + # Router + @router host router.{$DOMAIN} + handle @router { + import auth_http 192.168.1.254:80 + } + + # Investigate Tool + @investigate host investigate.{$DOMAIN} + handle @investigate { + import auth_http 192.168.1.100:7000 + } + + # Epic Games + @epicgames host epicgames.{$DOMAIN} + handle @epicgames { + import auth_http 192.168.1.100:8052 + } + + # n8n + @n8n host n8n.{$DOMAIN} + handle @n8n { + import auth_http 192.168.1.100:5678 + } + + # MISP - HTTPS + @misp host misp.{$DOMAIN} + handle @misp { + import auth_https https://192.168.1.100:4433 + } + + # MinIO with large file upload support + @minio host minio.{$DOMAIN} + handle @minio { + request_body { + max_size 5GB + } + import auth_http 192.168.1.100:9001 + } + + # MinIO API with large file upload support + @minioapi host minioapi.{$DOMAIN} + handle @minioapi { + request_body { + max_size 5GB + } + import auth_http 192.168.1.100:9000 + } + + # OpenSearch API + @api-opensearch host api-opensearch.{$DOMAIN} + handle @api-opensearch { + import auth_http 192.168.1.100:9210 + } + + # Rancher - HTTPS + @rancher host rancher.{$DOMAIN} + handle @rancher { + import auth_https https://192.168.1.100:4438 + } + + # Actual Budget + @actualbudget host actualbudget.{$DOMAIN} + handle @actualbudget { + import auth_http 192.168.1.100:6000 + } + + # Homepage + @homepage host homepage.{$DOMAIN} + handle @homepage { + import auth_http 192.168.1.100:8054 + } + + # 2FA Auth + @2fauth host 2fauth.{$DOMAIN} + handle @2fauth { + import auth_http 192.168.1.100:8085 + } + + # Parcel tracker + @parcel host parcel.{$DOMAIN} + handle @parcel { + import auth_http 192.168.1.100:8086 + } + + # Home Assistant + @homeassistant host homeassistant.{$DOMAIN} + handle @homeassistant { + import auth_http 192.168.1.100:8123 + } + + # HTTPD + @httpd host httpd.{$DOMAIN} + handle @httpd { + import auth_http 192.168.1.100:8085 + } + + # Grocy + @grocy host grocy.{$DOMAIN} + handle @grocy { + import auth_http 192.168.1.100:9283 + } + + # Kibana + @kibana host kibana.{$DOMAIN} + handle @kibana { + import auth_http 192.168.1.100:5601 + } + + # Authentik + @authentik host authentik.{$DOMAIN} + handle @authentik { + reverse_proxy https://192.168.1.100:9533 { + transport http { + tls_insecure_skip_verify + } + } + } + + # Zipline + @zipline host zipline.{$DOMAIN} + handle @zipline { + import auth_http 192.168.1.100:3003 + } + + # Code Server + @codeserver host codeserver.{$DOMAIN} + handle @codeserver { + import auth_http 192.168.1.100:8443 + } + + # Uptime Kuma + @uptime host uptime.{$DOMAIN} + handle @uptime { + import auth_http 192.168.1.100:3002 + } + + # CyberChef + @cyberchef host cyberchef.{$DOMAIN} + handle @cyberchef { + import auth_http 192.168.1.100:8001 + } + + # CV + @cv host cv.{$DOMAIN} + handle @cv { + import auth_http 192.168.1.100:8002 + } + + # PDF Tools + @pdf host pdf.{$DOMAIN} + handle @pdf { + import auth_http 192.168.1.100:8055 + } + + # IT Tools + @it-tools host it-tools.{$DOMAIN} + handle @it-tools { + import auth_http 192.168.1.100:8056 + } + + # VW + @vw host vw.{$DOMAIN} + handle @vw { + reverse_proxy 192.168.1.100:3004 + } + + # ConvertX + @convertx host convertx.{$DOMAIN} + handle @convertx { + import auth_http 192.168.1.100:3005 + } + + # Neko + @neko host neko.{$DOMAIN} + handle @neko { + import auth_http 192.168.1.100:3006 + } + + # Resume + @resume host resume.{$DOMAIN} + handle @resume { + import auth_http 192.168.1.100:3007 + } + + # Change Detection + @changedetection host changedetection.{$DOMAIN} + handle @changedetection { + import auth_http 192.168.1.100:3008 + } + + # ReadMe + @readme host readme.{$DOMAIN} + handle @readme { + import auth_http 192.168.1.100:8057 + } + + # Karakeep + @karakeep host karakeep.{$DOMAIN} + handle @karakeep { + reverse_proxy 192.168.1.100:8058 + } + + # Duplicati + @duplicati host duplicati.{$DOMAIN} + handle @duplicati { + import auth_http 192.168.1.100:8200 + } + + # Glance + @glance host glance.{$DOMAIN} + handle @glance { + import auth_http 192.168.1.100:8059 + } + + # Calibre Web Automated + @cwa host cwa.{$DOMAIN} + handle @cwa { + # Allow /kobo/* path without authentication + @kobo_path { + path /kobo/* + } + handle @kobo_path { + reverse_proxy 192.168.1.100:8060 { + # This header is needed to download kepubs from Kobo eReader + header_up X-Scheme {http.request.scheme} + } + } + + # Apply authentication to all other paths + handle { + import auth_http 192.168.1.100:8060 + } + } + + # Cup + @cup host cup.{$DOMAIN} + handle @cup { + import auth_http 192.168.1.100:8061 + } + + # qBitTorrent + @qbittorrent host qbittorrent.{$DOMAIN} + handle @qbittorrent { + import auth_http 192.168.1.100:8063 + } + + # Sonarr + @sonarr host sonarr.{$DOMAIN} + handle @sonarr { + import auth_http 192.168.1.100:8989 + } + + # Radarr + @radarr host radarr.{$DOMAIN} + handle @radarr { + import auth_http 192.168.1.100:7878 + } + + # Prowlarr + @prowlarr host prowlarr.{$DOMAIN} + handle @prowlarr { + import auth_http 192.168.1.100:9696 + } + + # Jellyfin + @jellyfin host jellyfin.{$DOMAIN} + handle @jellyfin { + reverse_proxy 192.168.1.100:8096 + } + + # Jellyseerr + @jellyseer host jellyseer.{$DOMAIN} + handle @jellyseer { + reverse_proxy 192.168.1.100:5056 + } + + # SABnzbd + @sabnzbd host sabnzbd.{$DOMAIN} + handle @sabnzbd { + import auth_http 192.168.1.100:8064 + } + + # Pocket-ID + @pocketid host pocketid.{$DOMAIN} + handle @pocketid { + reverse_proxy 192.168.1.100:8062 + } + + # Komodo + @komodo host komodo.{$DOMAIN} + handle @komodo { + import auth_http 192.168.1.100:9120 + } + + # Gitea + @gitea host gitea.{$DOMAIN} + handle @gitea { + import auth_http 192.168.1.100:8065 + } +} diff --git a/projects/caddy/README.md b/projects/caddy/README.md new file mode 100644 index 0000000..2e39fe9 --- /dev/null +++ b/projects/caddy/README.md @@ -0,0 +1,136 @@ +# Setting up Caddy with Cloudflared Tunnel + +This guide explains how to set up a Caddy server with Cloudflared for secure access to your applications through Cloudflare Tunnels. + +## Prerequisites + +- A Cloudflare account with a domain +- Docker and Docker Compose installed +- Cloudflare API token with the right permissions + +## Setup Instructions + +### 1. Create Directory Structure and Fix Permissions + +```bash +# Create directories +mkdir -p cloudflared +touch cloudflared/config.yml + +# Fix permissions - CRITICAL STEP! +sudo chown -R 65532:65532 cloudflared +``` + +### 2. Authenticate with Cloudflare + +Run the following command to authenticate with Cloudflare: + +```bash +docker run --rm -it -v $(pwd)/cloudflared:/home/nonroot/.cloudflared cloudflare/cloudflared:latest login +``` + +This will open a URL in your browser. Login with your Cloudflare account and authorize the certificate. The certificate will be saved to `cloudflared/cert.pem`. + +### 3. Create a Cloudflare Tunnel + +After authentication, create a new tunnel: + +```bash +docker run --rm -it -v $(pwd)/cloudflared:/home/nonroot/.cloudflared cloudflare/cloudflared:latest tunnel create caddy-tunnel +``` + +This will create a tunnel named "caddy-tunnel" and generate a credentials file in the `cloudflared` directory. + +### 4. Configure Cloudflared + +Now list your tunnels to get the Tunnel ID: + +```bash +docker run --rm -v $(pwd)/cloudflared:/home/nonroot/.cloudflared cloudflare/cloudflared:latest tunnel list +``` + +You'll see output like this: +``` +ID NAME CREATED DELETED CONNECTION +xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx caddy-tunnel 2025-04-09 10:00:00 false 1 +``` + +Edit the `cloudflared/config.yml` file with the tunnel ID and your domain: + +```yaml +tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +credentials-file: /home/nonroot/.cloudflared/credentials/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json + +# Set your tunnel name (for reference only) +tunnel-name: caddy-tunnel + +ingress: + # Route traffic to your Caddy server + - hostname: '*.YOUR_DOMAIN.com' + service: http://caddy:80 + - hostname: YOUR_DOMAIN.com + service: http://caddy:80 + # Default catch-all rule + - service: http_status:404 +``` + +Remember to replace: +- `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with your actual tunnel ID +- `YOUR_DOMAIN.com` with your actual domain name + +### 5. Route Your Domain to the Tunnel + +Run the following commands to route your domain to the tunnel: + +```bash +# Route your main domain to the tunnel +docker run --rm -v $(pwd)/cloudflared:/home/nonroot/.cloudflared cloudflare/cloudflared:latest tunnel route dns caddy-tunnel YOUR_DOMAIN.com + +# Route wildcard subdomains to the tunnel +docker run --rm -v $(pwd)/cloudflared:/home/nonroot/.cloudflared cloudflare/cloudflared:latest tunnel route dns caddy-tunnel "*.YOUR_DOMAIN.com" +``` + +Replace `YOUR_DOMAIN.com` with your actual domain name. + +### 6. Create Environment Variables File + +Create a `.env` file with the necessary environment variables: + +``` +CLOUDFLARE_API_TOKEN=your-cloudflare-api-token +DOMAIN=YOUR_DOMAIN.com +``` + +### 7. Start the Stack + +```bash +docker-compose up -d +``` + +You can check the logs to make sure everything is working: + +```bash +docker-compose logs -f cloudflared +``` + +## Validation + +After completing the setup, your applications should be accessible through your domain using the Cloudflare Tunnel. This provides secure, encrypted access to your services without exposing any ports to the internet. + +## Troubleshooting + +### Check Cloudflared Logs + +```bash +docker logs cloudflared +``` + +### Check Caddy Logs + +```bash +docker logs caddy +``` + +### Verify Tunnel Status + +Check the status of your tunnel in the Cloudflare Zero Trust dashboard under Access > Tunnels. diff --git a/projects/caddy/compose.yaml b/projects/caddy/compose.yaml new file mode 100644 index 0000000..a5e579d --- /dev/null +++ b/projects/caddy/compose.yaml @@ -0,0 +1,28 @@ +services: + caddy: + image: serfriz/caddy-cloudflare-ddns-security:latest + container_name: caddy + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "443:443/udp" # For HTTP/3 + environment: + CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} + DOMAIN: ${DOMAIN} + POCKET_ID_CLIENT_ID: ${POCKET_ID_CLIENT_ID} + POCKET_ID_CLIENT_SECRET: ${POCKET_ID_CLIENT_SECRET} + volumes: + - ./caddy:/etc/caddy + - ./data:/data + - ./config:/config + + cloudflared: + image: cloudflare/cloudflared:latest + container_name: cloudflared-caddy + restart: unless-stopped + command: tunnel --config /etc/cloudflared/config.yml run + volumes: + - ./cloudflared:/etc/cloudflared + depends_on: + - caddy