Replace Cloudflare DNS challenge with standard Let's Encrypt HTTP-01
Caddy's built-in ACME support handles TLS automatically — no CF_API_TOKEN, no Cloudflare account, no DNS plugin needed. Requires ports 80+443 forwarded to the Pi and ACME_EMAIL set in infra/.env.
This commit is contained in:
parent
3794f4cf36
commit
dc59293c5e
4 changed files with 46 additions and 83 deletions
|
|
@ -140,29 +140,24 @@ docker run --rm hello-world # verify
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Domain and DNS (Cloudflare)
|
## 6. Domain and DNS
|
||||||
|
|
||||||
You need a domain whose DNS is managed by Cloudflare (the free plan is fine).
|
You need a domain pointing to your Pi's public IP. Any DNS provider works.
|
||||||
|
|
||||||
1. In Cloudflare, add two DNS records pointing to your **public home IP**:
|
1. Add DNS A records pointing to your **public home IP**:
|
||||||
|
|
||||||
| Type | Name | Content | Proxy |
|
| Type | Name | Content |
|
||||||
|---|---|---|---|
|
|---|---|---|
|
||||||
| A | `hiy` | `<your-public-IP>` | Proxied (orange cloud) |
|
| A | `hiy` | `<your-public-IP>` |
|
||||||
| A | `*` | `<your-public-IP>` | Proxied (orange cloud) |
|
| A | `*` | `<your-public-IP>` |
|
||||||
|
|
||||||
The wildcard `*` record routes `<appname>.yourdomain.com` to the Pi.
|
The wildcard `*` record routes `<appname>.yourdomain.com` to the Pi.
|
||||||
|
|
||||||
2. In **Cloudflare → SSL/TLS → Overview**, set mode to **Full (strict)**.
|
|
||||||
|
|
||||||
3. Create a **Cloudflare API token** for Caddy's ACME DNS challenge:
|
|
||||||
- Go to My Profile → API Tokens → Create Token
|
|
||||||
- Use the **Edit zone DNS** template, scope it to your zone
|
|
||||||
- Copy the token — you will need it in step 8
|
|
||||||
|
|
||||||
### Port forwarding
|
### Port forwarding
|
||||||
|
|
||||||
Forward the following ports on your router to the Pi's static IP:
|
Forward the following ports on your router to the Pi's static IP.
|
||||||
|
**Both ports are required** — Caddy uses port 80 to prove domain ownership
|
||||||
|
to Let's Encrypt before issuing the HTTPS certificate.
|
||||||
|
|
||||||
| External | Internal |
|
| External | Internal |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -195,63 +190,20 @@ Minimum required variables:
|
||||||
# Your domain (apps will be at <name>.yourdomain.com)
|
# Your domain (apps will be at <name>.yourdomain.com)
|
||||||
DOMAIN_SUFFIX=yourdomain.com
|
DOMAIN_SUFFIX=yourdomain.com
|
||||||
|
|
||||||
# Cloudflare API token for ACME DNS-01 challenge
|
# Email for Let's Encrypt expiry notices
|
||||||
CF_API_TOKEN=your_cloudflare_api_token
|
ACME_EMAIL=you@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Caddy — automatic HTTPS
|
## 9. Caddy — automatic HTTPS
|
||||||
|
|
||||||
Caddy handles TLS via Cloudflare's DNS challenge (no port-80 HTTP challenge
|
Caddy obtains a Let's Encrypt certificate automatically via the HTTP-01
|
||||||
needed, works even behind CGNAT).
|
challenge. No DNS API token or Cloudflare account required.
|
||||||
|
|
||||||
Edit `proxy/caddy.json` and replace the top-level config with:
|
The `proxy/Caddyfile` is already configured — nothing to edit here.
|
||||||
|
Just make sure `DOMAIN_SUFFIX` and `ACME_EMAIL` are set in `infra/.env`
|
||||||
```json
|
and that ports 80 and 443 are forwarded to the Pi (see step 6).
|
||||||
{
|
|
||||||
"admin": { "listen": "0.0.0.0:2019" },
|
|
||||||
"apps": {
|
|
||||||
"tls": {
|
|
||||||
"automation": {
|
|
||||||
"policies": [{
|
|
||||||
"subjects": ["*.yourdomain.com", "yourdomain.com"],
|
|
||||||
"issuers": [{
|
|
||||||
"module": "acme",
|
|
||||||
"challenges": {
|
|
||||||
"dns": {
|
|
||||||
"provider": {
|
|
||||||
"name": "cloudflare",
|
|
||||||
"api_token": "{env.CF_API_TOKEN}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"servers": {
|
|
||||||
"hiy": {
|
|
||||||
"listen": [":80", ":443"],
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"handle": [{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"upstreams": [{"dial": "server:3000"}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note:** The Caddy image in `docker-compose.yml` needs the Cloudflare DNS
|
|
||||||
> plugin. Use `caddy:2-alpine` with `xcaddy` or replace the image with
|
|
||||||
> `ghcr.io/caddybuilds/caddy-cloudflare:latest`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -427,7 +379,7 @@ Caddy never restarts.
|
||||||
| Symptom | Likely cause | Fix |
|
| Symptom | Likely cause | Fix |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Dashboard not reachable | Compose not started / port 443 not forwarded | `docker compose logs server` |
|
| Dashboard not reachable | Compose not started / port 443 not forwarded | `docker compose logs server` |
|
||||||
| TLS certificate error | CF_API_TOKEN wrong or DNS not propagated | Check Caddy logs: `docker compose logs caddy` |
|
| TLS certificate error | Port 80/443 not forwarded or DNS not propagated yet | Check Caddy logs: `docker compose logs caddy` |
|
||||||
| Build fails — "docker not found" | Docker socket not mounted | Verify `docker-proxy` service is up |
|
| Build fails — "docker not found" | Docker socket not mounted | Verify `docker-proxy` service is up |
|
||||||
| Build fails — "pack not found" | `pack` not installed | See step 11 |
|
| Build fails — "pack not found" | `pack` not installed | See step 11 |
|
||||||
| Webhook returns 401 | Secret mismatch | Regenerate app, re-copy secret to GitHub |
|
| Webhook returns 401 | Secret mismatch | Regenerate app, re-copy secret to GitHub |
|
||||||
|
|
|
||||||
5
infra/.env.example
Normal file
5
infra/.env.example
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Your domain — apps will be served at <name>.yourdomain.com
|
||||||
|
DOMAIN_SUFFIX=yourdomain.com
|
||||||
|
|
||||||
|
# Email address for Let's Encrypt expiry notices (required for HTTPS)
|
||||||
|
ACME_EMAIL=you@example.com
|
||||||
|
|
@ -53,11 +53,14 @@ services:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
- "2019:2019" # admin API
|
- "2019:2019" # admin API
|
||||||
|
environment:
|
||||||
|
DOMAIN_SUFFIX: ${DOMAIN_SUFFIX:-localhost}
|
||||||
|
ACME_EMAIL: ${ACME_EMAIL:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ../proxy/caddy.json:/etc/caddy/caddy.json:ro
|
- ../proxy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
- caddy-data:/data
|
- caddy-data:/data
|
||||||
- caddy-config:/config
|
- caddy-config:/config
|
||||||
command: caddy run --config /etc/caddy/caddy.json
|
command: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||||
networks:
|
networks:
|
||||||
- hiy-net
|
- hiy-net
|
||||||
- default
|
- default
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,32 @@
|
||||||
# HIY — Caddyfile
|
# HIY — Caddyfile
|
||||||
#
|
#
|
||||||
# Local development: auto-HTTPS is disabled; everything runs on :80.
|
# Caddy automatically obtains a Let's Encrypt certificate for every domain it
|
||||||
# Production (Pi): remove the `auto_https off` line, set your real email,
|
# serves (HTTP-01 challenge). No Cloudflare or DNS API token required.
|
||||||
# and Caddy will obtain Let's Encrypt certificates automatically.
|
|
||||||
#
|
#
|
||||||
# Wildcard TLS on the Pi (recommended):
|
# Requirements:
|
||||||
# tls your@email.com {
|
# - Ports 80 and 443 must be publicly reachable (router port-forward to Pi)
|
||||||
# dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
# - DNS A record for {$DOMAIN_SUFFIX} must point to your public IP
|
||||||
# }
|
# - Set ACME_EMAIL in infra/.env (Let's Encrypt needs a contact address)
|
||||||
|
#
|
||||||
|
# Local dev: set DOMAIN_SUFFIX=localhost in infra/.env — Caddy will use a
|
||||||
|
# self-signed cert automatically for localhost.
|
||||||
|
|
||||||
{
|
{
|
||||||
# Admin API — used by build.sh to update routes dynamically.
|
# Admin API — used by hiy-server to add/remove app routes dynamically.
|
||||||
admin 0.0.0.0:2019
|
admin 0.0.0.0:2019
|
||||||
|
|
||||||
# Comment this out on the Pi with a real domain.
|
# Contact email for Let's Encrypt expiry notices.
|
||||||
auto_https off
|
email {$ACME_EMAIL}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fallback: serves the HIY dashboard itself.
|
# HIY dashboard — served at your root domain.
|
||||||
:80 {
|
{$DOMAIN_SUFFIX} {
|
||||||
reverse_proxy server:3000
|
reverse_proxy server:3000
|
||||||
}
|
}
|
||||||
|
|
||||||
# Example of what the build script adds via the Caddy API:
|
# Deployed apps are added here dynamically by hiy-server via the Caddy API.
|
||||||
|
# Each entry looks like:
|
||||||
#
|
#
|
||||||
# myapp.yourdomain.com {
|
# myapp.{$DOMAIN_SUFFIX} {
|
||||||
# reverse_proxy <container-ip>:3000
|
# reverse_proxy <container-ip>:<port>
|
||||||
# }
|
# }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue