Global http to https redirect in v2

After migrating to v2, i am trying to register i guess a middleware for an endpoint which will redirect all http requests to https.

Right now this topic is TODO in the migration guide, so i am not sure how to do it

I found https://docs.traefik.io/v2.0/middlewares/redirectscheme/ and i understand i can define this

[http.middlewares]
  [http.middlewares.httpsredirect.redirectScheme]
    scheme = "https"

And i am aware how i now would apply this to a specific router. But in my case, i want all requests on the endpoint http being redirect to https - not configuring every and each router ( which there are none ). There are no hints on https://docs.traefik.io/v2.0/routing/entrypoints/ either nor i found anything in the reference as a special parameter https://docs.traefik.io/v2.0/reference/static-configuration/file/

The best guess i could have come up is a catch-all router on a the specific http endpoiint, redirecting

[entryPoints]
  [entryPoints.http]
    address = 8080
  [entryPoints.https]
    address = 8443
    ..


[http.middlewares]
  [http.middlewares.httpsredirect.redirectScheme]
    scheme = "https"

[http.routers]
  [http.routers.redirecttohttps]
    entryPoints = ['http']
    middlewares = ['httpsredirect']
    rule = 'HostRegexp(`myhostvar`, `.*`)'
  • Is that what it should look like?
  • What is the most effective way to do a catchall rule?
  • Am i even allowed to define a router without a service?

Thanks

2 Likes

Have you been successful in setting this up?
I think your catchall rule looks right, but the docs say: " You must attach a service per router. Services are the target for the router." - however, a redirect from http to https in the documentation examples also has no service attached to the insecure router, so i am unsure.

My ultimate setup i am trying to achieve is a deployment via ansible, where i can have a boolean which indicates if traefik should act as an ssl terminator (with a catchall rule) and load balancer, or if it just should sit behind a different ssl terminator and just acts as a reverse proxy for the docker container (which includes no ssl at all).

The approach is right:

# traefik.toml
## static configuration

[entryPoints]
  [entryPoints.web]
    address = 80
  [entryPoints.websecure]
    address = 443

[providers.file]
  directory = "/dynamic/"
# /dynamic/redirect.toml
## dynamic configuration

[http.routers]
  [http.routers.redirecttohttps]
    entryPoints = ["web"]
    middlewares = ["httpsredirect"]
    rule = "HostRegexp(`{host:.+}`)"
    service = "noop"

[http.services]
  # noop service, the URL will be never called
  [http.services.noop.loadBalancer]
    [[http.services.noop.loadBalancer.servers]]
      url = "http://192.168.0.1"

[http.middlewares]
  [http.middlewares.httpsredirect.redirectScheme]
    scheme = "https"

with Docker:

version: "3.7"

services:

  traefik:
    image: traefik:v2.1.2
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
    ports:
      - 80:80
      - 443:443
    labels:
      traefik.http.routers.http-catchall.rule: hostregexp(`{host:.+}`)
      traefik.http.routers.http-catchall.entrypoints: web
      traefik.http.routers.http-catchall.middlewares: redirect-to-https@docker
      traefik.http.middlewares.redirect-to-https.redirectscheme.scheme: https
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

https://docs.traefik.io/migration/v1-to-v2/#http-to-https-redirection-is-now-configured-on-routers

4 Likes

I'm sorry, i still need some help. I was successful with setting up redirect without the catchall domain, but now i want to change this. I have the following config:

traefik.yaml

api:
  dashboard: true
  insecure: true

entryPoints:
  web:
    address: ":80"
  web-secure: 
    address: ":443"
  traefik:
    address: ":8082"

metrics:
  prometheus: {}

accessLog: {}

certificatesResolvers:
  le:
    acme:
      email: mail@simonszu.de
      storage: acme.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedbydefault: false

  file:
    directory: /conf.d
    watch: true

conf.d/redirectScheme.yaml

http:
  middlewares:
# Single redirect
    redirect:
      redirectScheme:
        scheme: https
# catchall Redirect
    httpsredirect:
      redirectScheme:
        scheme: https

conf.d/routers.yaml

http:
  routers:
    redirecttohttps:
      entrypoints: "web"
      middlewares: "httpsredirect"
      rule: "HostRegexp(`{host:.+}`)"
      service: noop

conf.d/services.yaml

http:
  services:
    # noop service, the URL will be never called
    noop:
      loadBalancer:
        servers:
          url: "http://192.168.0.1"

these are the labels i have added to a container i was hoping to get via catchall:

      traefik.http.routers.adguard-secured.rule: "Host(`adguard.{{ traefik_domain }}`)"
      traefik.http.routers.adguard-secured.entrypoints: "web-secure"
      traefik.http.routers.adguard-secured.service: "adguard"
      traefik.http.routers.adguard-secured.tls.certresolver: "le"
      traefik.http.services.adguard.loadbalancer.server.port: "3000" 

The current situation is: It is not redirecting. Calling adguard.mydomain just stays on HTTP, and gets a 404. Maybe someone can point me to the error i have.

The error in the logs: Cannot start the provider *file.Provider: yaml: unmarshal errors:\n line 5: cannot unmarshal !!strhttpsre...into []string" - is my middleware syntax wrong?

1 Like

It is wrong on the line 5, you are trying to pass a string starting with "httpsre" where an array of strings is expected. You need to fix this. (pass an array of one string in it instead)

2 Likes

OK, yeah, i managed to fix this while copying the dynamic file directory to a single file. I had not defined some items as a list where traefik expected it.

1 Like

I have the same problem here, just migrated to v2 on kubernetes. We have a global redirect to https as well. I have enabled the providers.kubernetescrd and providers.kubernetesIngress and a providers.file with the above configuration (Also tried as IngressRoute), the redirect to ssl works however only for IngressRoutes not for all Ingress. But we need this for classic Ingresses.

So how can I configure this to redirect http ingresses to https ingresses? (Don't want to hijack this thread, if more details needed I will open a new one.)

1 Like

I do not think that Kubernetes Ingress in traefik v2 can use middlewares. I think this is one of the reason they encourage people to move to IngressRoutes instead. It looks like if the redirect is a requirement for you, this is what you will have to do.

1 Like

I dont like where this is going. There needs to be portability and simplicity. IngressRoutes and Middlewares are useful but there must be no force to migrate to those. Ingresses are the right choice in many cases and simple stuff like https redirects should continue to work. I dont want to complicate things which IngressRoutes clearly do.

I'm sorry to hear you see it this way.

Let me tell you this - changing the kubernetes manifests for dozens of deployments from Ingress to Ingress route is no fun. I wish that there were an easier path. They do provide Traefik Migration Tool that easies the pain somewhat, but even then it is not ideal. So I totally hear you.

However, I also understand their reasoning. Breaking backward compatibility is a huge pain for the end users. In most places in the industry this is a big no-no. For example, in Microsoft world, products such as .Net framework and Powershell, carry tonnes of sub-optimally designed "features" that are left there just for backward compatibility reasons. It's not uncommon to ask developers to "finally fix" something because everyone agrees that currently it's not ideal, and hear back, "sorry we cannot, because we have to be backward compatible".

Putting on a developer hat, I tell you that this is as frustrating. I often see that my product would work better if I designed it a bit differently. Or a lot differently. And I often want to go back to drawing board and to re-implement things to make them, easier, better, more stream-lined. In a business enterprise context I rarely have this opporunity. Why? Because it's cost in-effective, there is already something that is working and it costs less to keep band-aiding it, rather than do a major rewrite.

Traefik team decided that they've learned enough from the v1, that it warrants such a re-design. They saw what worked and what did not. In particular the idea of router, middlewares and services, is something that became obvious only after a lot of feedback about the v1 model has been gathered. Also when the developers wanted to implement or fix something that was tricky because of v1 design shortcomings - that informed their descision to switch to new approach too.

They blogged about it, you probably can easily find those blog posts if you are interested in reading them.

In short, in order to switch to a new design, they had to make many things work differently in traefik 2. And this is what you are experiencing.

In particular, customer experience with Ingress was not that great, configuring things via labels were a pain, so they decided to improve that via CRDs, which is, in my opinion a very logical thing to do. You simply cannot model everything that Traefik has to offer within constraints of Kubernetes Ingress object. This means that focus of the development now shifted to CRDs, which is also undestandable, there is only so many things you can do and only so many hours in a day, so it clearly makes sense to invest in the better and more flexible system that in the older one, that is being phased out.

The good thing is, that there is no big pressure to switch from v1, at least not right now. You can plan and execute that migration at your own pace. They did acnowledge that the situation with http redirect needs to be improved, since a lot of people voiced that, they did not say what their solution is going to be but they are aware of the complaint and would like to make improvements.

3 Likes

Argh^^ My scenario does actually work. I just got it during writing my answer here (at the bottom).
I have enabled both the kubernetesIngress and kubernetescrd provider and have many classic Ingresses.
I did create a catch all IngressRoute with a https redirectScheme. My errors was to assume that priority 1 is the highest. Using priority 50 does actually work. My catchall IngressRoute with a dummy service (The need for a dummy service seems odd) works now and does a https redirect to all Ingresses.
With what priority are Ingresses handled? 1?

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  creationTimestamp: "2019-09-24T13:45:07Z"
  generation: 13
  name: https-redirect
  namespace: kube-system
  resourceVersion: "26129541"
  selfLink: /apis/traefik.containo.us/v1alpha1/namespaces/kube-system/ingressroutes/https-redirect
  uid: 18e85b17-70a7-4ec7-931d-43793f5fc168
spec:
  entryPoints:
  - http
  - https
  routes:
  - kind: Rule
    match: HostRegexp(`{host:.+}`)
    middlewares:
    - name: redirect-https
    priority: 50
    services:
    - name: dummy
      passHostHeader: true
      port: 8080


Don't get me wrong I completely understand the reason behind this move, its fine work.
I just don't like the fact that this is completely traefik specific and not portable. We have dozens of apps which rely on very simple Ingresses (/ => service) and that's it. The only thing they all have in common is a simple https redirect which seems not to work now. And now we should migrate to something traefik specific only because of the https redirect? Seems odd. I'd like to combine simplicity and complexity, meaning using simple ingresses and for more complex stuff IngressRoutes. I just don't see simple https redirects as "complex".

1 Like

From my experience ingerss on perm looks different from ingerss in cloud, and I suspect, althought I do not know for sure that they also may look different between azure, aws and gce, so it's already something that varies. So while I kind of see where you are coming from I feel like I'm missing some information.

What platform are you hosting kubernetes in? What type of ingress did you use before traefik that used to be compatible?

Well yeah probably Ingresses are not quite portable between cloud vendors. My case here belongs to a k8s on premise. We've been using traefik v1 from the beginning. But still, I like portability. Maybe someone can point out portability of Ingresses between cloud vendors here.

But since the combination of IngressRoutes and Ingresses actually works there is nothing to complain about. That's the combination of simplicity and complexity I was writing about. Only the v2 docs lack some information here and there. The thing with the priority should be documented better. As well the combination of Ingresses And IngressRoute, I had to spend some hours yesterday to figure the v2 stuff out.

It's a bit a hijack :wink:

This one don't catch the http in the catchall. It don't redirect http to https.

version: "3.3"

networks:
    traefik:
        external: true

services:

  traefik:
    image: "traefik:v2.0"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web-secure"
      #- "--certificatesresolvers.myhttpchallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.myhttpchallenge.acme.email=me@mail.com"
      - "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
    labels:
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - "traefik"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  inner-whoami:
    image: "containous/whoami"
    container_name: "inner-whoami"
    networks:
      - "traefik"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.inner-whoami.rule=Host(`sub.domaine.com`)"
      - "traefik.http.routers.inner-whoami.entrypoints=web"
      - "traefik.http.routers.inner-whoami-secured.rule=Host(`sub.domaine.com`)"
      - "traefik.http.routers.inner-whoami-secured.entrypoints=web-secure"
      - "traefik.http.routers.inner-whoami-secured.tls=true"
      - "traefik.http.routers.inner-whoami-secured.tls.certresolver=myhttpchallenge"

I tried to comment the lines of whoami related to the router for http but it just makes http going into a 404 error.

1 Like

@Thibd could you resolve your problem? I'm having the same problem and I'm using similar configs...
Could you share your solution if you found one?

Hi @Dan42

I tried to use labels for redirect on each service and I still had errors. After getting some help it was that the http challenge has to be done on port 80, so that, .

- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web-secure"

Has to be replaced by :

- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"

I did not re-test it with the above code. I will stick with on adding labels on each service because this is for a remote dev environement and it gives some flexibility. Tell me if if you had some success.

Regards

Hi @Thibd,
my cert configs were correct.
I played around, but in the end I only got it working with the provided (Post #3) dynamic .toml file configs.
Setting the redirect with labels didn't work out for me and I don't know why. The middleware and the router didn't show up in traefik dashboard. Adding the suggested noop service via labels didn't change anything.

same I am also stuck on this http to https redirect

Indeed it seems crazy I'm going to have to deploy a service in kubernetes that is never hit. As-is, when deploying the almost exact manifest you provided it yields Cannot create service: service not found traefik/dummy.

@raffis what did you use for a dummy service, something that takes almost no resources I presume? Advice welcome.

EDIT: I found a small test image I had around to test rolling updates - rosskevin/example-nginx:1.0