Exposing Services over TLS from Containers in Docker Swarm

I am trying to expose web services running as container in a docker swarm via https. This is what I've come up with so far:

version: "3.7"

volumes:
  certificates:

services:

  proxy:
    image: traefik:v2.4
    environment:
      - EMAIL=tcurdt@foo.com
      - DOMAIN=foo.com
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - certificates:/certificates
    command:
      - --log.level=DEBUG
      - --api.insecure=true
      # docker
      - --providers.docker.swarmmode
      - --providers.docker.exposedbydefault=false
      # ports
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls=true
      # redirects
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      # certificats
      - --entrypoints.websecure.http.tls.certResolver=le
      - --certificatesresolvers.le.acme.tlschallenge=true
      - --certificatesresolvers.le.acme.email=${EMAIL}
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
    deploy:
      placement:
        constraints:
          - node.role==manager

  backend:
    image: traefik/whoami
    # expose:
    #   - 80
    labels:
      - traefik.enable=true

I am still unclear what labels are required to to make traefik find the backend services. The redirecting works, but no backends are responding.

> curl -sk http://127.0.0.1
Moved Permanently
> curl -sk https://127.0.0.1
404 page not found

It seems like traefik is not finding the containers:

> curl -s http://127.0.0.1:8080/api/rawdata | jq
{
  "routers": {
    "api@internal": {
      "entryPoints": [
        "traefik"
      ],
      "service": "api@internal",
      "rule": "PathPrefix(`/api`)",
      "priority": 2147483646,
      "status": "enabled",
      "using": [
        "traefik"
      ]
    },
    "dashboard@internal": {
      "entryPoints": [
        "traefik"
      ],
      "middlewares": [
        "dashboard_redirect@internal",
        "dashboard_stripprefix@internal"
      ],
      "service": "dashboard@internal",
      "rule": "PathPrefix(`/`)",
      "priority": 2147483645,
      "status": "enabled",
      "using": [
        "traefik"
      ]
    },
    "web-to-websecure@internal": {
      "entryPoints": [
        "web"
      ],
      "middlewares": [
        "redirect-web-to-websecure@internal"
      ],
      "service": "noop@internal",
      "rule": "HostRegexp(`{host:.+}`)",
      "priority": 2147483646,
      "status": "enabled",
      "using": [
        "web"
      ]
    }
  },
  "middlewares": {
    "dashboard_redirect@internal": {
      "redirectRegex": {
        "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$",
        "replacement": "${1}/dashboard/",
        "permanent": true
      },
      "status": "enabled",
      "usedBy": [
        "dashboard@internal"
      ]
    },
    "dashboard_stripprefix@internal": {
      "stripPrefix": {
        "prefixes": [
          "/dashboard/",
          "/dashboard"
        ]
      },
      "status": "enabled",
      "usedBy": [
        "dashboard@internal"
      ]
    },
    "redirect-web-to-websecure@internal": {
      "redirectScheme": {
        "scheme": "https",
        "port": "443",
        "permanent": true
      },
      "status": "enabled",
      "usedBy": [
        "web-to-websecure@internal"
      ]
    }
  },
  "services": {
    "api@internal": {
      "status": "enabled",
      "usedBy": [
        "api@internal"
      ]
    },
    "dashboard@internal": {
      "status": "enabled",
      "usedBy": [
        "dashboard@internal"
      ]
    },
    "noop@internal": {
      "status": "enabled",
      "usedBy": [
        "web-to-websecure@internal"
      ]
    }
  }
}

What do I need to change?

It's a real shame that there are no "getting started examples".
At least I couldn't find any.

I've updated the backend service to look like this:

  backend:
    image: traefik/whoami
    # expose:
    #   - 80
    labels:
      - traefik.enable=true
      - traefik.http.routers.backend.rule=Host(`example.com`)
      # - traefik.http.routers.backend.rule=HostRegexp(`{host:.+}`)
      - traefik.http.routers.backend.tls.certresolver=le
      - traefik.http.routers.backend.entrypoints=websecure
      # - traefik.http.services.backend.loadbalancer.server.port=80

In the log I can see

time="2021-02-17T20:31:38Z" level=debug msg="Provider connection established with docker 20.10.3 (API 1.41)" providerName=docker,
time="2021-02-17T20:31:38Z" level=debug msg="Filtering disabled container" providerName=docker container=portainer-agent-vy8p2cbj3pq2i9uyc7fxw23kf,
time="2021-02-17T20:31:38Z" level=debug msg="Filtering disabled container" providerName=docker container=portainer-server-birv66ms20znw5jjiydo2rakc,
time="2021-02-17T20:31:38Z" level=debug msg="Filtering disabled container" providerName=docker container=web-backend-4bp6qaxddayvn1bah8rb2212q,

and I am wondering why the container is disabled?!?

And something else concerning:

time="2021-02-17T20:31:40Z" level=debug msg="Configuration received from provider docker: {\"http\":{},\"tcp\":{},\"udp\":{}}" providerName=docker,
time="2021-02-17T20:31:40Z" level=debug msg="http: panic serving 10.0.0.2:45106: runtime error: invalid memory address or nil pointer dereference",
time="2021-02-17T20:31:40Z" level=debug msg="goroutine 86 [running]:",
time="2021-02-17T20:31:40Z" level=debug msg="net/http.(*conn).serve.func1(0x641a240)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/net/http/server.go:1801 +0xf0",
time="2021-02-17T20:31:40Z" level=debug msg="panic(0x239edc0, 0x448b480)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/runtime/panic.go:975 +0x47c",
time="2021-02-17T20:31:40Z" level=debug msg="crypto/tls.(*Conn).readClientHello(0x5d16600, 0x0, 0xa6cb44e0, 0x0)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/crypto/tls/handshake_server.go:139 +0x54",
time="2021-02-17T20:31:40Z" level=debug msg="crypto/tls.(*Conn).serverHandshake(0x5d16600, 0x1, 0x249501)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/crypto/tls/handshake_server.go:40 +0x20",
time="2021-02-17T20:31:40Z" level=debug msg="crypto/tls.(*Conn).Handshake(0x5d16600, 0x0, 0x0)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/crypto/tls/conn.go:1362 +0xd0",
time="2021-02-17T20:31:40Z" level=debug msg="net/http.(*conn).serve(0x641a240, 0x2cb6600, 0x5cfc040)",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/net/http/server.go:1817 +0x180",
time="2021-02-17T20:31:40Z" level=debug msg="created by net/http.(*Server).Serve",
time="2021-02-17T20:31:40Z" level=debug msg="\t/usr/local/go/src/net/http/server.go:2969 +0x2d0",

Hey tcurdt :wave:

Seems that for Swarm you should have labels under the section deploy and not directly with image from what I have noticed in your config. I think that it is the root cause why Traefik is not able to find your backend.

Please have a look at the example configuration on my repo, and notice the line number 44 and than 55

Thanks,

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.