HTTPS Redirects not working with Wildcard Domains

:wave: I’m having some trouble getting Caddy to serve redirects for HTTPs correctly when mixing wildcard domains, and HTTP only websites.

1. The problem I’m having:

I have a Caddyfile that will serve three things:

  1. A healthcheck endpoint on a non-standard port, on localhost
  2. An internal only service on an HTTP port, on an internal domain
  3. A number of externally accessible subdomains on HTTPS, served with on_demand TLS and a wildcard DNS certificate, on a second external domain

I am able to get this configuration to work, except that the externally accessible domains do not issue 301 redirects from the HTTP endpoint.

2. Error messages and/or full log output:

I can hit the external domain on HTTPS as expected:

curl -v https://rpc.external-example.com/
* Host rpc.external-example.com:443 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
*   Trying x.x.x.x:443...
* Connected to rpc.external-example.com (x.x.x.x) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (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-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=rpc.external-example.com
*  start date: Jun 27 23:18:41 2024 GMT
*  expire date: Sep 25 23:18:40 2024 GMT
*  subjectAltName: host "rpc.external-example.com" matched cert's "rpc.external-example.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://rpc.external-example.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: rpc.external-example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: rpc.external-example.com
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/2 200 
< content-type: application/json
< date: Sun, 30 Jun 2024 21:16:42 GMT
< server: Caddy
     [snip]

However, HTTP requests error, and I’d expect them to issue an HTTP 301 redirect:

curl -v http://rpc.external-example.com/
* Host rpc.external-example.com:80 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
* Trying x.x.x.x:80...
* Connected to rpc.external-example.com (x.x.x.x) port 80
> GET / HTTP/1.1
> Host: rpc.external-example.com
> User-Agent: curl/8.6.0
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection
curl: (56) Recv failure: Connection reset by peer

For completeness, the other endpoints are working over HTTP just fine:

Healthcheck works as expected:

curl -v 127.0.0.1:8080/healthz
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /healthz HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Date: Sun, 30 Jun 2024 21:28:52 GMT
< Content-Length: 7
< 
* Connection #0 to host 127.0.0.1 left intact
health

Internal site also works as expected:

curl -v rpc.internal-example.com/
* Host rpc.internal-example.com:80 was resolved.
* IPv6: (none)
* IPv4: x.x.x.x
*   Trying x.x.x.x:80...
* Connected to rpc.internal-example.com (x.x.x.x) port 80
> GET / HTTP/1.1
> Host: rpc.internal-example.com
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 863924
< Content-Type: text/html; charset=utf-8
< Date: Sun, 30 Jun 2024 21:14:06 GMT
< Server: Caddy
    [snip]

3. Caddy version:

Caddy: tags/v2.8.4
XCaddy: tags/v0.4.2

4. How I installed and ran Caddy:

Build xcaddy:

go build  -trimpath -o ./ ./...

Build Caddy:

xcaddy build v2.8.4
        --output ./caddy
        --with github.com/caddy-dns/route53

Running Caddy:

a. System environment:

Ubuntu Jammy

b. Command:

Simply on the CLI for demoing:

./caddy run --config ./Caddyfile --environ --watch

c. Service/unit/compose file:

N/A

d. My complete Caddy config:

# Global Options
{
  # Enable http/https and cleartext grpc
	servers { 
		protocols h1 h2 h2c
 	}

	log default {
		output stdout
		format console
	}

	# Request on demand
  # For demo purposes, this always returns 200.
	on_demand_tls {
		ask http://rpc.internal-example.com/v1/caddy/ask
	}
}

# An internal service, served on http
http://rpc.internal-example.com {
	# Try to dynamically serve a service from Consul, with a static fallback
	reverse_proxy {
		dynamic srv {
			name rpc-internal.service.consul
			refresh 15s
		}
		to http://internal.ts.net:9090
		
		lb_policy random
	}
}

# Heath check, which is served on localhost over HTTP
# Inspired by: https://caddy.community/t/health-check-endpoint-to-monitor-caddy-itself/1517
http://127.0.0.1:8080 {
	handle /healthz {
		respond "healthy"
	}
}

# External services
*.external-example.com.com {
	tls {
		# Use Route53 DNS for ACME
		dns route53 {
			max_retries 10
			access_key_id "<redacted>"
			secret_access_key "<redacted>"
			region "<redacted>"
		}
		
		# See: https://github.com/caddy-dns/route53/issues/43
		propagation_timeout -1
		propagation_delay 20s
		
		# Use on demand TLS.
		# Not strictly needed in this example, but needed in the production version.
		on_demand
	}

	@external-rpc host rpc.external-example.com
	handle @external-rpc {
		reverse_proxy {
			dynamic srv {
				name rpc-external.service.consul
				refresh 15s
			}
			
			lb_policy random
		}
	}

	@external-rest host rest.external-example.com.com
	handle @external-rest {
		reverse_proxy {
			dynamic srv {
				name rest-external.service.consul
				refresh 15s
			}
			
			lb_policy random
		}
	}
}

Sorry for the wait, had a busy week.

This is telling me port 80 just isn’t open. The connection isn’t reaching Caddy at all. It’s not a problem with Caddy, but a problem with your network in front of Caddy.

2 Likes

No worries on the response latency, I know you folks are busy :slight_smile:

On my end, oof. That’s obvious in retrospect. I hadn’t thought I had a connection issue because I could issue connections to the internal site fine, but in fact you’re right that that is the issue.

For future folks who might come digging here: I had correctly configured a firewall on the internal network interface, but not the external one for connections on 80, and that’s what was messing things up.

2 Likes