TLS handshake error client offered only unsupported versions: [301]

Hi (bluepuma),

I have all my Traefik working on local with local DNS resolver. I can access to my containers with a reverse proxy but I'm struggling to make it working over internet.

Traefik is running on a macvlan local IP 192.168.0.102 under a Synology

When I'm trying to access over internet I have those errors in the logs :

level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:2149: write tcp 192.168.0.102:443->XXX.XXX.XXX.XXX:2149: write: connection reset by peer"
level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:2150: write tcp 192.168.0.102:443->XXX.XXX.XXX.XXX:2150: write: connection reset by peer"
level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:2151: tls: client offered only unsupported versions: [301]"

I've tried to lower the minimum value for TLS to 1.1 but still not working. I would not even need this as I try to access from an up to date device.

Of course I have a valid cert in acme.json that Traefik sees

I'm wondering if it comes from the home router, as I forward incoming requests from port 80 and 443 TCP to 192.168.0.102 port 80 and 443

Here is the config :
docker-compose.yml

version: '3'

secrets:
  ovh_application_key:
    file: blabla
  ovh_application_secret:
    file: blabla
  ovh_consumer_key:
    file: blabla

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    networks:
      macvlan:
      traefik:
    volumes:
      - /volume1/docker/traefik/config:/etc/traefik
      - /etc/localtime:/etc/localtime:ro

    labels:
      traefik.enable: true
      traefik.http.routers.dashboard.rule: Host(`traefik.mydomain.com`)
      traefik.http.routers.dashboard.service: api@internal
      traefik.http.routers.dashboard.middlewares: auth
      traefik.http.routers.dashboard.entrypoints: websecure
      traefik.http.middlewares.auth.basicauth.users: XXXXXXXXXXXXX
    depends_on:
      - socket-proxy

    secrets:
      - ovh_application_key
      - ovh_application_secret
      - ovh_consumer_key

    environment:
      OVH_ENDPOINT: ovh-eu
      OVH_APPLICATION_KEY_FILE: /run/secrets/ovh_application_key
      OVH_APPLICATION_SECRET_FILE: /run/secrets/ovh_application_secret
      OVH_CONSUMER_KEY_FILE: /run/secrets/ovh_consumer_key
  
  socket-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: traefik_docker_socket-proxy
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      CONTAINERS: 1
    networks:
      - traefik

networks:
  macvlan:
    name: macvlan
  traefik:
    name: traefik
  

static config traefik.yml

global:
  checkNewVersion: true
  sendAnonymousUsage: false

entrypoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: :443
    http:
      tls:
        certResolver: letsencrypt
        domains:
          - main: mydomain.com
            sans: "*.mydomain.com"
    forwardedHeaders:
  traefik:
    address: :8080

ping : {}

log:
  level: debug

accessLog:
  filePath: "/etc/traefik/access.log"

serversTransport:
  insecureSkipVerify: true

api:
  dashboard: true
  debug: true

providers:
  docker:
    endpoint: tcp://socket-proxy:2375
    exposedByDefault: false
  file:
    filename: /etc/traefik/rules.yml

certificatesResolvers:
  letsencrypt:
    acme:
      email: XXXXX
      storage: /etc/traefik/acme.json
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      dnsChallenge:
        provider: ovh
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

Nothing special in rules.yml (dynamic config)(maybe that's why it is not working)

One container I'm trying to access :

version: '3.8'
services:
  pingvin-share:
    image: stonith404/pingvin-share
    container_name: pingvin-share
    restart: unless-stopped
    environment:
      - PORT:3000
    volumes:
      - blabla
      - blabla
    networks:
      traefik:
    labels:
      traefik.enable: true
      traefik.http.routers.pingvin-share.rule: Host(`share.mydomain.com`)
      traefik.http.routers.pingvin-share.entrypoints: websecure
      traefik.http.routers.pingvin-share.tls: true
      traefik.http.routers.pingvin-share.tls.certresolver: letsencrypt

networks:
  traefik:
    external: true

Thank you

Check with curl -v what a client tells you.

It seems you try to improve security, but are maybe not aware that

  1. tecnativa/docker-socket-proxy image (with default tag latest) is already 3 years old (link), using outdated haproxy

  2. the volume

- /var/run/docker.sock:/var/run/docker.sock:ro

is not read-only, as the configuration implies

Thank you for taking time, here are

curl -v share.mydomain.com

curl -v share.mydomain.com
*   Trying XXX.XXX.XXX.XXX:80... #Public IP
* Connected to share.mydomain.com (XXX.XXX.XXX.XXX) port 80
> GET / HTTP/1.1
> Host: share.mydomain.com
> User-Agent: curl/8.4.0
> Accept: */*
>
* Recv failure: Connection was reset
* Closing connection
curl: (56) Recv failure: Connection was reset

and curl -v https://share.mydomain.com

curl -v https://share.mydomain.com
*   Trying XXX.XXX.XXX.XXX:443...
* Connected to share.mydomain.com (XXX.XXX.XXX.XXX) port 443
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* Recv failure: Connection was reset
* schannel: failed to receive handshake, SSL/TLS connection failed
* Closing connection
* schannel: shutting down SSL/TLS connection with share.mydomain.com port 443
* Send failure: Connection was reset
* schannel: failed to send close msg: Failed sending data to the peer (bytes written: -1)
curl: (35) Recv failure: Connection was reset

Working fine on local, HTTP redirected to HTTPS

curl -v share.mydomain.com
*   Trying 192.168.0.102:80...
* Connected to share.mydomain.com (192.168.0.102) port 80
> GET / HTTP/1.1
> Host: share.mydomain.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Location: https://share.mydomain.com/
< Date: Wed, 27 Mar 2024 10:40:06 GMT
< Content-Length: 17
< Content-Type: text/plain; charset=utf-8
<
Moved Permanently* Connection #0 to host share.mydomain.com left intact

One thing I may need to mention is that the host (Synology) has an other certificate running. It's the same domain and wildcard domain by Let's Encrypt but it was generated earlier, should i switch to default cert ?

About security,

  1. what image should I use then ?
  2. Should I add read_only: true to docker-compose ?

Thank you

Step 1 would be getting port 80 only to work. Maybe with a plain whoami container on port 80.

I understand you are trying to connect home, maybe your provider does not allow connections to port 80.

I added a whoami container to traefik, working great on local

Hostname: 775cb58a7602
IP: 127.0.0.1
IP: 192.168.208.4 #Docker IP of whoami container
RemoteAddr: 192.168.208.3:49628 #Docker IP of traefik container
GET / HTTP/1.1
Host: whoareyou.mydomain.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: fr-FR,fr;q=0.9,en;q=0.8
Sec-Ch-Ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 192.168.0.20 #My local IP
X-Forwarded-Host: whoareyou.mydomain.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: xxxx
X-Real-Ip: 192.168.0.20

But got same result from internet :

level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:9950: write tcp 192.168.0.102:443->XXX.XXX.XXX.XXX:9950: write: connection reset by peer"
level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:9951: write tcp 192.168.0.102:443->XXX.XXX.XXX.XXX:9951: write: connection reset by peer"
level=debug msg="http: TLS handshake error from XXX.XXX.XXX.XXX:9952: tls: client offered only unsupported versions: [301]"

whoami container

whoami:
    image: "traefik/whoami"
    container_name: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoareyou.mydomain.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
    networks:
      - traefik

I'm seriously lost, I do not know what to do

I can use NAT to redirect 80 and 443 to traefik, it worked before with Synology reverse proxy

It seems you never use ports in docker-compose.yml to let Traefik listen on the ports outside of the container.

Compare to simple Traefik example.

So probably another service is listening (and responding) on ports 80+443.

Quick reply,

If I set ports 80 and 443, I get this error :

Error response from daemon: driver failed programming external connectivity on endpoint traefik (blabla_containerID_blabla): Error starting userland proxy: listen tcp4 0.0.0.0:443: listen: address already in use

That's why I did macvlan so Traefik can use all the ports it wants
If Traefik couldn't listen on port 80 and 443, I wouldn't get those TLS handshake errors from Traefik, would it ?

I do have this error too sometimes :

http: TLS handshake error from XXX.XXX.XXX.XXX:38788: EOF

If Synology has already something listening, then the port is occupied. Sorry I ignored your macvlan, but how do you set the IP?

Yes it's occupied for the IP of the Syno, but Traefik has an other IP

It's defined in the docker-compose (I removed it on the original post)

macvlan:
    name: macvlan
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.0.0/24
          ip_range: 192.168.0.102/32
          gateway: 192.168.0.1

Here, the container has no other choice than having the 192.168.0.102 IP and that's what it does

So you can reach Traefik on Syno from another node in the local network, but not from the Internet via your router?

You do forward external router ports 80+443 to the macvlan Traefik IP?

Your domain is set to the external router IP?

That's it, I do not have TLS handshake errors on local

That's it

Yes

On local it's doing :

local PC (192.168.0.20)
-> Syno as a local DNS resolver (192.168.0.48)
-> "IP of mydomain.com is 192.168.0.102"
-> Connect to Traefik (192.168.0.102)

From internet :

External PC (XXX.XXX.XXX.XXX)
-> Home Router (My public IP)
-> Port 443/80 from Home Router redirects to
-> 192.168.0.102 Port 443/80 (Traefik)

We are sure traffic from public IP is coming to Traefik as Traefik logs the handshake error of a public IP
There might have something I'm doing wrong with the certificate but I can't know what it is

Is the router doing a redirect (send HTTP redirect to client to use a different address) or is it forwarding the request to Syno?

I do not really know, it’s a NAT rule set : external port 443(/80) TCP going to 192.168.0.102 port 443(/80)

If I set the ports for the docker-compose of Traefik like

ports:
     - 80:80
     - 443:443

after disabling ports 80 and 443 on Syno
Traefik is running on both macvlan and Syno IP (so I could just remove macvlan)
I change the NAT rule to

External PC (XXX.XXX.XXX.XXX)
-> Home Router (My public IP)
-> Port 443/80 from Home Router redirects to
-> 192.168.0.48 Port 443/80 (Syno but Traefik using those ports)

it works but not ideal. The macvlan has been made useless and that's not what I want.
So it's working well from outside if Traefik is under Syno IP but as soon as I put the macvlan IP, I have those TLS handshake errors, it doesn't make sense, any idea ?

Maybe try the Docker Forum regarding macvlan and Synology.

I'll try on there thank you.

For now, what I've done is following this : Free 80,443 Ports - 3os

and set

ports:
      - 80:80
      - 443:443

while removing macvlan so it's working for now but not what I wanted. At least with this config I can use the Syno firewall

Thank you for trying to help