Traefik with FTPS

hello, i m trying to migrate from HAProxy to Traefik, i have an existing config of Haproxy that act as TCP loadbalancer for an FTPS server. now i m trying to that same with traefik.
The FPTS server Operate on the following port 4113 for control and 4114-4120 to transfer Data
here the current config for my Traefik:

static Config
api:
  insecure: true
  dashboard: true

log:
  level: "DEBUG"
  filePath: "/logs/traefik.log"

certificatesResolvers:
  letsEncrypt:
    acme:
      email: "name@email"  # Email address used for registration
      storage: "/etc/traefik/acme/acme.json"    # File or key used for certificates storage
      tlsChallenge: {}


entryPoints:
  ftps:
    address: ":4113"
  ftps_data_1:
    address: ":4114"
  ftps_data_2:
    address: ":4115"
  ftps_data_3:
    address: ":4116"
  ftps_data_4:
    address: ":4117"
  ftps_data_5:
    address: ":4118"
  ftps_data_6:
    address: ":4119"
  ftps_data_7:
    address: ":4120"


metrics:
  prometheus: {}

providers:
  file:
    directory: "/etc/traefik/config"
    watch: true

    
serversTransport:
  insecureSkipVerify: true
Dynamic config
tcp:
  routers:
    hi_ftps_prod:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps"
        - "ftps_data_1"
        - "ftps_data_2"
        - "ftps_data_3"
        - "ftps_data_4"
        - "ftps_data_5"
        - "ftps_data_6"
        - "ftps_data_7"
      service: "ssl_hi_ftps_prod"
      tls: {}
    
    hi_ftps_stage:
      rule: "HostSNI(`ms-stage.hi.com`)"
      entryPoints:
        - "ftps"
        - "ftps_data_1"
        - "ftps_data_2"
        - "ftps_data_3"
        - "ftps_data_4"
        - "ftps_data_5"
        - "ftps_data_6"
        - "ftps_data_7"
      service: "ssl_hi_ftps_stage"
      tls: {}

  services: 
    ssl_hi_ftps_prod:
      loadBalancer:
        servers:
          - address: "172.20.149.86:4113" # Connectivity 
          - address: "172.20.149.86:4114" # Data 
          - address: "172.20.149.86:4115" # Data 
          - address: "172.20.149.86:4116" # Data 
          - address: "172.20.149.86:4117" # Data 
          - address: "172.20.149.86:4118" # Data 
          - address: "172.20.149.86:4119" # Data 
          - address: "172.20.149.86:4120" # Data  
    ssl_hi_ftps_stage:
      loadBalancer:
        servers:
        - address: "172.20.150.9:4113" # Connectivity 
        - address: "172.20.150.9:4114" # Data 
        - address: "172.20.150.9:4115" # Data
        - address: "172.20.150.9:4116" # Data 
        - address: "172.20.150.9:4117" # Data 
        - address: "172.20.150.9:4118" # Data 
        - address: "172.20.150.9:4119" # Data 
        - address: "172.20.150.9:4120" # Data 
tls.yml
tls:
  certificates:
    - certFile: /etc/letsencrypt/certs/hi.com/fullchain.pem
      keyFile: /etc/letsencrypt/certs/hi.com/key.pem
  
  options:
    default:
      sniStrict: true

docker-compose.yml
services:
  traefik:
    image: traefik:2.10
    container_name: traefik
    network_mode: host
    restart: always
    volumes:
      - /srv/traefik/:/etc/traefik
      - /srv/traefik/logs:/logs
      - /etc/letsencrypt:/etc/letsencrypt:ro
    command: [ ]

from the logs i got:

time="" level=debug msg="Handling TCP connection from [my_ipv6]:40760 to 172.20.149.86:4114"
time="" level=error msg="Error while dialing backend: dial tcp 172.20.149.86:4114: connect: connection refused"
time="" level=debug msg="Handling TCP connection from [my_ipv6]:43232 to 172.20.149.86:4118"
time="" level=error msg="Error while dialing backend: dial tcp 172.20.149.86:4118: connect: connection refused"

i don't get it, i never setup such a thing with traefik , also when the setup was done with haproxy , it prompt me in filezilla to accept the certifcate when i try to connect to the ftps server.
anyone that have tried to do such a config kindly provide me with some guidance?

Are those ports TCP or UDP?

It seems you didn’t open any ports on Traefik container.

You probably need to create 7 different routers/services because I don’t think your target server likes receiving packets on port 4114 when it was intended for 4120.

It might not work at all, as a chatbot suggests that the initial connection is TCP but not TLS. So regular HostSNI() would not work. You could try HostSNI(`*`), but then you can only have a single target on the port, as Traefik can’t differentiate.

Note: you can only use one static config, traefik.yml or command:.

Thus are TCP Ports, the network_mode: host setting means that the container will share the host system's networking namespace.

when using HostSNI('*')

time="" level=debug msg="Error while terminating TCP connection: tls: CloseWrite called before handshake complete"
time="" level=debug msg="Handling TCP connection from [MY_ip]:47696 to 172.20.149.86:4113"
time="" level=error msg="Error while handling TCP connection: readfrom tcp 172.20.148.13:53494->172.20.149.86:4113: tls: client requested unsupported application protocols ([ftp])"
time="" level=debug msg="Error while terminating TCP connection: tls: CloseWrite called before handshake complete"

Under the assumption it's not a "real" TLS connection, you need to remove tls: {}, so no TLS is activated for the router and only plain TCP/IP is used to proxy the connection to the target service.

i m in need to use TLS connection, but i would like the LB to give me his own certifcate to accept it with filezilla in other word, a secure connection between client and the LB, the the LB would re-transmit the trafiic to the backend.
i got some progess using the following config :

tcp:
  routers:
    ## Connect
    hi_ftps_prod_connect:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps"
      service: "ssl_hi_ftps_prod_connect"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_1:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_1"
      service: "ssl_hi_ftps_prod_passive_1"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_2:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_2"
      service: "ssl_hi_ftps_prod_passive_2"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_3:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_3"
      service: "ssl_hi_ftps_prod_passive_3"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_4:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_4"
      service: "ssl_hi_ftps_prod_passive_4"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_5:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_5"
      service: "ssl_hi_ftps_prod_passive_5"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_6:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_6"
      service: "ssl_hi_ftps_prod_passive_6"
      tls: true

    ## Data transfer
    hi_ftps_prod_passive_7:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_7"
      service: "ssl_hi_ftps_prod_passive_7"
      tls: true

    
  services:
    
    ## Prod Connect
    ssl_hi_ftps_prod_connect:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4243" # Connectivity 
        
    ## Data transfer
    ssl_hi_ftps_prod_passive_1:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4244" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_2:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4245" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_3:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4246" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_4:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4247" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_5:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4248" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_6:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4249" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_7:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4250" # Data

now i got from the logs

time="" level=error msg="Error while handling TCP connection: readfrom tcp MY-ip:48192->172.20.149.86:4243: tls: client requested unsupported application protocols ([ftp])"
time="" level=debug msg="Error while terminating TCP connection: tls: CloseWrite called before handshake complete"

NOTE: i m connecting with the Filezilla Client

As I wrote in an earlier post, it might not be a pure TLS connection:

There are reasons why FTP is considered a deprecated protocol. Have a look at WebDAV, which uses http, so should be reverse proxy compatible.

further information on the error with the ftp, i was messing the alpnProtocols on TLS handshake:
i should be something like the following :

tls:
  certificates:
    - certFile: /etc/letsencrypt/certs_files/fullchain.pem
      keyFile: /etc/letsencrypt/certs_files/key.pem
 
  options:
    default:
      alpnProtocols:
        - ftp

and the dynamic config will be :

tcp:
  routers:
    ## Connect
    hi_ftps_prod_connect:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps"
      service: "ssl_hi_ftps_prod_connect"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_1:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_1"
      service: "ssl_hi_ftps_prod_passive_1"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_2:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_2"
      service: "ssl_hi_ftps_prod_passive_2"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_3:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_3"
      service: "ssl_hi_ftps_prod_passive_3"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_4:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_4"
      service: "ssl_hi_ftps_prod_passive_4"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_5:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_5"
      service: "ssl_hi_ftps_prod_passive_5"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_6:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_6"
      service: "ssl_hi_ftps_prod_passive_6"
      tls: {}

    ## Data transfer
    hi_ftps_prod_passive_7:
      rule: "HostSNI(`ms.hi.com`)"
      entryPoints:
        - "ftps_data_7"
      service: "ssl_hi_ftps_prod_passive_7"
      tls: {}

    
  services:
    
    ## Prod Connect
    ssl_hi_ftps_prod_connect:
      loadBalancer:
        servers:
        - address: "172.20.149.86:4113" # Connectivity 
        
    ## Data transfer
    ssl_hi_ftps_prod_passive_1:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4114" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_2:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4115" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_3:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4116" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_4:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4117" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_5:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4118" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_6:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4119" # Data 

    ## Data transfer
    ssl_hi_ftps_prod_passive_7:
      loadBalancer:
        servers:
        - address: "172.20.49.6:4120" # Data

now i got the tls handsahke to complete between the FileZilla client and traefik , but i unable to get the directory from the server:
filezilla showig the following :

Command:	PWD
Response:	257 "/" is current directory.
Command:	TYPE I
Response:	200 Command TYPE okay.
Command:	EPSV
Response:	229 Entering Passive Mode (|||4114|)
Command:	MLSD
Response:	150 File status okay; about to open data connection.
Error:	Connection timed out after 20 seconds of inactivity
Error:	Failed to retrieve directory listing

logs from traefik :

time="# level=debug msg="Handling TCP connection from [MY-ip]:53050 to 172.20.49.6:4113"
time="#" level=debug msg="Handling TCP connection from [MY-ip]:50873 to 172.20.49.6:4114"

any idea what could be blocking traffic from going back to client ?

I am pretty new to Traefik, so sorry for blind shot, but I assume that neither HAProxy nor Traefik can terminate TLS from FTPS and you have to pass it thru:

    tls:
        passthrough: true

it does terminate the TLS, with the last comment i mention, now i'm just facing a timeout from filezilla client when data transfer start.

Command:	PWD
Response:	257 "/" is current directory.
Command:	TYPE I
Response:	200 Command TYPE okay.
Command:	EPSV
Response:	229 Entering Passive Mode (|||4114|)
Command:	MLSD
Response:	150 File status okay; about to open data connection.
Error:	Connection timed out after 20 seconds of inactivity
Error:	Failed to retrieve directory listing
1 Like

It’s probably an issue because you re-map the ports from internal to external to different port numbers.

The server doesn’t know about it and announces to the client certain ports to use, which are not available externally.