In follow-up to my previous thread, I’ve made changes to my deployment that allow TLS-ALPN-01 and HTTP-01 (in lieu of DNS-01 which takes too long).
When accessing sites directly (without CloudFront), ALPN works fantastic, and a cert is issued typically in < 5s.
However, since ALPN is not supported with CloudFront, I also enabled HTTP-01, but I can’t seem to quite get it to work–the request from ACME seems to go all the way to the backend service, rather than having Caddy respond directly.
Note that the CF distribution has HTTP/2 + HTTP/3 enabled, and is passing both 80+443 ports to Caddy.
Caddy Logs
May 19 08:32:42 2023/05/19 15:32:42.953 INFO tls.on_demand obtaining new certificate {"remote_ip": "172.16.45.16", "remote_port": "59450", "server_name": "prod4.localdev.mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.245 INFO tls.obtain acquiring lock {"identifier": "prod4.localdev.mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.359 INFO tls.obtain lock acquired {"identifier": "prod4.localdev.mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.360 INFO tls.obtain obtaining certificate {"identifier": "prod4.localdev.mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.364 INFO http waiting on internal rate limiter {"identifiers": ["prod4.localdev.mywordpress.io"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": "info@mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.364 INFO http done waiting on internal rate limiter {"identifiers": ["prod4.localdev.mywordpress.io"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": "info@mywordpress.io"}
May 19 08:32:45 2023/05/19 15:32:45.853 INFO http.acme_client trying to solve challenge {"identifier": "prod4.localdev.mywordpress.io", "challenge_type": "http-01", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
May 19 08:32:48 2023/05/19 15:32:48.382 INFO layer4.handlers.proxy.health_checker.active host is down {"address": "172.16.46.54:25566", "timeout": 5, "error": "dial tcp 172.16.46.54:25566: connect: connection refused"}
May 19 08:32:48 2023/05/19 15:32:48.787 ERROR http.acme_client challenge failed {"identifier": "prod4.localdev.mywordpress.io", "challenge_type": "http-01", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "216.137.45.107: Invalid response from https://prod4.localdev.mywordpress.io/wp-admin/install.php: \"<!DOCTYPE html>\\n<html lang=\\\"en-US\\\" xml:lang=\\\"en-US\\\">\\n<head>\\n\\t<meta name=\\\"viewport\\\" content=\\\"width=device-width\\\" />\\n\\t<meta http-e\"", "instance": "", "subproblems": []}}
May 19 08:32:48 2023/05/19 15:32:48.787 ERROR http.acme_client validating authorization {"identifier": "prod4.localdev.mywordpress.io", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "216.137.45.107: Invalid response from https://prod4.localdev.mywordpress.io/wp-admin/install.php: \"<!DOCTYPE html>\\n<html lang=\\\"en-US\\\" xml:lang=\\\"en-US\\\">\\n<head>\\n\\t<meta name=\\\"viewport\\\" content=\\\"width=device-width\\\" />\\n\\t<meta http-e\"", "instance": "", "subproblems": []}, "order": "https://acme-v02.api.letsencrypt.org/acme/order/1117020427/183199142017", "attempt": 1, "max_attempts": 3}
May 19 08:32:50 2023/05/19 15:32:50.066 INFO http.acme_client trying to solve challenge {"identifier": "prod4.localdev.mywordpress.io", "challenge_type": "tls-alpn-01", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
May 19 08:32:50 2023/05/19 15:32:50.635 ERROR http.acme_client challenge failed {"identifier": "prod4.localdev.mywordpress.io", "challenge_type": "tls-alpn-01", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "Cannot negotiate ALPN protocol \"acme-tls/1\" for tls-alpn-01 challenge", "instance": "", "subproblems": []}}
May 19 08:32:50 2023/05/19 15:32:50.636 ERROR http.acme_client validating authorization {"identifier": "prod4.localdev.mywordpress.io", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "Cannot negotiate ALPN protocol \"acme-tls/1\" for tls-alpn-01 challenge", "instance": "", "subproblems": []}, "order": "https://acme-v02.api.letsencrypt.org/acme/order/1117020427/183199151007", "attempt": 2, "max_attempts": 3}
May 19 08:32:50 2023/05/19 15:32:50.636 ERROR tls.obtain could not get certificate from issuer {"identifier": "prod4.localdev.mywordpress.io", "issuer": "acme-v02.api.letsencrypt.org-directory", "error": "HTTP 403 urn:ietf:params:acme:error:unauthorized - Cannot negotiate ALPN protocol \"acme-tls/1\" for tls-alpn-01 challenge"}
May 19 08:32:50 2023/05/19 15:32:50.636 ERROR tls.obtain will retry {"error": "[prod4.localdev.mywordpress.io] Obtain: [prod4.localdev.mywordpress.io] solving challenge: prod4.localdev.mywordpress.io: [prod4.localdev.mywordpress.io] authorization failed: HTTP 403 urn:ietf:params:acme:error:unauthorized - Cannot negotiate ALPN protocol \"acme-tls/1\" for tls-alpn-01 challenge (ca=https://acme-v02.api.letsencrypt.org/directory)", "attempt": 1, "retrying_in": 60, "elapsed": 5.277105841, "max_duration": 2592000}
Caddy Config
{
"admin": {
"config": {
"persist": false
},
"disabled": false,
"enforce_origin": false,
"listen": "127.0.0.1:8443",
"origins": [
"http://127.0.0.1:8443"
]
},
"logging": {
"logs": {
"default": {
"encoder": {
"format": "console"
},
"level": "info",
"writer": {
"output": "stdout"
}
}
},
"sink": {
"writer": {
"output": "stderr"
}
}
},
"storage": {
"address": "https://vault:8200",
"approle_login_path": "auth/approle/login",
"approle_logout_path": "auth/token/revoke-self",
"approle_role_id": "dead-beef",
"approle_secret_id": "ea7-beef",
"insecure_skip_verify": true,
"module": "vault",
"path_prefix": "production/caddy",
"secrets_path": "klm/secrets"
},
"apps": {
"tls": {
"automation": {
"on_demand": {
"ask": "https://a.b.c.d/caddy/onDemand",
"rate_limit": {
"burst": 3,
"interval": "1m"
}
},
"policies": [
{
"issuers": [
{
"ca": "https://acme-v02.api.letsencrypt.org/directory",
"challenges": {
"http": {
"disabled": false
},
"tls-alpn": {
"disabled": false
}
},
"email": "info@mywordpress.io",
"module": "acme"
}
],
"on_demand": true
}
]
}
},
"http": {
"grace_period": "30s",
"http_port": 80,
"https_port": 443,
"servers": {
"http": {
"listen": [
"0.0.0.0:80"
],
"routes": [
{
"group": "default",
"handle": [
{
"body": "DEFAULT ROUTE",
"close": true,
"handler": "static_response",
"status_code": "200"
}
]
}
]
},
"https": {
"listen": [
"0.0.0.0:443"
],
"metrics": {},
"routes": [
{
"group": "default",
"handle": [
{
"dynamic_upstreams": {
"dial_timeout": "750ms",
"name": "service.tld",
"port": "443",
"refresh": "30s",
"source": "a"
},
"handler": "reverse_proxy",
"load_balancing": {
"selection_policy": {
"policy": "least_conn"
}
},
"transport": {
"protocol": "http",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
],
"tls_connection_policies": [
{}
]
}
}
}
}
}
I am not quite able to understand why the HTTP-01 challenge is not being served by Caddy when it’s requested through port 80. It seems like the HTTP-01 challenge is getting all the way to the backend service (which then says “hey, go here for installation”), even though I would expect Caddy to directly serve the HTTP-01 challenge response first, before it gets that far? Maybe I’m not understanding how that part of HTTP-01 works.