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

Kubernetes support externalname service #1149

Merged
56 changes: 56 additions & 0 deletions docs/user-guide/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,59 @@ You should now be able to visit the websites in your browser.
* [cheeses.local/stilton](http://cheeses.local/stilton/)
* [cheeses.local/cheddar](http://cheeses.local/cheddar/)
* [cheeses.local/wensleydale](http://cheeses.local/wensleydale/)

## Disable passing the Host header
By default Træfɪk will pass the incoming Host header on to the upstream resource. There
are times however where you may not want this to be the case. For example if your service
is of the ExternalName type.

### Disable entirely
Add the following to your toml config:
```toml
disablePassHostHeaders = true
```

### Disable per ingress
To disable passing the Host header per ingress resource set the "traefik.frontend.passHostHeader"
annotation on your ingress to "false".

Here is an example ingress definition:
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example
annotations:
traefik.frontend.passHostHeader: "false"
spec:
rules:
- host: example.com
http:
paths:
- path: /static
backend:
serviceName: static
servicePort: https
```

And an example service definition:
```yaml
apiVersion: v1
kind: Service
metadata:
name: static
spec:
ports:
- name: https
port: 443
type: ExternalName
externalName: static.otherdomain.com
```

If you were to visit example.com/static the request would then be passed onto
static.otherdomain.com/static and static.otherdomain.com would receive the
request with the Host header being static.otherdomain.com.

Note: The per ingress annotation overides whatever the global value is set to. So you
could set `disablePassHostHeaders` to true in your toml file and then enable passing
the host header per ingress if you wanted.
68 changes: 48 additions & 20 deletions provider/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
map[string]*types.Backend{},
map[string]*types.Frontend{},
}
PassHostHeader := provider.getPassHostHeader()
for _, i := range ingresses {
for _, r := range i.Spec.Rules {
if r.HTTP == nil {
Expand All @@ -124,6 +123,19 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
},
}
}

PassHostHeader := provider.getPassHostHeader()

passHostHeaderAnnotation := i.Annotations["traefik.frontend.passHostHeader"]
switch passHostHeaderAnnotation {
case "true":
PassHostHeader = true
case "false":
PassHostHeader = false
default:
log.Warnf("Unknown value of %s for traefik.frontend.passHostHeader, falling back to %s", passHostHeaderAnnotation, PassHostHeader)
}

if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
templateObjects.Frontends[r.Host+pa.Path] = &types.Frontend{
Backend: r.Host + pa.Path,
Expand Down Expand Up @@ -193,28 +205,44 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
if port.Port == 443 {
protocol = "https"
}
endpoints, exists, err := k8sClient.GetEndpoints(service.ObjectMeta.Namespace, service.ObjectMeta.Name)
if err != nil || !exists {
log.Errorf("Error retrieving endpoints %s/%s: %v", service.ObjectMeta.Namespace, service.ObjectMeta.Name, err)
continue
}
if len(endpoints.Subsets) == 0 {
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(int(port.Port)),
if service.Spec.Type == "ExternalName" {
url := protocol + "://" + service.Spec.ExternalName
name := url

templateObjects.Backends[r.Host+pa.Path].Servers[name] = types.Server{
URL: url,
Weight: 1,
}
} else {
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports))
name := url
if address.TargetRef != nil && address.TargetRef.Name != "" {
name = address.TargetRef.Name
}
templateObjects.Backends[r.Host+pa.Path].Servers[name] = types.Server{
URL: url,
Weight: 1,
endpoints, exists, err := k8sClient.GetEndpoints(service.ObjectMeta.Namespace, service.ObjectMeta.Name)
if err != nil {
log.Errorf("Error while retrieving endpoints from k8s API %s/%s: %v", service.ObjectMeta.Namespace, service.ObjectMeta.Name, err)
continue
}

if !exists {
log.Errorf("Service not found for %s/%s", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
continue
}

if len(endpoints.Subsets) == 0 {
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(int(port.Port)),
Weight: 1,
}
} else {
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports))
name := url
if address.TargetRef != nil && address.TargetRef.Name != "" {
name = address.TargetRef.Name
}
templateObjects.Backends[r.Host+pa.Path].Servers[name] = types.Server{
URL: url,
Weight: 1,
}
}
}
}
Expand Down