How to make whoami example work with HTTPS and self-signed certificates?

Hi,

I have just found Traefik this week. I was referred to take a look at it by someone on the Docker Slack Channel. Frankly, I think it's pretty fascinating, and I really want to propose that we use it to terminate SSL for an upcoming docker based tomcat + websockets app deployment, where the websockets containers are orchestrated dynamically by the tomcat application. I thought that with Traefik, I could save the developers the complication of building SSL into their applications and containers. I have only looked at Traefik V2, thinking that it's best to start with the current version.

I have had good luck over the past couple of days setting up some reference examples with Traefik V2 using HTTP. Basically setting up Traefik to proxy to either my own container and/or the whoami container. From this reference example I am trying to get to using HTTPS instead of HTTP and failing miserably. I have looked over these links, but am not able to successfully force the use of HTTPS with the whoami container:

https://docs.traefik.io/https/tls/#user-defined
https://docs.traefik.io/migration/v1-to-v2/#tls-configuration-is-now-dynamic-per-router
https://docs.traefik.io/migration/v1-to-v2/#http-to-https-redirection-is-now-configured-on-routers


Can someone tell me how where I'm going wrong in my configs below? I need SSL to run on port 8443, as there is already software binding to port 443 on my test machine. Using the curl commands from the examples, I should be able to do this:

$ curl -k -H Host:whoami.docker.local -L http://127.0.0.1/

and receive the whoami output after redirection to https://127.0.0.1:8443/, but what I get is:

curl: (6) Could not resolve host: whoami.docker.local

Here is my config details:

docker-compose.yml:

version: '3'

services:
  reverse-proxy:
    image: traefik:latest
    command:
      - --api.insecure=true
      - --providers.docker
      - --entrypoints.web.address=:80
      - --entrypoints.web-secured.address=:8443
      - --entrypoints.traefik.address=:8080
      - --providers.file.directory=/config
      - --log.level=debug
    labels:
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.port=8443
      - traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.redirs.entrypoints=web
      - traefik.http.routers.redirs.middlewares=redirect-to-https
    ports:
      - "8080:8080"
      - "8443:443"
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config:/config:ro
      - ./certs/:/certs:ro

  whoami:
    image: containous/whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.rule=Host(`whoami.docker.local`)
      - traefik.http.routers.whoami.entrypoints=web-secured
      - traefik.http.routers.whoami.tls=true

To create the certs I have done the following:

$ mkdir certs
$ openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/privkey.key -x509 -days 365 -out certs/cert.crt

...using whoami.docker.local as the certificate's CN. I've tried other names to no success.

To create the config dir:

$ mkdir config
$ cat << EOF > config/certs.toml
[[tls.certificates]]
  certFile = "/certs/cert.crt"
  keyFile = "/certs/privkey.key"

[tls.stores]
  [tls.stores.default]
    [tls.stores.default.defaultCertificate]
      certFile = "/certs/cert.crt"
      keyFile = "/certs/privkey.key"
EOF

Thanks for taking a look, I welcome any help or suggestions.

curl is telling you that it cannot resolve host whoami.docker.local. You need to fix that. One way to do this would be to put it in your hosts file.

Alternatively do not allow curl to do the redirect, and query the url it would redirect to directly. This way you can provide the correct host header as per your example, so curl would not need to resolve the host itself.

Thanks for your reply @zespri. I agree with your comment about curl not being able to resolve whoami.docker.local. After posting I had added whoami.docker.local to the 127.0.0.1 entry in /etc/hosts on my laptop. This didn't work as desired but I don't recall the exact result.

Once nice thing about all of this is that it's pretty simple to start over and try again. So I spent time yesterday doing exactly that, and took perhaps a more incremental approach. I've published this effort on github: https://github.com/salderma/traefik-concept, the entire docker-compose.yml file is there, but I've put the relevant parts below:

  1. just HTTP over port 80 to the whoami container. :white_check_mark:
  2. switch external port to 9080, but keep Traefik's internal port as 80. :white_check_mark:
services:
  reverse-proxy:
    image: traefik:latest
    command:
      - --entrypoints.web.address=:80 
    ports:
      - 9080:80 
  1. put the whoami container behind /whoami url path. :white_check_mark:
  whoami:
    image: containous/whoami
    container_name: whoami.docker.local
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=web,websecure
      - traefik.http.routers.whoami.rule=HostRegexp(`whoami.docker.local`)"
      - traefik.http.routers.whoami.rule=Path(`/whoami`)
      - traefik.http.middlewares.whoami.stripprefix.prefixes=/whoami
  1. added HTTPS - file provider, certs volume, config volume, websecure entrypoint for 443, port map 9443:443. :white_check_mark:
services:
  reverse-proxy:
    image: traefik:latest
    command:
        # setup https entrypoint on port 9443
      - --entrypoints.websecure.address=:443
    ports:
        # https port - backends to 443
      - "9443:443"

This part is resulting in a 404 page not found message with the following commands:

$ curl -k -H Host:whoami.docker.local  https://127.0.0.1:9443/
404 page not found
$ curl -k -H Host:whoami.docker.local  https://whoami.docker.local:9443/
404 page not found

I have run curl -v also and found that Traefik is serving up my custom self-signed certificate, so that's good. Past that, I'm not sure what the deal is. I've got logging turned on, but there's nothing relevant to the 404 result in the logs.

404 coming from traefik, means the request did not match any rules. In your particualr case is that you are sepcifying a HostRefexp rule, then ovewriting it with Path rule, and your query does not indeed match the path. Read here https://docs.traefik.io/routing/routers/#rule on how to write the rules correctly.

:slight_smile: I think I discovered that rule issue... I haven't commited a change to the git repo b/c I've been experimenting a bit. I've tried the following (note I have a hosts entry for whoami.docker.local):

    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.rule=Host(`whoami.docker.local`) && Path(`/whoami`)
      - traefik.http.middlewares.whoami.stripprefix.prefixes=/whoami

which produced:

$ curl -k -H Host:whoami.docker.local https://whoami.docker.local:9443/whoami/
404 page not found

Also tried:

    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.rule=Host(`whoami.docker.local`)
        # && Path(`/whoami`)
      - traefik.http.middlewares.whoami.stripprefix.prefixes=/whoami

which produced:

$ curl -k -H Host:whoami.docker.local https://whoami.docker.local:9443/whoami/
404 page not found

Also tried:

    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=websecure
        #- traefik.http.routers.whoami.rule=Host(`whoami.docker.local`)
        # && Path(`/whoami`)
      - traefik.http.routers.whoami.rule=PathPrefix(`/whoami`)
      - traefik.http.middlewares.whoami.stripprefix.prefixes=/whoami

which produced:

$ curl -k -H Host:whoami.docker.local https://whoami.docker.local:9443/whoami/
404 page not found

So https request go to TLS routers.In that last configurations you posted I don't see you creating one. Read here https://docs.traefik.io/routing/routers/#tls

Ok, so I guess that's where I'm getting hung up. The way I've been understanding this must be wrong. I thought the tls router was for tls inside/between traefik and an app container? I mean I just want traefik to terminate ssl and not have any ssl in the app containers.

So you're saying I need to add another router for the whoami container using tls? The link you provide doesn't have a docker label configuration for this. I will see if I can translate.

1 Like

And this produces success:

    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami-http.entrypoints=web
      - traefik.http.routers.whoami-http.rule=PathPrefix(`/whoami`)
      - traefik.http.middlewares.whoami-http.stripprefix.prefixes=/whoami
      - traefik.http.routers.whoami-https.entrypoints=websecure
      - traefik.http.routers.whoami-https.tls=true
      - traefik.http.routers.whoami-https.rule=PathPrefix(`/whoami`)
      - traefik.http.middlewares.whoami-https.stripprefix.prefixes=/whoami

Next I will move on to redirection http -> https, which I think there is good examples of.

Please feel free to use any of my reference examples (variations by commit) within my git repo for this exercise.

I have added some doc and setup the compose file such that the containers run as the executor's UID and therefore root won't own any files after playing with it.

Thanks a bunch to @zespri for the help and guidance.

TLS router means a router that terminates TLS. You do not need another router if TLS is all you doing, if you want to process requests both via TLS and non-TLS channel, yes, you will need to separate routers for this. Glad you got that sorted!

@zespri Are you on the Traefik Team?

The DOC at https://docs.traefik.io/migration/v1-to-v2/#http-to-https-redirection-is-now-configured-on-routers shows this slightly confusing piece:

traefik.http.routers.web.middlewares=redirect@file (full context below).

That seems not to work, is it a documentation mistake? I ended up following the global redirect path that @ldez posted in another post here instead of this doc.

labels:
- traefik.http.routers.web.rule=Host(`foo.com`)
- traefik.http.routers.web.entrypoints=web
- traefik.http.routers.web.middlewares=redirect@file
- traefik.http.routers.web-secured.rule=Host(`foo.com`)
- traefik.http.routers.web-secured.entrypoints=web-secure
- traefik.http.routers.web-secured.tls=true

@salderma I am not on the Traefik Team, just a regular guy from internet :wink:

You want to read https://docs.traefik.io/middlewares/overview/#provider-namespace to understand the the @ syntax.

When you say "it does not work" it is not saying a lot. What does not work? Are there any errors in the log? Does configuration dumped in DEBUG log match your expected configuration?

Looking in crystal ball, may be you did not define your redirect middleware in your file provider?

Well, yes, perhaps that's exactly what I'm poking at - It's documentation, if one is trying to learn how something should work by reading the documentation and attempting to implement the examples therein, one might expect the examples to have some context, or linkage to a fuller context if more is required to make it function. Ultimately, the docs should probably have some complete reference examples start to finish, that actually work in a scratch environment...be that a Vagrant box running docker-ce or someone's laptop. e.g.

  • docker-compose method - copy this compose file and type docker-compose up -d, and go.
  • toml/yaml file method - copy this file and go.
  • etc.

I mean I guess that goes back to my OP, in trying to get a simple whoami container working with a self-signed SSL Cert, it shouldn't have taken me 4 days. :man_shrugging:

This has been discussed before, I believe. The fundamental problem is that traefik is very versatile and flexible, which means if one context is provided for a setting a hundred others will not and it is not practically possible to have all the combinations documented, there are too many.

You can find a few most common scenarios documented under User Guides on the left at https://docs.traefik.io/.

I find the documentation pretty comprehensive, most questions that I'm seeing is because the asker did not read this or that part of the documentation but it is there.

Having said that traefik team always trying to improve the documentation, for example take a look at the currently open PRs https://github.com/containous/traefik/pulls?q=is%3Apr+is%3Aopen+label%3Aarea%2Fdocumentation