Retrieve Client IP in Docker Bridge Mode

TL;DR is there a way to make docker forwarding the real client IP to Traefik while running it in bridge mode?

All my setups look like the following:

  • Traefik running in bridge mode sharing an internal network network with haproxy to access the docker socket in a secure way and an external network with all the apps
  • Traefik exposes ports 80 and 443 to the host
  • The apps join the external network and may have an internal one shared with the database, for instance
  • That way I achieve nice encapsulation of the services

The respective docker-compose config roughly looks like the following:

version: "3"

volumes:
  traefik-letsencrypt:

networks:
  service-socket-proxy:
    internal: true
  service-gateway:
    external: true

services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy:latest
    container_name: "socket-proxy"
    restart: unless-stopped
    privileged: true
    environment:
      CONTAINERS: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - service-socket-proxy.
  traefik:
    image: "traefik:v2.8"
    container_name: "traefik"
    depends_on:
      - socket-proxy
    restart: unless-stopped
    privileged: false
    volumes:
      - ./conf:/etc/traefik:ro
      - traefik-letsencrypt:/letsencrypt
    ports:
      - 80
      - 443
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=service-socket-proxy"
      # secure router
      - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_HOST}`, `www.${TRAEFIK_HOST}`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=LetsEncrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      # middlewares
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=..."
    networks:
      - service-socket-proxy
      - service-gateway

The issue is that Traefik receives client requests through a bridge network, thus the request is coming from a docker internal IP and the X-Real_IP is pointing to the respective gateway, 172.19.0.1 in my case.

Is there any way to make docker forwarding the real client IP without running Traefik in host mode? I see that this is a docker issue independent from Traefik but I feel like this is a very common issue people should run into when working with Traefik...

I set up a small test service that basically displays the client IP:

version: "3"

networks:
  service-gateway:
    external: true

services:
  ipcheck:
    image: "hurt/echoip"
    container_name: "ipcheck"
    networks:
      - service-gateway
    ports:
      - "8080:8080"
    labels:
      - "traefik.enable=true"
      ## Insecure router
      - "traefik.http.routers.ipcheck.rule=Host(`example.com`, `www.example.com`)"
      - "traefik.http.routers.ipcheck.entrypoints=web"
      - "traefik.http.routers.ipcheck.service=ipcheck-ipcheck"
      ## Secure router
      - "traefik.http.routers.ipcheck-sec.rule=Host(`example.com`, `www.example.com`)"
      - "traefik.http.routers.ipcheck-sec.entrypoints=websecure"
      - "traefik.http.routers.ipcheck-sec.tls.certresolver=LetsEncrypt"
      - "traefik.http.routers.ipcheck-sec.service=ipcheck-ipcheck"

When browsing it via Traefik (example.com) it gives me the internal gateway address (172.19.0.1) but when I browse it directly (example.com:8080) it works as intended although it should be affected by the same limitations if I understand correctly...