Reverse proxy to external host

Hello everyone,

I am pretty new to traefik in general but already love its ease of use and potential.
Now I am running against kind of a wall right now. I want to reverse proxy all traffic to one a subfolder traefik has control of to an external host. Like so:

https://dev.mytraefikhost.de/myexthost/www --> https://www.myexthost.de

Is that possible and if yes, how to configure it? I am right now using docker-compose, traefik.yaml and trafik-dynamic.yaml for config.

Thank you guys a lot for your awesome help on the forums already. Was a blast to learn treafik from the ground up with your help!
Kind regards
Lukas

2 Likes

Hello,

I created 2 examples:

  • foo_redirect_simple
  • foo_redirect_pattern
$ curl -k -L https://dev.mytraefikhost.localhost/myexthost/www
Hostname: 2b514ffd3ae9
IP: 127.0.0.1
IP: 172.20.0.2
RemoteAddr: 172.20.0.3:53686
GET / HTTP/1.1
Host: www.myexthost.localhost
User-Agent: curl/7.67.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.20.0.1
X-Forwarded-Host: www.myexthost.localhost
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: a78ae49ed1cb
X-Real-Ip: 172.20.0.1
version: "3.7"

services:
  traefik:
    image: traefik:v2.1.1
    command: >
      --log.level=INFO
      --api
      --providers.docker.exposedbydefault=false
      --entrypoints.web.address=:80
      --entrypoints.websecure.address=:443
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      # enable this service
      traefik.enable: true

      # dashboard
      traefik.http.routers.api.rule: Host(`traefik.localhost`)
      traefik.http.routers.api.entrypoints: web
      traefik.http.routers.api.service: api@internal

      # foo
      traefik.http.routers.foo.rule: Host(`dev.mytraefikhost.localhost`)
      traefik.http.routers.foo.entrypoints: websecure
      traefik.http.routers.foo.tls: true
      traefik.http.routers.foo.middlewares: foo_redirect_simple

      # foo redirect middleware (simple)
      traefik.http.middlewares.foo_redirect_simple.redirectregex.regex: https:\/\/dev\.mytraefikhost\.localhost\/myexthost\/www
      traefik.http.middlewares.foo_redirect_simple.redirectregex.replacement: https://www.myexthost.localhost

      # foo redirect middleware (pattern)
      # traefik.http.middlewares.foo_redirect_pattern.redirectregex.regex: https:\/\/dev\.mytraefikhost\.localhost\/([^/]+)\/([^/]+)
      # traefik.http.middlewares.foo_redirect_pattern.redirectregex.replacement: https://$$2.$$1.localhost

  whoami:
    image: containous/whoami
    labels:
      traefik.enable: true

      traefik.http.routers.whoami.rule: Host(`www.myexthost.localhost`)
      traefik.http.routers.whoami.entrypoints: websecure
      traefik.http.routers.whoami.tls: true

Hi Idez,

huge thanks for your fast reply. I just tested it and it does work. But sadly not a intended. I hoped to be able and serve the website hosted on https://www.myexthost.de through https://dev.mytraefikhost.de/myexthost/www without redirecting the user to the other host. He should still be thinking to be on https://dev.mytraefikhost.de/myexthost/www, not obviously knowing that he has been reverse proxied to another host. Is that possible? (i just want the frontend to still show the dev.* url but the backend to be different, if that makes sense)

Thanks again for your fast support on here!
Kind regards
Lukas

version: "3.7"

services:
  traefik:
    image: traefik:v2.1.1
    command: >
      --log.level=INFO
      --api
      --providers.docker.exposedbydefault=false
      --providers.file.directory=/dynamic/
      --entrypoints.web.address=:80
      --entrypoints.websecure.address=:443
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic/:/dynamic/
    labels:
      # enable this service
      traefik.enable: true

      # dashboard
      traefik.http.routers.api.rule: Host(`traefik.localhost`)
      traefik.http.routers.api.entrypoints: web
      traefik.http.routers.api.service: api@internal

./dynamic/mytraefikhost.yml

http:
  routers:
    foobar:
      entryPoints:
      - websecure
      middlewares:
      - strip
      service: myexthost
      rule: Host(`dev.mytraefikhost.de`) && Path(`/myexthost/www`)
      tls: {}

  myexthost:
    foobar:
      loadBalancer:
        servers:
        - url: https://www.myexthost.de
 
  middlewares:
    strip:
      stripPrefix:
        prefixes:
        - /myexthost/www

Hey Idez,

huge thanks again for your fast response. We are slowly but surely getting there. I've just tested it on a clean maschine. Reverse proxy without a redirect that is visible to the user works. The only problem that prevails is that the host I am accessing seems to gets a request on the wrong url. So I get an Error 404 on the hosts end. I've just tested it with google.com in my example, as you can see here:

./docker-compose.yaml

version: "3.7"

services:
  traefik:
    image: traefik:v2.1.1
    command: >
      --log.level=INFO
      --api
      --providers.docker.exposedbydefault=false
      --providers.file.directory=/dynamic/
      --entrypoints.web.address=:80
      --entrypoints.websecure.address=:443
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic/:/dynamic/
    labels:
      # enable this service
      traefik.enable: true

      # dashboard
      traefik.http.routers.api.rule: Host(`traefik.localhost`)
      traefik.http.routers.api.entrypoints: web
      traefik.http.routers.api.service: api@internal

./dynamic/mytraefikhost.yml

http:
  routers:
    foobar:
      entryPoints:
      - websecure
      middlewares:
      - strip
      service: myexthost
      rule: Host(`dev.mytraefikhost.localhost`) && Path(`/myexthost/www`)
      tls: {}

  services:
    myexthost:
      loadBalancer:
        servers:
        - url: https://www.google.com
 
  middlewares:
    strip:
      stripPrefix:
        prefixes:
        - /myexthost/www

Site returns:

$ curl -k -L https://dev.mytraefikhost.localhost/myexthost/www
<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 404 (Not Found)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>404.</b> <ins>That’s an error.</ins>
  <p>The requested URL <code>/</code> was not found on this server.  <ins>That’s all we know.</ins>

It would be so dope if there was a way to fix it. :smiley:
Kind regards
Lukas

@lukas-runge Did you ever manage to get this to work? I have the same problem.

If using Docker, is it required to use a dynamic file (YAML) ./dynamic/mytraefikhost.yml vs labels in the Docker Compose?

        - traefik.http.routers.syndrive.rule=Host(`drive.mylab.com`)
        - traefik.http.routers.syndrive.service=syndrive
        - traefik.http.routers.syndrive.tls.passthrough=true
        - traefik.http.services.syndrive.loadbalancer.servers.url=https://drive.mylab.com
        - traefik.http.services.syndrive.loadbalancer.passhostheader=true

I have these labels on my Traefik container and it isn't working. If it's required to configure via File, that is probably why.

no, if you are using Docker it's recommended to use labels as a dynamic configuration.

Using a file as a dynamic configuration is mainly for "non-cloud" infra (VM, bare metal, etc.)

Be aware that you need a static and dynamic configuration for Traefik.

The static configuration contains entrypoints, providers (like Docker or File), static TLS and certificatesResolvers. It can be in a file or via command in Docker Compose.

The dynamic configuration (Router, Service) comes from a provider (Docker, File) or from container labels.

Example docker-compose.yml:

version: '3.9'

services:
  traefik:
    image: traefik:v2.9
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
    networks:
      - proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-certificates:/certificates
    command:
      --providers.docker=true
      --providers.docker.exposedByDefault=false
      --entryPoints.web.address=:80
      --entryPoints.web.http.redirections.entryPoint.to=websecure
      --entryPoints.web.http.redirections.entryPoint.scheme=https
      --entryPoints.websecure.address=:443
      --entryPoints.websecure.http.tls=true
      --api.debug=true
      --api.dashboard=true
      --log.level=DEBUG
      --accesslog=true
      --certificatesResolvers.myresolver.acme.email=mail@example.com
      --certificatesResolvers.myresolver.acme.storage=/certificates/acme.json
      --certificatesResolvers.myresolver.acme.httpchallenge.entrypoint=web
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.tls.certresolver=myresolver
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=myauth
      - 'traefik.http.middlewares.myauth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/'

  whoami:
    image: traefik/whoami:v1.8
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
      - traefik.http.routers.whoami.tls.certresolver=myresolver
      - traefik.http.services.whoami.loadbalancer.server.port=80

networks:
  proxy:
    name: proxy
    external: true

volumes:
  traefik-certificates:

Cool. That's what I've been doing for all of my other services that are running behind Traefik in Docker with no issues. Looks like I need to turn up debugging and find out why it's not working for me on routing to my Synology NAS. Thanks!

I have a working static configuration with my entrypoints, etc. and use labels in Docker compose for dynamic. In this case, just not succeeding in proxying traffic external to Docker.

Go into your traefik container and check if you can connect to the target:

docker exec -it <traefik-container> sh
wget 'https://drive.mylab.com'

According to docs the tls.passthrough is only available on TCP entrypoints.

It's reachable from the container.

/ # wget 'https://drive.mylab.com'
Connecting to drive.mylab.com (192.168.7.197:443)
saving to 'index.html'
index.html           100% |*************************************************************|  6330  0:00:00 ETA
'index.html' saved

Good catch, still doesn't change my outcome. I wonder if it's header related?

Enable Traefik dashboard, debug log and access log to see whats happening.

So, I finally caught this statement, which is exactly my situation.

So, I set up the dynamic configuration but I cannot get Traefik to not terminate the TLS connection. It keeps using the Treafik default certificate and I don't want it to. I just want Traefik to proxy the connection and allow the terminating device (in my case, a Synology NAS) to use its TLS certificate. Any ideas?

tcp:
  routers:
    syndrive:
      service: syndrive
      rule: HostSNI(`drive.mylab.com`)
      tls:
      	passthrough: true

  services:
    syndrive:
      loadBalancer:
        servers:
        - url: https://drive.mylab.com

If you want to use plain TCP, just pass TCP/IP packets, you need a (separate) TCP entrypoint.

The entrypoint I am using is TCP

entryPoints:
  websecure:
    address: :443

Sorry, you are right, plain TCP is configured in router.

Can someone share a wotking example that can work on digital ocean vps container.

I woul like to access an external service but never got it to work due to https issues .

Maybe you try to more precisely describe your issue, share some Traefik debug logs.

Is the issue with ingress (incoming) TLS/SSL or forwarding to a TLS/SSL target service?