Skip to content

Instantly share code, notes, and snippets.

@estelsmith
Last active July 4, 2022 13:57
Show Gist options
  • Save estelsmith/815b6c4a3f3642c6a1bec744f597b093 to your computer and use it in GitHub Desktop.
Save estelsmith/815b6c4a3f3642c6a1bec744f597b093 to your computer and use it in GitHub Desktop.
Attempting to do name-based FTP proxying with Traefik

This is an attempt at using TLS-SNI to provide name-based routing to independent FTP servers.

Currently it fails because, while Traefik connects to the FTP server and passes data, TLS is terminated in Traefik and the FTP server requests TLS again using 421 TLS is required, confusing the client because it's already made a TLS connection.

Not requiring TLS on the FTP server causes data-transfer connections to fail when receiving the PASV command, because the FTP server expects an unencrypted data connection, whereas the client thinks it's connected via TLS and attempts to use TLS for both the control and data connections.

Attempting to switch from TLS-termination to TLS-passthrough in Traefik results in the FTP server failing because it wasn't expecting TLS to already be set up. FTP has protocol-specific requirements for upgrading from a plaintext to TLS connection.

# This is hot garbage, but it shows that FTPS routing can be done using Traefik.
# Generate TLS key+cert using the following commands
# $ openssl req -nodes -x509 -newkey rsa:4096 -keyout ftp1-key.pem -out ftp1-cert.pem -days 9000
# $ openssl req -nodes -x509 -newkey rsa:4096 -keyout ftp2-key.pem -out ftp2-cert.pem -days 9000
version: "3.7"
services:
ingress:
image: library/traefik:2.3.3
ports:
- 8080:8080
- 8021:8021
volumes:
- ./traefik.yaml:/etc/traefik/traefik.yaml
- ./traefik-dynamic.yaml:/etc/traefik/traefik-dynamic.yaml
ftp1:
image: drakkan/sftpgo:v1.2.2-alpine
command: ["sftpgo", "serve", "--config-dir", "/", "--config-file", "config"]
volumes:
- ./ftp1.yaml:/config.yaml
- ./ftp-auth.sh:/ftp-auth.sh
- ./ftp1:/srv
- ./ftp1-cert.pem:/cert.pem
- ./ftp1-key.pem:/key.pem
networks:
default:
# @see https://github.com/drakkan/sftpgo/blob/master/docs/full-configuration.md
sftpd:
host_keys:
- "/tmp/id_rsa"
ftpd:
bind_port: 8021
force_passive_ip: "127.0.0.1"
passive_port_range:
start: 8026
end: 8030
certificate_file: "/cert.pem"
certificate_key_file: "/key.pem"
tls_mode: 1 # Force TLS connections
data_provider:
driver: "memory"
name: ""
external_auth_hook: /ftp-auth.sh
external_auth_scope: 0
credentials_path: /tmp
tcp:
services:
ftp1:
loadBalancer:
servers:
-
address: "ftp1:8021"
routers:
ftp1:
rule: "HostSNI(`ftp1.local`)"
entryPoints:
- "ftp"
- "ftp-pasv1"
- "ftp-pasv2"
- "ftp-pasv3"
- "ftp-pasv4"
- "ftp-pasv5"
service: "ftp1"
tls: {}
api:
dashboard: true
insecure: true
entryPoints:
ftp:
address: ":8021"
ftp-pasv1:
address: ":8026"
ftp-pasv2:
address: ":8027"
ftp-pasv3:
address: ":8028"
ftp-pasv4:
address: ":8029"
ftp-pasv5:
address: ":8030"
providers:
file:
filename: "/etc/traefik/traefik-dynamic.yaml"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment