This commit is contained in:
2026-09-15 06:59:44 +08:00
commit 64c194a5d8
6 changed files with 196 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# Cloudflare API Token (Requires Zone:DNS:Edit permission)
CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here
ACME_EMAIL=[email protected]
# Cloudflare Tunnel Token (From Cloudflare Zero Trust Dashboard -> Networks -> Tunnels)
CLOUDFLARE_TUNNEL_TOKEN=your_cloudflare_tunnel_token_here
+6
View File
@@ -0,0 +1,6 @@
# Local environment variables
.env
# Caddy persistent storage and configurations
data/
config/
+93
View File
@@ -0,0 +1,93 @@
{
email {$ACME_EMAIL:[email protected]}
}
# 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
}
+8
View File
@@ -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
+49
View File
@@ -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
```
+33
View File
@@ -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