1. The problem I’m having:
I have a caddyfile to manages a bunch of subdomains and have worked out a map directive that works for me, to map hosts to backends.
I am needed to add a new backend that requires uri strip_prefix /alexa
to work. It is possible that in the future I might have another backend that needs uri strip_prefix /something_else
. So I want to add this as an optional map variable. But it doesn’t seem to work.
Request I am making, with the output (from http-echo server mendhak/http-https-echo):
curl -vL https://dorewrite.realsam.co/alexa/hello
* Trying 38.68.134.37:443...
* TCP_NODELAY set
* Connected to dorewrite.realsam.co (38.68.134.37) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=realsam.co
* start date: Oct 22 01:16:02 2024 GMT
* expire date: Jan 20 01:16:01 2025 GMT
* subjectAltName: host "dorewrite.realsam.co" matched cert's "*.realsam.co"
* issuer: C=US; O=Let's Encrypt; CN=E6
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x556877c600e0)
> GET /alexa/hello HTTP/2
> Host: dorewrite.realsam.co
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< content-type: application/json; charset=utf-8
< date: Thu, 24 Oct 2024 03:39:19 GMT
< etag: W/"257-wixQIzjyKMDXL23PdyEMjJo8TZA"
< server: Caddy
< x-powered-by: Express
< content-length: 599
<
{
"path": "/alexa/hello",
"headers": {
"host": "dorewrite.realsam.co",
"user-agent": "curl/7.68.0",
"accept": "*/*",
"x-forwarded-for": "202.144.161.92",
"x-forwarded-host": "dorewrite.realsam.co",
"x-forwarded-proto": "https",
"accept-encoding": "gzip"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "dorewrite.realsam.co",
"ip": "202.144.161.92",
"ips": [
"202.144.161.92"
],
"protocol": "https",
"query": {},
"subdomains": [
"dorewrite"
],
"xhr": false,
"os": {
"hostname": "http-echo"
},
"connection": {}
* Connection #0 to host dorewrite.realsam.co left intact
}%
I am expecting to see:
{
"path": "/hello",
...
}
2. Error messages and/or full log output:
Nothing wrong seen in caddy debug logs:
{"level":"info","ts":1729741151.2381005,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/load","remote_ip":"127.0.0.1","remote_port":"59950","headers":{"Accept-Encoding":["gzip"],"Content-Length":["3364"],"Content-Type":["application/json"],"Origin":["http://localhost:2019"],"User-Agent":["Go-http-client/1.1"]}}
{"level":"info","ts":1729741151.238755,"msg":"config is unchanged"}
{"level":"info","ts":1729741151.2387686,"logger":"admin.api","msg":"load complete"}
{"level":"debug","ts":1729741159.3723786,"logger":"events","msg":"event","name":"tls_get_certificate","id":"5682b465-5d6a-46a0-b198-6e6d4857ae46","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"ServerName":"dorewrite.realsam.co","SupportedCurves":[29,23,30,25,24],"SupportedPoints":"AAEC","SignatureSchemes":[1027,1283,1539,2055,2056,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"RemoteAddr":{"IP":"202.144.161.92","Port":30792,"Zone":""},"LocalAddr":{"IP":"10.62.191.2","Port":443,"Zone":""}}}}
{"level":"debug","ts":1729741159.3724205,"logger":"tls.handshake","msg":"no matching certificate; will choose from all certificates","identifier":"dorewrite.realsam.co"}
{"level":"debug","ts":1729741159.3724356,"logger":"tls.handshake","msg":"choosing certificate","identifier":"dorewrite.realsam.co","num_choices":2}
{"level":"debug","ts":1729741159.3725164,"logger":"tls.handshake","msg":"custom certificate selection results","identifier":"dorewrite.realsam.co","subjects":["realsam.co","*.nlsspeaker.realsam.co","*.realsam.co"],"managed":false,"issuer_key":"","hash":"fb5c3f239850649a597ca3a211d2bb54bab8bdabb35c4f526929772ba926d390"}
{"level":"debug","ts":1729741159.3725343,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"202.144.161.92","remote_port":"30792","subjects":["realsam.co","*.nlsspeaker.realsam.co","*.realsam.co"],"managed":false,"expiration":1737335762,"hash":"fb5c3f239850649a597ca3a211d2bb54bab8bdabb35c4f526929772ba926d390"}
{"level":"debug","ts":1729741159.5844364,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"http-echo:8080","total_upstreams":1}
{"level":"debug","ts":1729741159.5920455,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"{backend}","duration":0.007529887,"request":{"remote_ip":"202.144.161.92","remote_port":"30792","client_ip":"202.144.161.92","proto":"HTTP/2.0","method":"GET","host":"dorewrite.realsam.co","uri":"/alexa/hello","headers":{"User-Agent":["curl/7.68.0"],"Accept":["*/*"],"X-Forwarded-For":["202.144.161.92"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["dorewrite.realsam.co"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"dorewrite.realsam.co"}},"headers":{"Content-Type":["application/json; charset=utf-8"],"Content-Length":["599"],"Etag":["W/\"257-wixQIzjyKMDXL23PdyEMjJo8TZA\""],"Date":["Thu, 24 Oct 2024 03:39:19 GMT"],"Connection":["keep-alive"],"Keep-Alive":["timeout=5"],"X-Powered-By":["Express"]},"status":200}
3. Caddy version:
root@us-docker3:~/docker-servers# docker exec ds-caddy caddy version
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
4. How I installed and ran Caddy:
Docker Compose
a. System environment:
.env:
NET_DOT=10.62.191
NET_DASH=10-62-191
MYIP_DASH=38-68-134-37
OS: Ubuntu 22.04.2 LTS
Arch: x64
Docker Version:
root@us-docker3:~/docker-servers# docker version
Client: Docker Engine - Community
Version: 24.0.4
API version: 1.43
Go version: go1.20.5
Git commit: 3713ee1
Built: Fri Jul 7 14:50:55 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 24.0.4
API version: 1.43 (minimum version 1.12)
Go version: go1.20.5
Git commit: 4ffc614
Built: Fri Jul 7 14:50:55 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.21
GitCommit: 3dce8eb055cbb6872793272b4f20ed16117344f8
runc:
Version: 1.1.7
GitCommit: v1.1.7-0-g860f061
docker-init:
Version: 0.19.0
GitCommit: de40ad0
b. Command:
root@us-docker3:~/docker-servers# docker exec -it ds-caddy ps
PID USER TIME COMMAND
1 root 1:19 caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
To reload my config, I use:
root@us-docker3:~/docker-servers# docker exec -it ds-caddy caddy reload --config /etc/caddy/Caddyfile
2024/10/24 03:52:46.908 INFO using config from file {"file": "/etc/caddy/Caddyfile"}
2024/10/24 03:52:46.913 INFO adapted config to JSON {"adapter": "caddyfile"}
2024/10/24 03:52:46.913 WARN Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies {"adapter": "caddyfile", "file": "/etc/caddy/Caddyfile", "line": 8}
c. Service/unit/compose file:
services:
ds-caddy:
image: caddy:2-alpine
container_name: ds-caddy
hostname: ds-caddy
restart: on-failure
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./vols/caddy_data:/data
- ./vols/caddy_config:/config
- /root/sslcerts:/root/sslcerts
environment:
- NET_DASH=${NET_DASH}
- NET_DOT=${NET_DOT}
- MYIP_DASH=${MYIP_DASH}
networks:
ds-net:
ipv4_address: ${NET_DOT}.2
d. My complete Caddy config:
I haven’t run caddy fmt
because it gets rid of the spacing in the map directive.
Pls see: ## NEW SERVICE TO ADD
{
debug
}
*.realsam.co *.*.realsam.co {
tls /root/sslcerts/realsam.co/caddy/realsam_co_cert.pem /root/sslcerts/realsam.co/caddy/realsam_co_key.pem
map "{labels.3}.{labels.2}" {backend} {slash_redir} {strip_alexa} {
## NEW SERVICE TO ADD
.dorewrite http-echo:8080 - /alexa
# all DS
~.ds-{$NET_DASH}-([3456789])$ "{$NET_DOT}.${1}:1900${1}"
~.ds-{$NET_DASH}-([1-9][0-9])$ "{$NET_DOT}.${1}:190${1}"
# streamers under our control, match st-(name) with streamer-(name)
~.st-([a-zA-Z]*)-{$MYIP_DASH}$ "streamer-${1}:32003"
# webhooks - preset
.w1-mtb-alexa webhook-mytalkingbooks:32001
.realpaolo webhook-nlspaolo:32001
realbooks.nlsspeaker webhook-realbooks:32001
develop.nlsspeaker webhook-qa:32001
# authproxy - preset
.auth-mtb-nlsspeaker authproxy-mytalkingbooks:7779
.mytalkingbookslogin authproxy-mytalkingbooks:7779 webcontact
.auth-smrtdev authproxy-smrtdev:7779
.nlsloginsmrtdev authproxy-smrtdev:7779 webcontact
.auth-realbooks-nlsspeaker authproxy-realbooks:7779
.realbookslogin authproxy-realbooks:7779 webcontact
.auth-nlsspeaker authproxy-nls:7779
.nlslogin authproxy-nls:7779 webcontact
# default
default - - -
}
@slash_redir_set not vars {slash_redir} -
handle @slash_redir_set {
rewrite / /{slash_redir}
rewrite /* /{slash_redir}{path}?{query}
reverse_proxy {backend}
}
@slash_strip_alexa_set not vars {strip_alexa} -
handle @slash_strip_alexa_set {
uri strip_prefix {strip_alexa}
reverse_proxy {backend}
# reverse_proxy http-echo:8080
}
@backend_set not vars {backend} -
handle @backend_set {
reverse_proxy {backend}
}
## Everything else
handle {
respond "{host} Not Found!" 404
}
}
:443 {
respond "Bad Domain {host}!" 404
}
5. Links to relevant resources:
6. Other questions/thoughts:
Any suggestions to improve this caddyfile are highly appereciated!
I am sure there are better ways to do things, I would love to know more, as long as I get to keep the same behaviour.