Traefik v1.7 is running within a Docker Swarm, functioning as a global service. Consequently, an instance of Traefik is deployed for each host within our Docker Swarm cluster. It is important to note that the Swarm’s routing mesh is not employed in this setup. Instead, the port mode is configured as “host,” allowing the port to be attached to the host’s network.

traefik:
  image: ${REGISTRY}/traefik:1.6
  build: ./traefik
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
  networks:
    - traefik
  environment:
    - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
    - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
    - AWS_REGION=${AWS_REGION}
    - AWS_HOSTED_ZONE_ID=${AWS_HOSTED_ZONE_ID}
  ports:
    - target: 80
      published: 80
      protocol: tcp
      mode: host
    - target: 443
      published: 443
      mode: host
      protocol: tcp
    - target: 8080
      published: 8080
      mode: host
      protocol: tcp
  deploy:
    mode: global
    restart_policy:
      condition: on-failure

Docker Swarm deployed to AWS and lives behind internal/external load balancers (Classic type).

// terraform SG fragment
ingress {
  from_port   = 80
  to_port     = 80
  protocol    = "tcp"
  description = "acme chellange"
  cidr_blocks = ["0.0.0.0/0"]
}
ingress {
  from_port   = 443
  to_port     = 443
  protocol    = "tcp"
  description = "SSL termination"
  cidr_blocks = ["0.0.0.0/0"]
}

Let’s assume mydomain.com/mobile-api should be worldwide visible but the rest of app should be available via Whitelist. Hopefully Traefik supports entrypoints by route, so there is nothing easiest then make one more clone of app and set frontend rule targeting to /route.

modile-api:
  image: ${REGISTRY}/app:${VERSION:-latest}
  networks:
    - traefik
  deploy:
    mode: replicated
    replicas: 1
    restart_policy:
      condition: on-failure
    labels:
      - traefik.backend=modile-api
      - traefik.port=80
      - traefik.frontend.rule=Host:${HOST_API};PathPrefix:/mobile-api
      - traefik.docker.network=traefik
      - traefik.enable=true

app:
  image: ${REGISTRY}/app:${VERSION:-latest}
  networks:
    - traefik  
  deploy:
    mode: replicated
    replicas: 1
    restart_policy:
      condition: on-failure
    labels:
      - traefik.backend=app
      - traefik.port=80
      - traefik.frontend.whiteList.sourceRange=${WHITE_LIST}
      - traefik.frontend.whiteList.useXForwardedFor=true
      - traefik.frontend.rule=Host:${HOST_API}
      - traefik.docker.network=traefik
      - traefik.enable=true

Important part: set up ProxyProtocol on both side Traefik (reverse-proxy) and AWS ELB. Proxy protocol Traefik side: traefik.toml

[entryPoints.https.whiteList]
  sourceRange = ["1.2.3.4/24"] # you can override this part on service level
  useXForwardedFor = true
[entryPoints.https.proxyProtocol]
  trustedIPs = ["10.0.100.0/24"] # private subnet CIRD block or IPs
[entryPoints.https.forwardedHeaders]
  trustedIPs = ["10.0.100.0/24"]  # private subnet CIRD block or IPs

Proxy protocol AWS side: (if your listener settings support Proxy Protocol! )

1 # make sure proxyPolicy is disabled. => "BackendServerDescriptions" : []
aws elb describe-load-balancer-policy-types

2 aws elb create-load-balancer-policy --load-balancer-name %lb_name% --policy-name %name% --policy-type-name ProxyProtocolPolicyType --policy-attributes AttributeName=ProxyProtocol,AttributeValue=true
3 aws elb set-load-balancer-policies-for-backend-server --load-balancer-name %lb_name% --instance-port 443 --policy-names %policy_name%
4 check (optional)
aws elb describe-load-balancers --load-balancer-name %lb_name%

debug:
aws elb delete-load-balancer-policy --load-balancer-name %lb_name% --policy-name %policy_name%
# get ELB IPs
aws ec2 describe-network-interfaces --filters Name=description,Values="ELB elb_name" --query 'NetworkInterfaces[*].PrivateIpAddresses[*]'

If everything goes as expected you’ll see your IP in X-Forwarded-For header 🎉

Hostname: 6b27211a260c
IP: 127.0.0.1
IP: 10.255.1.59
IP: 172.18.0.13
IP: 10.0.2.65
GET / HTTP/1.1
Host: domain.com
User-Agent: redacted
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ru-RU;q=0.8,ru;q=0.7,uk-UA;q=0.6,uk;q=0.5
Cache-Control: max-age=0
Connection: keep-alive
Upgrade-Insecure-Requests: 1
X-Forwarded-For: you_real_ip
X-Forwarded-Port: 80
X-Forwarded-Proto: https