Search code examples
traefik

Traefik forwarding to a host and overriding IP


Foreword: I appreciate that this is not ideal, I'm in the process of migrating away from an old Apache-based server, and need to leave a few sites there for now.

I'd like to have Traefik forward certain requests to another server, consider the following config:

[file]

[frontends.attie]
backend = "attie"

[frontends.attie.routes._]
rule = "HostRegexp: {host:^(www\\.)?attie.co.uk}"

[backends.attie.servers._]
url = "http://attie.co.uk:80"

Externally, the DNS points attie.co.uk at this server. Traefik must then forward the request on to the old server using the Host: attie.co.uk header.

Unfortunately, the DNS points attie.co.uk to this server, and we end up in a loop (of course).

I've added entries to the container's /etc/hosts, but this doesn't work - we still end up in a loop (see the log in this gist), presumably because Traefik is doing name resolution itself and ignoring the hosts file.


I've tried using the customRequestHeaders to no avail - it appears in the config blob in the log, but doesn't work.

[frontends.attie.headers.customRequestHeaders]
Host = "attie.co.uk"

[backends.attie.servers._]
url = "http://10.42.0.4:80"

Note the warning on this page:

If the custom header name is the same as one header name of the request or response, it will be replaced.


Is there any way I could do one of these?

  • Configure the name resolution to consider /etc/hosts
  • Force the backend to connect to a host by giving an IP alongside the url
  • Provide a Host: header paired with an IP in the URL

I've had a fairly extensive look at the documentation, but may have missed something.


PS: I would have expected this question to be better suited to ServerFault or SuperUser, but they don't have the tag, and the documentation specifically mentions StackOverflow.


Solution

  • If you're using the official traefik image, this one is built from scratch. Most notably there is no /etc/nsswitch.conf file present. In this case the golang implemented the same fallback mechanism as glibc, which is to ignore /etc/hosts - which is exactly issue you are observing.

    The important line in the configuration is the one, that tells the resolver to first look into local /etc/hosts file and then fallback to DNS query:

     hosts: files dns
    

    The minimal nsswitch.conf file created by running this in the container would do:

    echo "hosts: files dns" > /etc/nsswitch.conf
    

    However considering a proper nsswitch.conf file is present on any normal host system, perhaps the simplest solution is to add an extra mount in your docker run. Here's the adapted example from traefik docker hub repo:

    docker run -d -p 8080:8080 -p 80:80 \
    -v $PWD/traefik.toml:/etc/traefik/traefik.toml \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /etc/nsswitch.conf:/etc/nsswitch.conf:ro \
    traefik
    

    I'm sure you can adapt this if you use docker compose or something else. After you do that the /etc/hosts file inside should work as one would expect. (which you probably modified by --add-host docker run flag)