Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP header HOST always modified when externalName is used in service backend #3343

Open
pshen opened this issue Dec 12, 2024 · 4 comments
Open

Comments

@pshen
Copy link

pshen commented Dec 12, 2024

Describe the bug
I observed strange behavior when externalName type is used in the service backend, the HTTP header HOST of request is always modified to the value of externalName.

To Reproduce

in namespace NS-1:
ingress
-- Host: foo.com
-- backend: ingressgateway

svc
-- name: ingressgateway
-- externalName: bar.NS-2.svc.cluster.local

When the traffic of foo.com comes, the data flow looks like below:
ingress:foo.com(NS-1) -> svc:ingressgateway(NS-1) -> bar.NS-2.svc.cluster.local(NS-2)

However the request to bar.NS-2.svc.cluster.local, whose HTTP header HOST is always modified to bar.NS-2.svc.cluster.local, I actually expected the HTTP header HOST is still foo.com.

Expected behavior
The HTTP header HOST in the request to externalName backend should be kept, or this behavior is configurable.

Observed behavior
The HTTP header HOST in the request to externalName backend is always modified to the value of externalName.

I checked the documentation, but I can't find any configuration to control this behavior to keep the HOST value or change it to the destination.

@AlexanderYastrebov
Copy link
Member

AlexanderYastrebov commented Dec 12, 2024

Hello, looks like skipper always sets Host header (via setRequestHeader) equal to external name:

func externalNameRoute(
ns, name, idHost string,
hostRegexps []string,
svc *service,
servicePort *servicePort,
allowedNames []*regexp.Regexp,
) (*eskip.Route, error) {
if !isExternalDomainAllowed(allowedNames, svc.Spec.ExternalName) {
return nil, fmt.Errorf("%w: %s", errNotAllowedExternalName, svc.Spec.ExternalName)
}
scheme := "https"
if n, _ := servicePort.TargetPort.Number(); n != 443 {
scheme = "http"
}
u := fmt.Sprintf("%s://%s:%s", scheme, svc.Spec.ExternalName, servicePort.TargetPort)
f, err := eskip.ParseFilters(fmt.Sprintf(`setRequestHeader("Host", "%s")`, svc.Spec.ExternalName))
if err != nil {
return nil, err
}
return &eskip.Route{
Id: routeID(ns, name, idHost, "", svc.Spec.ExternalName),
BackendType: eskip.NetworkBackend,
Backend: u,
Filters: f,
HostRegexps: hostRegexps,
}, nil
}

I think we may consider adding a flag to disable this and let user control which host to use via preserveHost filter and -proxy-preserve-host flag.

@pshen
Copy link
Author

pshen commented Dec 13, 2024

Hi @AlexanderYastrebov ,

I can understand sometimes this behavior is wanted if the externalName is an internet website, like google.com. Of course google.com expects the HOST header in the request is also "google.com".

However if the externalName is some other ingress in the same k8s cluster, like in my case bar.NS-2.svc.cluster.local, I don't want skipper to always modify the HOST header in the request.

An annotation configuration could solve this issue.

@AlexanderYastrebov
Copy link
Member

As a workaround for now you may consider using RouteGroup CRD with a network backend instead of ExtrenalName service.

@pshen
Copy link
Author

pshen commented Dec 13, 2024

Thanks Alex for the tip, we prefer to use ingress and its annotation configs. Currently we're working on completely avoiding externalName in the setup.

Hopefully one day, this behavior can be configurable in skipper. :-D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants