Cannot negotiate ALPN protocol (Docker environment)

Hi!

So, I have this website running on Laravel 8. I dockerized it then published it on a repository. Since it's a small website, the server itself is Artisan, Laravel's main tool, and it is set to run on 0.0.0.0:8080, so it's easily set to be available outside the container. The issue may come from here, though.

Anyway, I followed this toturial from Traefik's documentation itself and this documentation to push a bit further. As a result, here is my docker-compose file:

version: '3.5'

services:

  traefik:
    image: traefik:chevrotin
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --certificatesresolvers.le.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.le.acme.email=jlevarato@pm.me
      - --certificatesresolvers.le.acme.storage=/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true

  web:
    image: drillan767/mypetsnanny:latest
    restart: unless-stopped
    ports:
      - "8080:80"
    links:
      - db
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - mpn_storage:/app/storage/app/public
      - .env:/app/.env
    networks:
      - mpn
    labels:
      - "traefik.http.routers.web.rule=Host(`mypetsnanny.fr`)"
      - "traefik.http.routers.web.tls=true"
      - "traefik.http.routers.web.tls.certresolver=le"

  db:
    image: mariadb:latest
    ports:
      - 3306
    environment:
      MYSQL_USER: someuser
      MYSQL_DATABASE: somedb
      MYSQL_PASSWORD: some_pwd
      MYSQL_ROOT_PASSWORD: some_other_pwd
    networks:
      - mpn

volumes:
  mpn_storage:
    name: mpn_storage
    external: true

networks:
  mpn:
    external: true

There's a lot of stuff I'm not even sure if I'm doing right, but I got no fatal error from Traefik which is nice.

However, when running docker-compose logs traefik, I get the following:

traefik_1 | time="2020-12-06T00:48:27Z" level=error msg="Unable to obtain ACME certificate for domains "mypetsnanny.fr": unable to generate a certificate for the domains [mypetsnanny.fr]: error: one or more domains had a problem:\n[mypetsnanny.fr] acme: error: 403 :: urn:ietf:params:acme:error:unauthorized :: Cannot negotiate ALPN protocol "acme-tls/1" for tls-alpn-01 challenge, url: \n" providerName=le.acme routerName=web@docker rule="Host(mypetsnanny.fr)"

In the mean time, I get a simple "gateway timeout" message on my website.

Also, if I run "curl localhost" outside my container, I'll get 404 page not found, but if I do curl localhost:8080 it inside it, I'll get the html for my website. It seems that I failed to "plug" Traefik to the website but I don't know how it should be done.

Thank you in advance!

I think Traefik and ALPN are setup properly, nothing to change there. I can connect and Traefik accepts the acme-tls/1 protocol.

What I actually think is going wrong is that the IPv6 address/AAAA DNS record of mypetsnanny.fr is set to some random Apache server, and Let's Encrypt is connecting to that instead of Traefik.

Try removing the AAAA record and see whether that makes any difference.

3 Likes

One more thing:

Not directly related to your problem, but shouldn't you be mounting /acme.json as a volume for Traefik to persistently store the accounts, private keys and certificates?

Otherwise Traefik would recreate everything on every restart and you'll quickly hit rate-limits and be unable to create certificates.

If you've already got persistence working some other way, sorry!

3 Likes

Thanks for the quick reply, I got to bed right after posting this question so I didn't see it until recently!

I removed the "AAAA" DNS record, and now I instantly get "404 not found" when trying http, but I still get Gateway timeout when trying https.

As of right now, here is how my docker-compose file looks with your feedback and some stuff I found online, such as forcing https redirection:

version: '3.7'

services:

  traefik:
    image: traefik:chevrotin
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "80:80"
      - "443:443"
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --certificatesresolvers.le.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.le.acme.email=jlevarato@pm.me
      - --certificatesresolvers.le.acme.storage=/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
    labels:
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    volumes:
      - ./acme.json:/acme.json
      - /var/run/docker.sock:/var/run/docker.sock:ro

  web:
    image: drillan767/mypetsnanny:latest
    restart: unless-stopped
    ports:
      - "8080:80"
    links:
      - db
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - mpn_storage:/app/storage/app/public
      - .env:/app/.env
    networks:
      - mpn
    labels:
      - "traefik.http.routers.web.rule=Host(`mypetsnanny.fr`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.web.tls=true"
     # - "traefik.http.routers.web.tls.certresolver=le"

  db:
    image: mariadb:latest
    restart: unless-stopped
    labels:
      - "traefik.backend=db"
      - "traefik.enable=false"
    ports:
      - 3306
    environment:
      MYSQL_USER: someuser
      MYSQL_DATABASE: somedb
      MYSQL_PASSWORD: some_pwd
      MYSQL_ROOT_PASSWORD: some_other_pwd
    networks:
      - mpn

volumes:
  mpn_storage:
    name: mpn_storage
    external: true

So now, when completely removing completely my containers (docker-compose down --rmi=all) and restarting everything, I get no error from Traefik, just a notice that it loaded the flagged configuration. However, I still can't get to display my website outside its container, maybe matching it to the 80 port while it's used by Traefik isn't the best idea, but I have no clue of what I should do instead

Thanks in advance

P.S: btw, I added the volume as you suggested, but the acme.json file stays empty, and I can't find other ways to set it up.

Small update: I changed the port used by my application to 8080 and added a label for Traefik so it uses it, and fixed a bad copy / paste on the traefik.http.routers.whoami.entrypoints=websecure line (whoami has nothing to do here). Still no website displayed, but at least things are evolving.

Even smaller update: I discovered that it was linked to the fact that Traefik had to be linked to the same network than the web app. And now it works, but the https isn't safe:

image

What do the logs for the Traefik container say now?

Just the following:

traefik_1 | time="2020-12-06T20:37:23Z" level=info msg="Configuration loaded from flags."

This being said, my acme.json is still empty (I used chmod 600 on it just fyi). I added it inside my docker-compose but it doesn't appear inside the traefik container's root. Not sure if it's normal..

I was able to adapt your docker-compose.json slightly and got it running:

  1. Updating the acme.json volume mount to use an absolute path on the host system
  2. Pre-creating the empty acme.json file on the host system and ensuring it is 0600 (though I see you seem to have figured that out yourself)
  3. Uncommenting the certresolver label in the web service (which I replaced with a dummy nginx:latest container since your container isn't public)

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