cloudflare, caddy, docker, pihole, dns, lets encrypt

As of 11/7/2024 — This is my home network software development setup. I have much more running than just Ollama, ChromaDb, etc. I also have several Postgres, Mongo, and other databases running in this setup.

Assumptions: You have a machine running Docker and have a local static IP set on that machine.

In this post, I’ll walk you through a setup for serving local network services securely over HTTPS, using Caddy, Cloudflare DNS, and Let’s Encrypt. This configuration allows you to have SSL certificates from Let’s Encrypt for your internal services, even though they’re only accessible within your local network. This setup is ideal if you want to avoid setting up a local certificate authority while still using HTTPS.

Overview of the Infrastructure

The core components of this setup include:

  • Caddy: A powerful web server with automatic HTTPS capabilities, configured with Cloudflare for DNS-01 challenge-based SSL certificates.
  • Cloudflare DNS: Handles DNS for our domain and provides an API that Caddy uses to validate certificates without exposing services publicly.
  • Let’s Encrypt: Provides SSL certificates via DNS-01 challenge, verified through Cloudflare.

The setup uses Docker to containerize Caddy and other services (e.g., Ollama, ChromaDb, TTS, Pihole, and anything else you desire).

How It Works

1. User Device Requests a Local Service

When a local device, like a laptop or phone, tries to access a service (e.g., https://ollama.webenclave.com), the request goes to Cloudflare DNS. Cloudflare manages the DNS for webenclave.com, pointing the subdomains (e.g., ollama.webenclave.com) to an internal IP address (e.g., 192.168.x.x). Cloudflare is set to “DNS only” mode to allow direct access.

2. Caddy as the Reverse Proxy

On the Docker host machine, Caddy listens for HTTPS requests. Caddy is configured with a Cloudflare DNS plugin that enables it to automatically obtain SSL certificates using Let’s Encrypt, without requiring external exposure. Caddy does the following:

  • Reverse Proxy: Caddy forwards requests to the appropriate Docker container based on the subdomain.
  • TLS with DNS-01 Challenge: Caddy handles HTTPS certificates for each service using a DNS-01 challenge. When a certificate is needed (for example, for ollama.webenclave.com), Caddy triggers the DNS-01 challenge with Cloudflare.

3. Let’s Encrypt and Cloudflare API for Certificate Management

Caddy communicates with Cloudflare’s DNS API to complete the DNS-01 challenge for Let’s Encrypt. Here’s how it works:

  • DNS-01 Challenge: Caddy requests a certificate from Let’s Encrypt for ollama.webenclave.com. Let’s Encrypt requires proof that we control this domain, so Caddy uses the Cloudflare API to create a temporary DNS TXT record for validation.
  • Certificate Issuance: Let’s Encrypt validates this TXT record, verifying our control over the domain, and issues a certificate. Caddy then stores and manages this certificate, automatically renewing it before it expires.

With this setup, Caddy provides valid HTTPS certificates from Let’s Encrypt for each local service, so connections are secure and trusted by default.

4. Reverse Proxy to Local Services

Once the certificate is obtained, Caddy acts as a reverse proxy to route requests to the appropriate Docker container for each service:

  • Ollama (ollama.webenclave.com)
  • ChromaDb (chroma.webenclave.com)
  • TTS (tts.webenclave.com)
  • Pihole (pihole.webenclave.com)

Each service is accessible over HTTPS, with traffic staying entirely within the local network.

Key Benefits of This Setup

  • Secure HTTPS for Local Services: Using Let’s Encrypt with Cloudflare’s DNS-01 challenge, you get trusted SSL certificates for local services without exposing them to the internet.
  • Automatic Certificate Management: Caddy handles obtaining and renewing certificates automatically, so you don’t need to manually manage SSL certificates.
  • Simple and Scalable: With Docker, it’s easy to add more services. You can configure additional subdomains and services in the Caddy configuration as needed.

Setting Up Your Own Environment

To set up this infrastructure on your own network, follow these steps:

  1. Register a Domain on Cloudflare (if you don’t already have one).
  2. Create DNS Records: In Cloudflare, create subdomain DNS records for each service, marked as “DNS Only” (gray). Set each subdomain to your local network IP (e.g., 192.168.x.x).
  3. Generate a Cloudflare API Token: This token needs DNS editing permissions to allow Caddy to use the DNS-01 challenge.
  4. Set Up Caddy with the Cloudflare Plugin: Use a Docker image for Caddy that includes the Cloudflare DNS plugin. Configure Caddy to use the Cloudflare API token for automatic HTTPS.
  5. Deploy Services: Deploy your services in Docker, configure them with Caddy as the reverse proxy, and access them securely over HTTPS within your network.

Code

Here’s an example Caddyfile that shows how Caddy is configured for DNS-01 challenges with Cloudflare:

{
    acme_dns cloudflare <YOUR_CLOUDFLARE_API_TOKEN>
}

ollama.webenclave.com {
    reverse_proxy http://x.x.x.x:11434
    tls {
        dns cloudflare <YOUR_CLOUDFLARE_API_TOKEN>
    }
}

chroma.webenclave.com {
    reverse_proxy http://x.x.x.x:8000
    tls {
        dns cloudflare <YOUR_CLOUDFLARE_API_TOKEN>
    }
}

tts.webenclave.com {
    reverse_proxy http://x.x.x.x:8020
    tls {
        dns cloudflare <YOUR_CLOUDFLARE_API_TOKEN>
    }
}

pihole.webenclave.com {
    reverse_proxy http://x.x.x.x
    tls {
        dns cloudflare <YOUR_CLOUDFLARE_API_TOKEN>
    }
}

Dockerfile (create docker image with Caddy + Cloudflare Plugin)

# syntax=docker/dockerfile:1
FROM caddy:2.8.4-builder AS builder
RUN xcaddy build \
  --with github.com/caddy-dns/cloudflare

FROM caddy:2.8.4 AS caddy
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

docker-compose.yml

version: "3.8"
services:
  caddy:
    build:
      context: .  # Build context where the Dockerfile is located
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    ports:
      - "443:443"
    networks:
      - mynetwork

networks:
  mynetwork:
    driver: bridge