From 20a99aea9c05c5843cebdb344c7ded7d5cf802e4 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 1 Nov 2025 21:31:20 +0100 Subject: [PATCH] fix ftp proxy (vip) --- .env.example | 70 ++++++++++++++++++++++++++ docker-compose.example.yml | 80 ++++++++++++++++++++++++++++++ src/ftp_csv_receiver.py | 12 ++++- src/utils/config/loader_ftp_csv.py | 25 ++++++---- 4 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 .env.example create mode 100644 docker-compose.example.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..33ae928 --- /dev/null +++ b/.env.example @@ -0,0 +1,70 @@ +# ASE Application - Environment Variables +# Copia questo file in .env e modifica i valori secondo le tue necessità + +# ============================================================================ +# FTP Server Configuration +# ============================================================================ + +# Porta iniziale del range di porte passive FTP +# Il range completo sarà FTP_PASSIVE_PORTS to (FTP_PASSIVE_PORTS + portRangeWidth - 1) +# Default: valore da ftp.ini +FTP_PASSIVE_PORTS=60000 + +# IP esterno da pubblicizzare ai client FTP (importante per HA con VIP) +# Questo è l'indirizzo che i client useranno per connettersi in modalità passiva +# In un setup HA, questo dovrebbe essere il VIP condiviso tra le istanze +# Default: valore da ftp.ini +FTP_EXTERNAL_IP=192.168.1.100 + +# ============================================================================ +# Database Configuration +# ============================================================================ + +# Hostname del server MySQL +# Default: valore da db.ini +DB_HOST=localhost + +# Porta del server MySQL +# Default: valore da db.ini +DB_PORT=3306 + +# Username per la connessione al database +# Default: valore da db.ini +DB_USER=ase_user + +# Password per la connessione al database +# Default: valore da db.ini +DB_PASSWORD=your_secure_password + +# Nome del database +# Default: valore da db.ini +DB_NAME=ase_lar + +# ============================================================================ +# Logging Configuration +# ============================================================================ + +# Livello di logging: DEBUG, INFO, WARNING, ERROR, CRITICAL +# Default: INFO +LOG_LEVEL=INFO + +# ============================================================================ +# Note per Docker Compose +# ============================================================================ +# +# 1. Le variabili d'ambiente OVERRIDE i valori nei file .ini +# 2. Se una variabile non è impostata, viene usato il valore dal file .ini +# 3. Questo permette deployment flessibili senza modificare i file .ini +# +# Esempio di uso in docker-compose.yml: +# +# environment: +# FTP_PASSIVE_PORTS: "${FTP_PASSIVE_PORTS:-60000}" +# FTP_EXTERNAL_IP: "${FTP_EXTERNAL_IP}" +# DB_HOST: "${DB_HOST}" +# DB_PASSWORD: "${DB_PASSWORD}" +# +# Oppure usando env_file: +# +# env_file: +# - .env diff --git a/docker-compose.example.yml b/docker-compose.example.yml new file mode 100644 index 0000000..be86025 --- /dev/null +++ b/docker-compose.example.yml @@ -0,0 +1,80 @@ +version: '3.8' + +services: + ftp-server: + build: . + container_name: ase-ftp-server + ports: + - "2121:2121" # FTP control port + - "60000-60099:60000-60099" # FTP passive ports range + environment: + # FTP Configuration + FTP_PASSIVE_PORTS: "60000" # Prima porta del range passivo + FTP_EXTERNAL_IP: "192.168.1.100" # IP esterno/VIP da pubblicizzare ai client + + # Database Configuration + DB_HOST: "mysql-server" + DB_PORT: "3306" + DB_USER: "ase_user" + DB_PASSWORD: "your_secure_password" + DB_NAME: "ase_lar" + + # Logging (opzionale) + LOG_LEVEL: "INFO" + volumes: + - ./logs:/app/logs + - ./data:/app/data + depends_on: + - mysql-server + restart: unless-stopped + networks: + - ase-network + + # Esempio di setup HA con più istanze FTP + ftp-server-2: + build: . + container_name: ase-ftp-server-2 + ports: + - "2122:2121" # Diversa porta di controllo per seconda istanza + - "61000-61099:60000-60099" # Diverso range passivo + environment: + FTP_PASSIVE_PORTS: "60000" # Stessa config, ma mappata su porte diverse dell'host + FTP_EXTERNAL_IP: "192.168.1.100" # Stesso VIP condiviso + DB_HOST: "mysql-server" + DB_PORT: "3306" + DB_USER: "ase_user" + DB_PASSWORD: "your_secure_password" + DB_NAME: "ase_lar" + LOG_LEVEL: "INFO" + volumes: + - ./logs2:/app/logs + - ./data:/app/data + depends_on: + - mysql-server + restart: unless-stopped + networks: + - ase-network + + mysql-server: + image: mysql:8.0 + container_name: ase-mysql + environment: + MYSQL_ROOT_PASSWORD: "root_password" + MYSQL_DATABASE: "ase_lar" + MYSQL_USER: "ase_user" + MYSQL_PASSWORD: "your_secure_password" + ports: + - "3306:3306" + volumes: + - mysql-data:/var/lib/mysql + - ./dbddl:/docker-entrypoint-initdb.d + restart: unless-stopped + networks: + - ase-network + +networks: + ase-network: + driver: bridge + +volumes: + mysql-data: diff --git a/src/ftp_csv_receiver.py b/src/ftp_csv_receiver.py index 6fd0050..1d97d55 100755 --- a/src/ftp_csv_receiver.py +++ b/src/ftp_csv_receiver.py @@ -155,13 +155,23 @@ def main(): handler = ASEHandler handler.cfg = cfg handler.authorizer = authorizer - handler.masquerade_address = cfg.proxyaddr + + # Set masquerade address only if configured (importante per HA con VIP) + # Questo è l'IP che il server FTP pubblicherà ai client per le connessioni passive + if cfg.proxyaddr and cfg.proxyaddr.strip(): + handler.masquerade_address = cfg.proxyaddr + logger.info(f"FTP masquerade address configured: {cfg.proxyaddr}") + else: + logger.info("FTP masquerade address not configured - using server's default IP") # Set the range of passive ports for the FTP server _range = list(range(cfg.firstport, cfg.firstport + cfg.portrangewidth)) handler.passive_ports = _range + # Log configuration logger.info(f"Starting FTP server on port {cfg.service_port} with DatabaseAuthorizer") + logger.info(f"FTP passive ports range: {cfg.firstport}-{cfg.firstport + cfg.portrangewidth - 1}") + logger.info(f"Database connection: {cfg.dbuser}@{cfg.dbhost}:{cfg.dbport}/{cfg.dbname}") # Create and start the FTP server server = FTPServer(("0.0.0.0", cfg.service_port), handler) diff --git a/src/utils/config/loader_ftp_csv.py b/src/utils/config/loader_ftp_csv.py index 0c5c767..41b63e9 100644 --- a/src/utils/config/loader_ftp_csv.py +++ b/src/utils/config/loader_ftp_csv.py @@ -1,5 +1,6 @@ """set configurations""" +import os from configparser import ConfigParser from . import ENV_PARENT_PATH @@ -10,15 +11,21 @@ class Config: """ Initializes the Config class by reading configuration files. It loads settings from 'ftp.ini' and 'db.ini' for FTP server, CSV, logging, and database. + Environment variables override INI file settings for Docker deployments. """ c = ConfigParser() c.read([f"{ENV_PARENT_PATH}/env/ftp.ini", f"{ENV_PARENT_PATH}/env/db.ini"]) - # FTP setting + # FTP setting (with environment variable override for Docker) self.service_port = c.getint("ftpserver", "service_port") - self.firstport = c.getint("ftpserver", "firstPort") - self.proxyaddr = c.get("ftpserver", "proxyAddr") + + # FTP_PASSIVE_PORTS: override della porta iniziale del range passivo + self.firstport = int(os.getenv("FTP_PASSIVE_PORTS", c.getint("ftpserver", "firstPort"))) + + # FTP_EXTERNAL_IP: override dell'IP pubblicizzato (VIP per HA) + self.proxyaddr = os.getenv("FTP_EXTERNAL_IP", c.get("ftpserver", "proxyAddr")) + self.portrangewidth = c.getint("ftpserver", "portRangeWidth") self.virtpath = c.get("ftpserver", "virtpath") self.adminuser = c.get("ftpserver", "adminuser").split("|") @@ -33,12 +40,12 @@ class Config: # LOG setting self.logfilename = c.get("logging", "logFilename") - # DB setting - self.dbhost = c.get("db", "hostname") - self.dbport = c.getint("db", "port") - self.dbuser = c.get("db", "user") - self.dbpass = c.get("db", "password") - self.dbname = c.get("db", "dbName") + # DB setting (with environment variable override for Docker) + self.dbhost = os.getenv("DB_HOST", c.get("db", "hostname")) + self.dbport = int(os.getenv("DB_PORT", c.getint("db", "port"))) + self.dbuser = os.getenv("DB_USER", c.get("db", "user")) + self.dbpass = os.getenv("DB_PASSWORD", c.get("db", "password")) + self.dbname = os.getenv("DB_NAME", c.get("db", "dbName")) self.max_retries = c.getint("db", "maxRetries") # Tables