Traefik v2 host header injection remedy wrt port bypass

Using Traefik v2.7.x, KubernetesCRD (IngressRoute & Middlewares (Header variant)) and args in deployment, we have limited the host to few domains using "allowedhosts". During a vulnerability test we realized that while host (domains) are validated but the port field of the host can be bypassed and payload by a malicious user. How do we do check this part in Traefik?

Although there's a mention of address in documentation as host:port we don't actually understand how it could be implemented without any example.

Unneeded spaces to domains are written here to bypass 4 links in a post limit
For eg if I have few domains as:

a. example. com
b. example. com
c. example. com
d. example. com

We have something like:

apiVersion: traefik. containo. us/v1alpha1
kind: Middleware
metadata:
name: headers-test
namespace: non-default
spec:
headers:
allowedHosts:
- a. example. com
- b. example. com
- c. example. com
- d. example. com


My expectation is to allow only https over 443

https://a.example.com or a.example.com:443 is failing to load application service altogether.

FYI: we used the ingressroute as:

spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(a. example. com) && PathPrefix(/)
middlewares:
- name: headers-test
services:
- kind: Service
name: app1
namespace: non-default
port: 4321


If Host is tampered on client end with google.com:8443 the server(Traefik) is returning 302 response with Location: https://google.com:8443/ indicating Host header Injection.

Request assistance.

Hi Team , can we have response to above question, even in my case host tampering would redirect the url to tampered host address , this is case of host poisoning , kindly help how to avoid it

Hi @Wisw and @prateekdegaons1991 - Thanks for your interest in Traefik!

Could you share a full example of your config and the DEBUG logs of Traefik?

I thought I'd never see the light of this post. While I fixed the issue by myself, I really believe that this and such examples from community posts need to be directly available over Docs section of Traefik v2.x or for that matter v1.x. Failing which its more like magic of words rather than serving true purpose!

@prateekdegaons1991 Coming the issue of Host header Injection through Port, say 80 (usually the case), first we need to remediate it to be redirected using middleware (through CLI argument or CRD resource). Here we used k8s CRD middleware resource.

apiVersion: traefik. containo .us/v1alpha1
kind: Middleware
metadata:
  name: redirectscheme
  namespace: non-default
spec:
  redirectScheme:
    permanent: true
    scheme: https

This makes the web (port 80) based requests are automatically redirected to websecure (port 443) and that is just beginning. Because we also need to fix the domain part from host. So this calls for an IngressRoute CRD resources specifically for addressing web based requests and another for addressing websecure based requests (If you are in doubt, yes we can create as many diff IngressRoute resources as we need. Traefik will make a collected rules internally).

apiVersion: traefik. containo .us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard-web
  namespace: non-default
spec:
  entryPoints:
    - web
  routes:
    - kind: Rule
      match: Host(a. example .com) && PathPrefix(`/`)
      middlewares:
        - name: redirectscheme # Used the middleware created before to autoredirect
      priority: 0
      services:
        - kind: Service
          name: traefik
          namespace: non-default
          port: 443
apiVersion: traefik. containo .us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard-websecure
  namespace: non-default
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(a. example .com) && PathPrefix(`/`)
      middlewares:
        - name: secureheaders
      priority: 0
      services:
        - kind: Service
          name: component-svc
          namespace: non-default
          port: <component-svc-port>
  tls:
    options:
      name: non-default
      namespace: non-default
    secretName: certificates

Form above you have seen that we have introduced another middleware for handling headers securely. Below is the resource sample for quick ref.

apiVersion: traefik. containo .us/v1alpha1
kind: Middleware
metadata:
  name: secureheaders
  namespace: non-default
spec:
  headers:
    allowedHosts:
      - a. example .com
    browserXssFilter: true
    contentTypeNosniff: true
    forceSTSHeader: true
    frameDeny: true
    permissionsPolicy: fullscreen=(), geolocation=()
    referrerPolicy: no-referrer
    sslRedirect: true
    stsIncludeSubdomains: true
    stsPreload: true
    stsSeconds: 63072000

"allowedHosts" is the one which actually helps restrict the requests to be accepted from a specified domain, to the server (Traefik in this case).
Other headers are for securing from other client-side issues in general which makes the site more secure from usual vulnerabilities.
Ref: https:// github .com/unrolled/secure#available-options
This article helps us use different options available within Go language which Treafik tool is based on.

Please check the URL(s) formatted above with spaces purposefully to bypass 4 link only limit by this community portal for new users.

@svx with your expertise kindly lemme know if there's anything absurd or better way of implementing same.

Hi @Wisw thanks for your feedback!
Yes, there is certainly room for improving the docs!

Your config looks OK.

One thing, what you could do (depending if that suits you) is to use Redirection on the web entrypoint.

By doing so, you would not need the whole redirectscheme.

Thanks @svc.

We are already using redirection on web entrypoint. But somehow it not able to do the redirect the web(port 80) based requests. We actually used the below and treafik continuously restarted as in its throwing error as Traefik can't understand them/not found.

--entrypoints.web.http.redirections.entrypoint.permanent:true
--entrypoints.web.http.redirections.entrypoint.scheme:https
--entrypoints.web.http.redirections.entrypoint.to:websecure

Ref: Traefik CLI Flags Documentation - Traefik

Just tried again just in case and ended up with same error before the post just now.

Hi @Wisw , Thank you for the detailed explanation, And I followed exact same, but when I do curl imposing host header, i could still bale to route to imposed host. :frowning:
curl -i -s -k -X $'GET' $'http://my . domain. example. com' -H $'Host: google.com' -v
it takes me to google.com , moved permanently 307 setting location header with host www. google. com

what is your traefik configuration ?

For CLI flags you need to use =, not :, see example.

@bluepuma77 totally agree with you reg usage of equal to sign rather than colon. A typo from my end. But fact remains the same, it somehow didn't work for my case of redirection at entrypoint level.

@prateekdegaons1991 I could share the exact config. if you could be specific.

Apart form already shared resources, here's my CLI args in brief at K8s Traefik deployment resource:


      args:
        - '--providers.kubernetescrd.namespaces=non-default'
        - '--providers.kubernetescrd.allowCrossNamespace=false'
        - '--providers.kubernetescrd.allowexternalnameservices=false'
        - '--global.checknewversion=false'
        - '--global.sendanonymoususage=false'
        - '--entrypoints.metrics.address=:9100/tcp'
        - '--entrypoints.traefik.address=:9000/tcp'
        - '--entrypoints.web.address=:8000/tcp'
        - '--entrypoints.websecure.address=:8443/tcp'
        - '--api.dashboard=false'
        - '--ping=true'
        - '--metrics.prometheus=false'
        - '--metrics.prometheus.entrypoint=metrics'
        - '--providers.kubernetescrd=true'
        - '--providers.kubernetesingress=false'
        - '--log.level=ERROR'
        - '--providers.http.tls.cert=/ssl/tls.crt'
        - '--providers.http.tls.key=/ssl/tls.key'
        - '--entrypoints.web.http.redirections.entrypoint.permanent=true'
        - '--entrypoints.web.http.redirections.entrypoint.priority=42'
        - '--entrypoints.web.http.redirections.entrypoint.scheme=https'
        - '--entrypoints.web.http.redirections.entrypoint.to=websecure'
        - '--entrypoints.websecure.http.tls=true'

Some of above can be changed from time to time as needed.

@Wisw Thank you ! looks good ! but why it still would fall back to host header ! :frowning:
let me dig more !
Thanks again !