1. Output of caddy version
:
2.6.2
2. How I run Caddy:
a. System environment:
docker
b. Command:
docker-compose up
c. Service/unit/compose file:
version: '3.7'
services:
caddy:
container_name: p6proxy-caddy
image: amalto/p6proxy:local
volumes:
- ./caddata/logs:/data/logs:delegated
- ./config:/config:delegated
ports:
- 8443:8443
- 8092:8092
network_mode:
bridge
environment:
- SITE_DOMAIN=*
restart:
unless-stopped
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8092/metrics" ]
interval: 40s
timeout: 2s
retries: 5
start_period: 20s
logging:
driver: "json-file"
options:
max-size: "200k"
max-file: "10"
d. My complete Caddy config:
{
debug
https_port 8443
}
*:8443 {
file_server {
root /data/conf.d/static_html
}
handle_errors {
@custom_err file /err-{err.status_code}.html /err.html
handle @custom_err {
rewrite * {file_match.relative}
templates
file_server
}
respond "{err.status_code} {err.status_text}"
}
route * {
error "it's not found!" 404
}
}
3. The problem I’m having:
Error responses generated within the route{} do not appear to invoke the handle_errors
I’m trying to generate custom html error pages for a range of common status codes that can be generated with our route{}
Note: ideally I only want to generate html when the Accepts: header shows it is allowed and I'd also like to switch custom error pages based on the Language: header
4. Error messages and/or full log output:
curl --insecure -v "https://localhost:8443/foo"
* Trying 127.0.0.1:8443...
* Connected to localhost (127.0.0.1) port 8443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: [NONE]
* start date: Jan 12 10:52:18 2023 GMT
* expire date: Jan 12 22:52:18 2023 GMT
* issuer: CN=Caddy Local Authority - ECC Intermediate
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: GET]
* h2h3 [:path: /foo]
* h2h3 [:scheme: https]
* h2h3 [:authority: localhost:8443]
* h2h3 [user-agent: curl/7.85.0]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x7f7c0c00c800)
> GET /foo HTTP/2
> Host: localhost:8443
> user-agent: curl/7.85.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 404
< alt-svc: h3=":8443"; ma=2592000
< content-type: text/plain; charset=utf-8
< server: Caddy
< content-length: 13
< date: Thu, 12 Jan 2023 15:47:48 GMT
<
* Connection #0 to host localhost left intact
404 Not Found
p6proxy-caddy | {"level":"debug","ts":1673538604.9637895,"logger":"events","msg":"event","name":"tls_get_certificate","id":"e877e24e-9477-4f31-9b07-700e37138f71","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"localhost","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"Conn":{}}}}
p6proxy-caddy | {"level":"debug","ts":1673538604.963876,"logger":"tls.handshake","msg":"no matching certificates and no custom selection logic","identifier":"localhost"}
p6proxy-caddy | {"level":"debug","ts":1673538604.9638822,"logger":"tls.handshake","msg":"choosing certificate","identifier":"*","num_choices":1}
p6proxy-caddy | {"level":"debug","ts":1673538604.9638903,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"*","subjects":["*"],"managed":true,"issuer_key":"local","hash":"a838811d66add68f297cadda7ec24c66b1aaed4cd9dbe3493f3ff899efbd8b29"}
p6proxy-caddy | {"level":"debug","ts":1673538604.963895,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"172.17.0.1","remote_port":"56232","subjects":["*"],"managed":true,"expiration":1673563939,"hash":"a838811d66add68f297cadda7ec24c66b1aaed4cd9dbe3493f3ff899efbd8b29"}
p6proxy-caddy | {"level":"debug","ts":1673538604.995109,"logger":"http.log.error","msg":"it's not found!","request":{"remote_ip":"172.17.0.1","remote_port":"56232","proto":"HTTP/2.0","method":"GET","host":"localhost:8443","uri":"/foo","headers":{"Accept":["*/*"],"User-Agent":["curl/7.85.0"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"localhost"}},"duration":0.00002183,"status":404,"err_id":"7hz1zanr9","err_trace":"caddyhttp.StaticError.ServeHTTP (staticerror.go:110)"}
5. What I already tried:
Embedding the handle_errors
inside the route
- but that fails the Caddyfile validation
6. Links to relevant resources:
Our project to package caddy with a set of required modules: GitHub - amalto/caddy-secure-docker: A secure docker image for Caddy V2. This image includes Amalto vars_regex & jwt_valid plugins plus Maxmind geolocation and AWS Route53 plugins