From 64c194a5d85e1e9a2ebe0487b7d3d996b0dff621 Mon Sep 17 00:00:00 2001 From: kongkx Date: Tue, 15 Sep 2026 06:59:44 +0800 Subject: [PATCH] init --- .env.example | 7 ++++ .gitignore | 6 +++ Caddyfile | 93 ++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 8 ++++ README.md | 49 ++++++++++++++++++++++++ docker-compose.yml | 33 ++++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Caddyfile create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c419d40 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +# Cloudflare API Token (Requires Zone:DNS:Edit permission) +CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here +ACME_EMAIL=admin@example.com + +# Cloudflare Tunnel Token (From Cloudflare Zero Trust Dashboard -> Networks -> Tunnels) +CLOUDFLARE_TUNNEL_TOKEN=your_cloudflare_tunnel_token_here + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723acdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Local environment variables +.env + +# Caddy persistent storage and configurations +data/ +config/ diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..832387c --- /dev/null +++ b/Caddyfile @@ -0,0 +1,93 @@ +{ + email {$ACME_EMAIL:admin@kucial.com} +} + +# Reusable TLS block using Cloudflare DNS-01 ACME challenge (Let's Encrypt / ZeroSSL) +(cloudflare_tls) { + tls { + dns cloudflare {env.CLOUDFLARE_API_TOKEN} + } +} + +# Synotrip (PHP 8.3 / MariaDB / Redis) +synotrip.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:3200 +} + +npm.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:4873 +} + +# Blog (PHP 8.3 / MariaDB) +blog.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:3201 +} + +gig-api.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:8088 +} + +gig-static.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:8088 +} + +gig-db.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:3100 +} + +gig-web.kucial.com { + import cloudflare_tls + reverse_proxy host.docker.internal:3101 +} + + +# Gitea on Z2Pro +gitea.kucial.com { + import cloudflare_tls + reverse_proxy 192.168.50.228:8081 +} + +*.ddev.kucial.com { + tls { + dns cloudflare {env.CLOUDFLARE_API_TOKEN} + } + + @api host gig-api.ddev.kucial.com + handle @api { + reverse_proxy https://192.168.50.160:443 { + header_up Host gig-platform.ddev.site + transport http { + tls_server_name gig-platform.ddev.site + # The public device-facing certificate is still fully trusted. + # This applies only to the fixed, private Caddy-to-DDEV hop. + tls_insecure_skip_verify + } + } + } + + @socket host gig-socket.ddev.kucial.com + handle @socket { + reverse_proxy http://192.168.50.160:6001 { + header_up Host gig-platform.ddev.site + } + } + + @static host gig-static.ddev.kucial.com + handle @static { + reverse_proxy https://192.168.50.160:443 { + header_up Host gig-platform.ddev.site + transport http { + tls_server_name gig-platform.ddev.site + tls_insecure_skip_verify + } + } + } + + respond 404 +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1bd61a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM caddy:builder AS builder + +RUN xcaddy build \ + --with github.com/caddy-dns/cloudflare + +FROM caddy:latest + +COPY --from=builder /usr/bin/caddy /usr/bin/caddy diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ef7da1 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Caddy Dual TLS + Cloudflare Tunnel Setup + +This architecture provides **End-to-End Dual TLS Encryption**: +1. **Edge TLS (Cloudflare Edge Certificate):** Cloudflare terminates HTTPS for public visitors on the internet. +2. **Origin TLS (Let's Encrypt / ACME Certificate):** Caddy automatically requests and renews valid SSL/TLS certificates using the Cloudflare DNS-01 ACME challenge plugin. +3. **Tunnel Transport:** Cloudflare Tunnel (`cloudflared`) connects to Caddy over **`HTTPS://caddy:443`** with strict origin TLS verification. + +--- + +## 📁 File Structure + +- [`Dockerfile`](file:///Users/kongkx/services/caddy/Dockerfile): Builds Caddy with `github.com/caddy-dns/cloudflare` using `xcaddy`. +- [`docker-compose.yml`](file:///Users/kongkx/services/caddy/docker-compose.yml): Runs `caddy` and `cloudflared` on the shared Docker bridge network. +- [`Caddyfile`](file:///Users/kongkx/services/caddy/Caddyfile): Configures `synotrip.kucial.com` & `blog.kucial.com` with DNS-01 TLS challenge. +- [`.env`](file:///Users/kongkx/services/caddy/.env): Stores `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_TUNNEL_TOKEN`. +- [`data/`](file:///Users/kongkx/services/caddy/data): Persistent volume storing Caddy's Let's Encrypt certificates. + +--- + +## 🚀 Setup & Cloudflare Dashboard Configuration + +### 1. In [`.env`](file:///Users/kongkx/services/caddy/.env) +- `CLOUDFLARE_API_TOKEN`: Token with `Zone:DNS:Edit` permission (allows Caddy to complete the DNS-01 challenge). +- `CLOUDFLARE_TUNNEL_TOKEN`: Token from Cloudflare Zero Trust Tunnels. + +### 2. In Cloudflare Zero Trust Dashboard (Public Hostnames) +Add your subdomains under your Tunnel's **Public Hostnames**: + +| Public Hostname | Service Type | URL | Origin Server Name (under Additional Settings -> TLS) | +| :--- | :--- | :--- | :--- | +| `synotrip.kucial.com` | `HTTPS` | `caddy:443` | `synotrip.kucial.com` | +| `blog.kucial.com` | `HTTPS` | `caddy:443` | `blog.kucial.com` | + +> [!NOTE] +> Setting **Origin Server Name** tells `cloudflared` which SNI hostname to send to Caddy during the TLS handshake, allowing Caddy to serve the matching Let's Encrypt certificate. + +### 3. Start Services + +```bash +docker compose up -d --build +``` + +### 4. Zero-Downtime Reload + +```bash +docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile +``` + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..684e4a0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + caddy: + build: + context: . + dockerfile: Dockerfile + container_name: caddy + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "443:443/udp" # HTTP/3 (QUIC) + environment: + - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN} + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - ./data:/data + - ./config:/config + - ./site:/srv + + cloudflared: + image: cloudflare/cloudflared:latest + container_name: cloudflared + restart: unless-stopped + command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} + environment: + - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + - caddy +