cloudflare / cloudflare/cloudflared
bug(ingress): host matching is case-sensitive, mixed-case Host/hostname misroutes (RFC 4343)
- Dominant language
- Go
- Stars
- 15.6k
- Forks
- 1.4k
- PR merge metrics
- No merged PRs in 30d
Description
**Describe the bug**
Ingress rule matching is case-sensitive, but hostnames are case-insensitive (RFC 4343; RFC 3986 §3.2.2 — "the host subcomponent is case-insensitive"). `matchHost` in `ingress/ingress.go` compares with `==` and `strings.HasSuffix` without normalizing case, and there is no case-normalization anywhere else in the ingress package:
```go
func matchHost(ruleHost, reqHost string) bool {
if ruleHost == reqHost {
return true
}
if strings.HasPrefix(ruleHost, "*.") {
toMatch := strings.TrimPrefix(ruleHost, "*")
return strings.HasSuffix(reqHost, toMatch)
}
return false
}
```
So any case difference between a rule's hostname and the request Host makes the request skip the intended rule and fall through to the catch-all (503 or the wrong origin).
This is reachable two ways:
- `proxy/proxy.go` passes `req.Host` (which can legitimately be mixed-case) straight into `FindMatchingRule` un-lowercased.
- `validateHostname` accepts mixed-case hostnames without lowercasing them, so a config such as `hostname: MyApp.example.com` is accepted but never matches `myapp.example.com`.
**To Reproduce**
`cloudflared tunnel ingress rule` (which passes `url.Hostname()` straight to `FindMatchingRule`) shows it, with no network needed. Given a config:
```yaml
ingress:
- hostname: myapp.example.com
service: http://localhost:8080
- service: http_status:404
```
```
$ cloudflared tunnel ingress rule https://MyApp.example.com/
```
reports the catch-all rule (`http_status:404`) instead of `myapp.example.com`. A unit test on `matchHost`/`FindMatchingRule` with a mixed-case host fails on `main`.
**Expected behavior**
Hostnames match case-insensitively, like every other HTTP router (nginx, Caddy, Envoy, Traefik).
**Fix**
Normalize case in `matchHost` (lower-case both sides before comparing). I have a small patch + tests ready and would like to open a PR.
Contributor guide
Research direction
Start with matchHost in ingress/ingress.go and the matchHost/FindMatchingRule unit tests described in the issue. Reproduce the mixed-case hostname case with cloudflared tunnel ingress rule and verify exact and wildcard host matching. Done means mixed-case request and configured hostnames select the intended ingress rule instead of the catch-all.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100