Cockpit behind traefik

Hi there,

I'm trying to access cockpit through traefik v2.3.2.

The whole setup is running on a VPS.

Traefik is running in docker-compose, using the traefik network.
I have multiple docker containers running flawlessly together with traefik.

Cockpit is running outside docker on localhost:9090
If i open the firewall to 9090, i can access cockpit with serverIP:9090

I would like to route cockpit through traefik to access via subdomain and handle https for me.

So my plan is to block 9090 with ufw and have traefik route to cockpit.

My problem is accessing a non-docker service on the same host.
My dynamic config gets picked up, and the router gets created, but the routing doesn't go through.
Depending on what i try i get errors 404, 502 or 504

  • Using network_mode: host prevents me from using the traefik network.
  • I cannot bind the traefik container on 9090 because the cockpit service is using that already

ufw config

     To       Action      From
     --       ------      ----
[ 1] 22      ALLOW IN    Anywhere                   # ssh
[ 2] 80      ALLOW IN    Anywhere                   # http
[ 3] 443     ALLOW IN    Anywhere                   # https

traefik docker-compose.yml

version: "3.3"

networks:
  traefik:
    external: true

services:
  traefik:
    image: traefik:v2.3.2
    restart: unless-stopped
    container_name: ${TRAEFIK_GUI_SUBDOMAIN}
    env_file:
      - .env
    ports:
      - 80:80
      - 443:443

      # Binding port 9090 does not work because cockpit is occupying that port
      #- 9090:9090

    # Trying network mode host stops me from using the traefik network
    # network_mode: host

    # using extra hosts also does not solve my problem
    # extra_hosts:
      #- "dockerhost:PUBLIC_SERVER_IP" # i would be surprised if that worked

    volumes:
      - ./data/acme.json:/acme.json # <== for certs
      - /var/run/docker.sock:/var/run/docker.sock # <== for docker access
      - ./config/traefik.yml:/etc/traefik/traefik.yml # static config file
      - ./config/rules:/etc/traefik/rules # dynamic config folder
      - ./config/users:/etc/traefik/users # users file
    networks:
      - traefik
    labels:
      # API / Dashboard
      - traefik.enable=true
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.rule=Host(`${TRAEFIK_GUI_SUBDOMAIN}.${DOMAIN}`)
      - traefik.http.routers.traefik.middlewares=simple_auth@file # enable auth

traefik.yml

log:
  level: ERROR #INFO #INFO #DEBUG, INFO, WARN, ERROR, FATAL, PANIC

api:
  insecure: false
  debug: false
  dashboard: true

global:
  sendAnonymousUsage: true

certificatesResolvers:
  cloudflare:
    acme:
      storage: /acme.json
      email: MY_EMAIL
      dnschallenge:
        provider: cloudflare
        resolvers: 1.1.1.1:53,1.0.0.1:53
        # caserver: https://acme-staging-v02.api.letsencrypt.org/directory # Staging Server

entryPoints:
  web:
    address: :80
  websecure:
    address: :443
    http:
      tls:
        certresolver: cloudflare

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

  file:
    directory: /etc/traefik/rules/
    watch: true

Inside /etc/traefik/rules i have:

auth.yml (works)

http:
  middlewares:
    simple_auth:
      basicauth:
        headerField: X-WebAuth-User
        usersFile: /etc/traefik/users

http-redirect.yml (works)

http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https
        permanent: true

  # dummy service for https redirect, the URL will be never called
  services:
    noop:
      loadBalancer:
        servers:
          - url: "http://192.168.0.1"

  routers:
    http-catchall:
      rule: "hostregexp(`{host:[a-z-.]+}`)"
      entrypoints:
        - web
      middlewares:
        - redirect-to-https
      service: noop

cockpit.yml (works, but the routing doesn't)

http:
  routers:
    cockpit:
      rule: "Host(`{{ env "COCKPIT_SUBDOMAIN" }}.{{ env "DOMAIN" }}`)"
      entryPoints:
        - websecure
      middlewares:
        - simple_auth
      service: cockpit
      # tls:
      #   certResolver: cloudflare

  services:
    cockpit:
      loadBalancer:
      # healthCheck shows always good, but that's a problem for another time
        healthCheck:
          interval: "5s"
          timeout: "3s"
        servers:
          # These are all the different urls i tried to route
          # - url: "http://host.docker.internal:9090"
          # - url: "http://172.17.0.1:9090"
          # - url: "http://localhost:9090"
          # - url: "http://SERVERS_PUBLIC_IP:9090" # no surprise here
          # - url: "https://dockerhost:9090" # together with extra_hosts => dockerhost:PUBLIC_SERVER_IP on the traefik container, also didn't expect this to work
          # - url: "http://host.docker.internal:9090"
          # - url: "http://172.17.0.1:9090"
          - url: "http://172.18.0.1:9090" # this is the ip from traefik network, but also no success

I also set the config for cockpit to allow reverse proxying

[WebService]
Origins = http://cockpit.mydomain.com ws://cockpit.mydomain.com https://cockpit.mydomain.com wss://cockpit.mydomain.com
ProtocolHeader = X-Forwarded-Proto
AllowUnencrypted=true

I'm pretty new to traefik and tried this now for a few days with no success. Can you give me any pointers on how i can get that working?

If you want to use host.docker.internal, you have to define this extra host in your traefik compose service:

extra_hosts:
  - "host.docker.internal:host-gateway"

PS: Available since Docker 20.10 (https://github.com/moby/moby/pull/40007)

First of all, for me cockpit is HTTPS, so try https://*:9090. Afterwards you also want to make sure Traefik trusts the CA from cockpit, which is probably randomly generated.

I managed to get it to work but the inline frame is showing up like this:

when I open inline frame in new window it is opening fine.

Hi @vitachaos ,

How did you do to make it works?

Thank you in advance.

To access external URLs with Traefik, you need to declare a service with loadbalancer.servers.url in a dynamic config file, which needs to be loaded with providers.file in static config. (Doc)

When using Docker, note that you can not use 127.0.0.1, as that would be the containers internal localhost, not the one of your host. In that case you would need to use the external IP of the host. host.docker.internal is usually just available on "Docker Desktop".

1 Like