Error 400 - bad request

Hello,
I'm new to traefik and was trying to use it to add https redirection to a service that I'm running on a VM.
I have no trouble accessing the service using https://VM_IP_address:8443 or https://vm_name:8443 but I want to add htpp -> https redirection and IP -> vm's name (=domain name) redirection. When I'm accessing http://vm_name I am redirected toward https://vm_name as I want. However, when I am trying to access this URL (https://vm_name) as well as http://VM_IP_address:8443 http://vm_name:8443 I am getting an "Error 400 - bad request - The plain HTTP request was sent to HTTPS port".

Ideally I would like all those requests to redirect me toward https://vm_name(:8443).

Docker-compose.yml

version: '3'
services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    ports:
      - 80:80
      - 443:443
      - 8080:808
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml
      - ./data/cert:/etc/cert/
      - ./data/log:/var/log/traefik
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      - --serverstransport.insecureSkipVerify=true
      - --providers.file.filename=/etc/traefik/dynamic_conf.yml
      - --providers.file.watch=true
      - --log=true
      - --log.filePath=/var/log/traefik/traefik.log
      - --log.level=DEBUG
      - --log.maxsize=50
      - --log.maxbackups=7
      - --log.maxage=180
      - --log.compress=true
      - --accessLog=true
      - --accessLog.filePath=/var/log/traefik/access.log
    restart: always
    networks:
      - pwndoc-network

  mongodb:
    image: mongo:4.2.15
    container_name: mongo-pwndoc-ng
    volumes:
      - ./data/mongo-db:/data/db
    restart: always
    ports:
      - 127.0.0.1:27017:27017
    environment:
      - MONGO_DB:pwndoc
    networks:
      - pwndoc-network

  backend:
    build: ./backend
    image: pwndoc-ng/pwndoc-ng:backend
    container_name: pwndoc-ng-backend
    volumes:
      - ./backend/report-templates:/app/report-templates
      - ./backend/src/config:/app/src/config
    depends_on:
      - mongodb
    environment:
      - COLLAB_WEBSOCKET_PORT=8440
    restart: always
    ports:
      - 4242:4242
    links:
      - mongodb
    networks:
      - pwndoc-network

  frontend:
    build: ./frontend
    image: pwndoc-ng/pwndoc-ng:frontend
    container_name: pwndoc-ng-frontend
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.routers.pwndoc-ng-frontend-http.rule=Host(`vm_name`)
      - traefik.http.routers.pwndoc-ng-frontend-http.entrypoints=web
      - traefik.http.routers.pwndoc-ng-frontend-http.middlewares=redirect
      - traefik.http.middlewares.redirect.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect.redirectscheme.permanent=true
      - traefik.http.routers.pwndoc-ng-frontend-https.rule=Host(`vm_name`)
      - traefik.http.routers.pwndoc-ng-frontend-https.entrypoints=web-secure
      - traefik.http.routers.pwndoc-ng-frontend-https.tls=true
      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.port=8443
#      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.scheme=https
#      - traefik.http.routers.pwndoc-ng-frontend.loadBalancer.passhostheader=true
    ports:
      - 8443:8443
    depends_on:
      - backend
    networks:
      - pwndoc-network

networks:
  pwndoc-network:
      driver: bridge

Dynamic_conf.yml

http:
  middlewares:
    ip-redirectregex:
      redirectRegex:
        regex: "^https://vm_ip/(.*)"
        replacement: "https://vm_name/$(1)"

tls:
  stores:
    default:
      defaultCertificate:
        certFile: /etc/cert/server.cert
        keyFile: /etc/cert/server.key
  options:
    default:
      minVersion: VersionTLS12
    mintls13:
      minVersion: VersionTLS13

Thanks in advance!

Well, you can’t use http protocol with https port/entrypoint, in your case 8443, which expects a TLS/SSL connection. Usually http uses port 80 (or 8080).

Also https://<IP> redirection does not work, as TLS certs are linked to domain names, so you will only see a cert error in your browser when accessing.

So it's impossible to redirect from the http to the https, and it's also impossible to redirect from https://ip: to https://domain_name?
The first one is not a big problem but it would have been great to be able to do the second redirection...

But thanks for the answer! I not longer need to spend any more time on it.

You can redirect from http IP to http domain and then from http to https.

And could you please tell me how I should modify my files to do so?

Create a router with Host(<IP>) which uses redirect middleware to domain (doc).
Place http-to-https redirect on router with domain name (doc), not on http entrypoint.

Thank you very much for your response, I will try to do so!

I tried to modify my files as you told me but it is still not working, could you tell me what mistake I made?

Docker-compose.yml:

version: '3'
services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    ports:
      - 80:80
      - 443:443
      - 8080:808
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml
      - ./data/cert:/etc/cert/
      - ./data/log:/var/log/traefik
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      - --serverstransport.insecureSkipVerify=true
      - --providers.file.filename=/etc/traefik/dynamic_conf.yml
      - --providers.file.watch=true
      - --log=true
      - --log.filePath=/var/log/traefik/traefik.log
      - --log.level=DEBUG
      - --log.maxsize=50
      - --log.maxbackups=7
      - --log.maxage=180
      - --log.compress=true
      - --accessLog=true
      - --accessLog.filePath=/var/log/traefik/access.log
    restart: always
    networks:
      - pwndoc-network

  mongodb:
    image: mongo:4.2.15
    container_name: mongo-pwndoc-ng
    volumes:
      - ./data/mongo-db:/data/db
    restart: always
    ports:
      - 127.0.0.1:27017:27017
    environment:
      - MONGO_DB:pwndoc
    networks:
      - pwndoc-network

  backend:
    build: ./backend
    image: pwndoc-ng/pwndoc-ng:backend
    container_name: pwndoc-ng-backend
    volumes:
      - ./backend/report-templates:/app/report-templates
      - ./backend/src/config:/app/src/config
    depends_on:
      - mongodb
    environment:
      - COLLAB_WEBSOCKET_PORT=8440
    restart: always
    ports:
      - 4242:4242
    links:
      - mongodb
    networks:
      - pwndoc-network

  frontend:
    build: ./frontend
    image: pwndoc-ng/pwndoc-ng:frontend
    container_name: pwndoc-ng-frontend
    restart: always
    labels:
      - traefik.enable=true
# Router to redirect from IP to domain
      - traefik.http.routers.pwndoc-ng-frontend-http.rule=Host(`ip`)
      - traefik.http.routers.pwndoc-ng-frontend-http.middlewares=redirect
      - traefik.http.middlewares.redirect.redirectregex.regex=^http://ip:8443/(.*)
      - traefik.http.middlewares.redirect.redirectregex.replacement=http://domain_name:8443/$${1}
      - traefik.http.middlewares.redirect.redirectscheme.permanent=true
# Router to redirect from http to https
      - traefik.http.routers.pwndoc-ng-frontend-https.rule=Host(`domain_name`)
      - traefik.http.routers.pwndoc-ng-frontend-https.middlewares=redirect-secure
      - traefik.http.middlewares.redirect-secure.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect-secure.redirectscheme.permanent=true
      - traefik.http.routers.pwndoc-ng-frontend-https.tls=true
      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.port=8443
#      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.scheme=https
#      - traefik.http.routers.pwndoc-ng-frontend.loadBalancer.passhostheader=true
    ports:
      - 8443:8443
    depends_on:
      - backend
    networks:
      - pwndoc-network

networks:
  pwndoc-network:
      driver: bridge

dynamic_conf.yml:

tls:
  stores:
    default:
      defaultCertificate:
        certFile: /etc/cert/server.cert
        keyFile: /etc/cert/server.key
  options:
    default:
      minVersion: VersionTLS12
    mintls13:
      minVersion: VersionTLS13

Whats not working?

curl -v http://<ip> should respond with a redirect to http://domain.

curl -v http://<domain> should respond with a redirect to https://domain.

https://<ip> will always result in a TLS cert error.

Don't expose ports of services other than Traefik, or potential security middlewares can be circumvented.

I commented the definition of the ports for the backend and the frontend in the dockerfile, then I stopped and removed the containers and remade them but:
curl -v http://ip -> 404 page not found
curl -v http://domain -> 404 page not found
curl -v https://ip -> certificate error (normal) and 404 page not found
curl -v https://domain -> 400 plain HTTP request sent to HTTPS port

So without defining the ports for the frontend and backend, nothing works anymore.

Enable Traefik access log in JSON format (doc), to see if error comes from the target service or from Traefik (OriginStatus / DownstreamStatus).

I got:
DownstreamStatus:404
OriginStatus:0

So Traefik has no matching router rule.

Have you enabled Traefik dashboard to check?

I tried to enable it in the beginning but when I tried to access http://vm_ip:8080/dashboard/ I got a "couldn't connect to server".

The part of docker-compose.yml for traefik:

version: '3'
services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    ports:
      - 80:80
      - 443:443
      - 8080:808
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml
      - ./data/cert:/etc/cert/
      - ./data/log:/var/log/traefik
    command:
      - --api.dashboard=true
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      - --serverstransport.insecureSkipVerify=true
      - --providers.file.filename=/etc/traefik/dynamic_conf.yml
      - --providers.file.watch=true
      - --log=true
      - --log.filePath=/var/log/traefik/traefik.log
      - --log.level=DEBUG
      - --log.maxsize=50
      - --log.maxbackups=7
      - --log.maxage=180
      - --log.compress=true
      - --accesslog=true
      - --accesslog.filePath=/var/log/traefik/access.log
      - --accesslog.addinternals
      - --accesslog.format=json
    restart: always
    networks:
      - pwndoc-network

Find the typo :wink:

Aww sorry for the dumb mistake, I missed it so many times...

So now I have access to the dashboard and I have the same error on the router and the middlewares for the Host(IP): "cannot create middleware: multi-types middleware not supported, consider declaring two different pieces of middleware instead"

Remove

You try to create a (incomplete) second middleware with the same name "redirect".

So now I no longer have any error on the dashboard and I have the redirection from http://ip toward http://domain. However the http://domain page and the https://ip pages are returning me a 404 page not found error. The https://domain page is returning me a " 400 Bad Request
The plain HTTP request was sent to HTTPS port"

So I have a domain redirection but the https redirection is not working and I can't access the service (only accessible through port 8443 when I define the ports for all the services in the docker-compose).

First router (working fine)

Second router (not working)

You are proxying requests to a https port

Maybe it helps to set scheme to https (doc):

traefik.http.services.<service_name>.loadbalancer.server.port
traefik.http.services.<service_name>.loadbalancer.server.scheme

If the target service is creating a custom TLS cert, then you probably need to add insecureSkipVerify (static doc or dynamic doc).

1 Like

Thank you very much!

Now http://ip and http://domain are redirected toward https://domain with the service running perfectly.
There's just the https://ip that still gives out a 404 page not found. Is it possible to juste change the regex in the following part or do I have to create a new router?

 frontend:
    build: ./frontend
    image: pwndoc-ng/pwndoc-ng:frontend
    container_name: pwndoc-ng-frontend
    restart: always
    labels:
      - traefik.enable=true
# Router to redirect from IP to domain
      - traefik.http.routers.pwndoc-ng-frontend-http.rule=Host(`ip`)
      - traefik.http.routers.pwndoc-ng-frontend-http.middlewares=redirect
      - traefik.http.middlewares.redirect.redirectregex.regex=^http://ip/(.*)
      - traefik.http.middlewares.redirect.redirectregex.replacement=http://domain/$${1}
# Router to redirect from http domain to https domain
      - traefik.http.routers.pwndoc-ng-frontend-https.rule=Host(`domain`)
      - traefik.http.routers.pwndoc-ng-frontend-https.middlewares=redirect-secure
      - traefik.http.middlewares.redirect-secure.redirectscheme.scheme=https  
      - traefik.http.middlewares.redirect-secure.redirectscheme.permanent=true
      - traefik.http.routers.pwndoc-ng-frontend-https.tls=true
      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.port=8443   
      - traefik.http.services.pwndoc-ng-frontend.loadBalancer.server.scheme=https