Una implementación personalizada del Protocolo de Transferencia de Archivos (FTP) en Java que incluye un servidor multihilo con control de acceso basado en roles (RBAC), un cliente GUI de estilo retro y un modo de consola interactivo construido sobre Apache Commons Net.
- Resumen
- Características
- Arquitectura
- Prerrequisitos
- Instalación
- Compilación y ejecución
- Inicio Rápido
- Uso
- Configuración
- Modos de Transferencia FTP
- Control de Acceso Basado en Roles
- Seguridad
- Despliegue en producción
- Solución de Problemas
- Contribuciones
- Licencia
- Agradecimientos
Este proyecto es una implementación educativa completa del Protocolo de Transferencia de Archivos (FTP) en Java, construido desde cero para demostrar:
- Programación de Red: Programación de sockets, multihilo y arquitectura cliente-servidor.
- Implementación de Protocolos: Manejo de comandos/respuestas FTP conforme al RFC 959.
- Sistemas Concurrentes: Gestión de un pool de hilos para múltiples conexiones simultáneas.
- Desarrollo de GUI: Interfaz retro basada en Swing con actualizaciones en tiempo real.
- Patrones de Seguridad: Control de acceso basado en roles (RBAC) y sistemas de autenticación.
Cliente FTP con interfaz gráfica estilo terminal retro ámbar
| Componente | Archivo | Descripción |
|---|---|---|
| Servidor | JavaFtpServer.java | Servidor FTP multihilo que soporta los modos de transferencia ACTIVO y PASIVO. |
| Cliente de Consola | JavaFtpClient.java | Cliente FTP interactivo de línea de comandos usando Apache Commons Net. |
| Cliente GUI | ClientGUI.java | Interfaz gráfica de estilo terminal retro ámbar para operaciones FTP. |
| Panel Admin | AdminGUI.java | Aplicación Swing para gestionar usuarios en la base SQLite (añadir, editar, activar/desactivar). |
| Dependencia | commons-net-3.11.1.jar |
Biblioteca Apache Commons Net (incluida en lib/). |
Al estudiar y ejecutar este proyecto, entenderás:
✓ Cómo funciona el protocolo FTP a nivel de red (canales de control y de datos). ✓ Cómo implementar el análisis de comandos y los códigos de respuesta del lado del servidor. ✓ Cómo gestionar conexiones de clientes concurrentes con pools de hilos. ✓ Cómo manejar los modos de transferencia de datos ACTIVO y PASIVO. ✓ Cómo construir sistemas de permisos basados en roles. ✓ Cómo crear aplicaciones de escritorio con GUI usando Java Swing. ✓ Cómo trabajar con E/S de archivos y flujos de red.
- Conexiones Concurrentes: Arquitectura multihilo que utiliza
ExecutorServicepara manejar múltiples clientes simultáneos. - Autenticación de Usuarios: Soporte para SQLite (recomendado) o fichero TXT; contraseñas con hash bcrypt. Gestión de usuarios con el panel Admin o la herramienta PasswordTool (modo fichero).
- Control de Acceso Basado en Roles (RBAC): Sistema de permisos de tres niveles:
BASICO— Acceso de solo lectura (LIST, RETR, CWD, PWD).INTERMEDIO— Acceso de lectura/escritura (añade STOR, MKD, RMD, RNFR, RNTO, DELE).ADMINISTRADOR— Acceso administrativo completo.
- Modos de Transferencia Duales:
- Modo PASIVO — El servidor abre un puerto de datos y el cliente se conecta (compatible con NAT/firewall).
- Modo ACTIVO — El cliente abre un puerto de datos y el servidor se conecta (requiere redirección de puertos en el cliente).
- Soporte de Comandos FTP: USER, PASS, SYST, FEAT, OPTS UTF8, NOOP, PASV, PORT, LIST, STOR, RETR, SIZE, MDTM, DELE, MKD, RMD, RNFR, RNTO, CWD, CDUP, PWD, QUIT.
- Menú interactivo con 10 operaciones FTP.
- Configuración de conexión de host/puerto personalizable.
- Selección del modo de transferencia (PASIVO/ACTIVO).
- Feedback de las operaciones en tiempo real.
- Diseño Retro: Estética de terminal ámbar inspirada en los años 80.
- Tabla de Archivos: Visualización con tipo de archivo, nombre, tamaño y fecha de modificación.
- Botones Dedicados: Acceso con un clic a 10 operaciones FTP (REFRESH, UPLOAD, DOWNLOAD, DELETE, MKD, RMD, RENAME, CD, CDUP, PWD).
- Registro en Tiempo Real: Mensajes codificados por colores (verde=éxito, rojo=error, amarillo=info).
- Tipografía Monoespaciada: Fuente Consolas para una apariencia retro auténtica.
- Barras de Progreso: Indicadores visuales en tiempo real para transferencias de archivos.
- Selección Múltiple: Operaciones batch con Ctrl+Click o Shift+Click.
- Navegación Intuitiva: Doble clic en directorios para navegar.
- Encoding UTF-8: Soporte completo para nombres con acentos, ñ y caracteres especiales.
- Transferencia Binaria: Configuración automática para archivos PDF, ZIP, imágenes y binarios.
java-ftp/
├── src/
│ └── FTP/
│ ├── Client/
│ │ ├── JavaFtpClient.java # Punto de entrada del cliente de consola
│ │ ├── ClientFunctions.java # Implementaciones de comandos del cliente
│ │ └── ClientGUI.java # Cliente GUI (interfaz retro)
│ ├── Server/
│ │ ├── JavaFtpServer.java # Punto de entrada del servidor
│ │ ├── FtpClientHandler.java # Manejador por conexión (Runnable)
│ │ ├── ServerFunctions.java # Implementaciones de comandos FTP
│ │ ├── User.java # Modelo de credenciales de usuario
│ │ └── UserProfile.java # Enum para RBAC
│ ├── Admin/
│ │ └── AdminGUI.java # Panel de administración de usuarios (SQLite)
│ └── Util/
│ └── Util.java # Utilidades compartidas
├── lib/
│ └── commons-net-3.11.1.jar # Biblioteca Apache Commons Net
├── files/
│ └── users/
│ └── users.txt # Usuarios (TXT) o usar SQLite (ftp_users.db) vía server.properties
├── bin/ # Clases compiladas (generado)
├── [LICENSE](LICENSE)
└── [README.md](README.md)
- JDK: 8 o superior.
- Shell: PowerShell (Windows) o Bash (Linux/macOS).
- Red: El puerto 21 debe estar disponible (o configurar
ftp.control.port=2121enserver.propertiespara usar un puerto no privilegiado).
Antes de la instalación, verifica tu entorno:
# Comprobar la versión de Java (debe ser 8+)
java -version
javac -version
# Verificar la estructura del proyecto
ls lib/commons-net-3.11.1.jar # Debería existirgit clone <url-del-repositorio>
cd java-ftpCrea los directorios necesarios:
# Windows (PowerShell)
mkdir -Force bin, files\users
# Linux/macOS
mkdir -p bin files/usersPuedes usar SQLite (recomendado) o fichero TXT. No crees users.txt a mano con contraseñas en claro: el servidor espera hashes bcrypt. La forma más sencilla es:
- SQLite: Ver Primera vez: no tengo usuarios (Opción B): panel Admin +
ftp.users.databaseenserver.properties. - TXT: En la misma sección, Opción A:
PasswordTool addusercrea el fichero con hash. Luego enserver.propertiesdejaftp.users.database=vacío y ponftp.root.directory=files.
Perfiles: BASICO (solo lectura), INTERMEDIO (lectura/escritura), ADMINISTRADOR (acceso completo).
Compila una vez con Maven o javac. Los comandos exactos para compilar y para ejecutar servidor, cliente (GUI y consola) y panel Admin están en la sección Compilación y ejecución.
chmod +x run-client-gui.shAhora estás listo para ejecutar el servidor y el cliente. Procede a Compilación y ejecución para los comandos exactos, o a Inicio Rápido.
Desde la raíz del proyecto (java-ftp/). En Windows usa ; en el classpath; en Linux/macOS usa :.
Tienes dos opciones. Elige una.
Opción A – Usar fichero TXT (más rápido)
- Compila (una vez):
mvn compileo eljavacde abajo. - Crea el primer usuario; el fichero y la carpeta se crean solos:
- Windows:
java -cp "bin;lib\*" FTP.Server.PasswordTool adduser admin tuPassword ADMINISTRADOR files/users/users.txt - Linux/macOS:
java -cp "bin:lib/*" FTP.Server.PasswordTool adduser admin tuPassword ADMINISTRADOR files/users/users.txt
- Windows:
- En
server.propertiesdejaftp.users.database=vacío y ponftp.root.directory=files(o la ruta que quieras). - Arranca el servidor y conecta con usuario
adminy contraseñatuPassword.
Opción B – Usar SQLite (recomendado)
- Compila (una vez):
mvn compileo eljavacde abajo. - Arranca el panel Admin:
- Windows:
java -cp "bin;lib\*" FTP.Admin.AdminGUI - Linux/macOS:
java -cp "bin:lib/*" FTP.Admin.AdminGUI
- Windows:
- En el panel: en el campo de ruta escribe
files/ftp_users.dby pulsa Cargar / Abrir. La base y la tabla se crean solas. (Si la carpetafilesno existe, créala antes:mkdir files.) - Pulsa Añadir usuario: nombre
admin, contraseña la que quieras, perfil ADMINISTRADOR. Guardar. - En
server.propertiesponftp.users.database=files/ftp_users.dbyftp.root.directory=files(o la ruta que quieras). - Arranca el servidor y conecta con ese usuario.
Opción A: Maven (descarga dependencias a ~/.m2 la primera vez)
mvn compileOpción B: javac con los JAR de lib/ (no descarga nada; útil si ya tienes lib/ llena)
Windows (PowerShell):
if (-not (Test-Path bin)) { New-Item -ItemType Directory -Path bin }
javac -d bin -cp "lib\*" -encoding UTF-8 src\FTP\Client\*.java src\FTP\Server\*.java src\FTP\Util\*.java src\FTP\Admin\*.javaLinux/macOS:
mkdir -p bin
javac -d bin -cp "lib/*" -encoding UTF-8 src/FTP/Client/*.java src/FTP/Server/*.java src/FTP/Util/*.java src/FTP/Admin/*.javaWindows:
java -cp "bin;lib\*" FTP.Server.JavaFtpServerLinux/macOS:
java -cp "bin:lib/*" FTP.Server.JavaFtpServerSi tienes server.properties con ftp.root.directory y usuarios configurados, el servidor arranca sin preguntar nada.
Windows:
java -cp "bin;lib\*" FTP.Client.ClientGUILinux/macOS:
java -cp "bin:lib/*" FTP.Client.ClientGUIScripts de conveniencia: run-client-gui.bat (Windows) o ./run-client-gui.sh (Linux/macOS).
Windows:
java -cp "bin;lib\*" FTP.Client.JavaFtpClientLinux/macOS:
java -cp "bin:lib/*" FTP.Client.JavaFtpClientGestiona usuarios en la base SQLite (añadir, editar, activar/desactivar). No hace falta haber arrancado el servidor antes: si la base no existe, se crea al cargarla y la tabla se crea automáticamente.
Windows:
java -cp "bin;lib\*" FTP.Admin.AdminGUILinux/macOS:
java -cp "bin:lib/*" FTP.Admin.AdminGUIScripts: run-admin-gui.bat (Windows) o ./run-admin-gui.sh (Linux/macOS). En la ventana, escribe la ruta del fichero de base de datos (ej. files/ftp_users.db) y pulsa Cargar / Abrir; si el fichero no existe, SQLite lo crea y el panel crea la tabla. Luego usa Añadir usuario para el primer usuario.
Si ya tienes files/users/users.txt y quieres pasar a SQLite: crea la base con el panel Admin (Cargar files/ftp_users.db) y luego ejecuta:
- Windows:
java -cp "bin;lib\*" FTP.Server.MigrateUsersToDb files/users/users.txt files/ftp_users.db - Linux/macOS:
java -cp "bin:lib/*" FTP.Server.MigrateUsersToDb files/users/users.txt files/ftp_users.db
💡 ¿Primera vez? Consulta QUICK_START.md para una guía visual paso a paso.
Sigue estos pasos para poner en marcha el servidor y el cliente FTP:
Necesitas al menos un usuario y el directorio raíz configurados. Dos opciones:
- Opción rápida (SQLite): Crea la carpeta
files, compila (Paso 2), ejecuta el panel Admin (java -cp "bin;lib\*" FTP.Admin.AdminGUIen Windows obin:lib/*en Linux/macOS), cargafiles/ftp_users.db, añade usuarioadmin. Enserver.propertiesponftp.root.directory=filesyftp.users.database=files/ftp_users.db. En Windows usa/en las rutas. - Opción TXT: Compila, ejecuta
PasswordTool adduser admin tuPassword ADMINISTRADOR files/users/users.txt(ver Primera vez: no tengo usuarios), y enserver.propertiesponftp.root.directory=filesy dejaftp.users.database=vacío.
Así el servidor arrancará sin pedir el directorio raíz y aceptará el login.
# Windows (PowerShell)
mkdir -Force bin
javac -d bin -cp "lib\*" -encoding UTF-8 src\FTP\Client\*.java src\FTP\Server\*.java src\FTP\Util\*.java src\FTP\Admin\*.java# Linux/macOS
mkdir -p bin
javac -d bin -cp "lib/*" -encoding UTF-8 src/FTP/Client/*.java src/FTP/Server/*.java src/FTP/Util/*.java src/FTP/Admin/*.javaResultado esperado: Sin errores. Los archivos .class estarán en bin/. Para más opciones (Maven, ejecutar servidor/cliente/Admin), ver Compilación y ejecución.
Abre una ventana de terminal y ejecuta:
# Windows (PowerShell)
java -cp "bin;lib\*" FTP.Server.JavaFtpServer# Linux/macOS (puede requerir sudo para el puerto 21)
java -cp "bin:lib/*" FTP.Server.JavaFtpServerSi en server.properties tienes ftp.root.directory definido (ej. files), el servidor arranca sin preguntar. Si no, te pedirá el directorio raíz (ej. files). Mantén esta terminal abierta.
Abre una nueva ventana de terminal (mantén el servidor en ejecución) y elige una de las siguientes opciones:
# Windows
java -cp "bin;lib\*" FTP.Client.ClientGUI
# o: .\run-client-gui.bat# Linux/macOS
java -cp "bin:lib/*" FTP.Client.ClientGUI
# o: chmod +x run-client-gui.sh && ./run-client-gui.shEn la ventana de la GUI:
- Host:
localhost - Puerto:
21 - Usuario:
admin - Contraseña: la que definiste al crear el usuario (PasswordTool o panel Admin)
- Modo: Selecciona
PASSIVE - Use FTPS (TLS): déjalo desmarcado salvo que hayas configurado TLS en el servidor (ver Habilitar FTPS (TLS))
- Haz clic en "▶ CONNECT"
Deberías ver un mensaje de éxito en verde y la lista de archivos se cargará.
# Windows
java -cp "bin;lib\*" FTP.Client.JavaFtpClient# Linux/macOS
java -cp "bin:lib/*" FTP.Client.JavaFtpClientCuando se te solicite:
- Servidor:
localhost(pulsa Enter) - Puerto:
21(pulsa Enter) - Usuario:
admin - Contraseña: la que configuraste para ese usuario
- Modo:
1(para PASIVO)
Deberías ver:
- Terminal del servidor:
✓ Usuario 'admin' ha iniciado sesión correctamente - Cliente: Listado de archivos o menú interactivo.
| Problema | Solución |
|---|---|
Address already in use |
Puerto 21 ocupado. En server.properties pon ftp.control.port=2121. |
Permission denied (Linux/macOS) |
Usa sudo o ftp.control.port=2121 en server.properties. |
ClassNotFoundException |
Usa classpath bin;lib\* (Windows) o bin:lib/* (Linux/macOS). Ver Compilación y ejecución. |
Connection refused |
Servidor no está en marcha o firewall/puerto incorrecto. |
| Servidor pide directorio raíz | Define ftp.root.directory en server.properties (en Windows usa / en la ruta). |
| Error "Archivo de usuarios no existe" | Define ftp.users.database para SQLite o crea files/users/users.txt; en Windows usa / en rutas. |
| FTPS no conecta | Configura TLS en el servidor: Habilitar FTPS (TLS). |
- Sube un archivo usando la opción
[2]en la consola o el botón UPLOAD en la GUI. - Navega por los directorios con
[8]o haciendo doble clic en las carpetas en la GUI. - Prueba diferentes cuentas de usuario con distintos niveles de permiso.
- Lee la sección de Uso para operaciones detalladas.
Los comandos con classpath completo están en Compilación y ejecución. Resumen:
Windows: java -cp "bin;lib\*" FTP.Server.JavaFtpServer
Linux/macOS: java -cp "bin:lib/*" FTP.Server.JavaFtpServer (puerto 21 puede requerir sudo; o configura ftp.control.port=2121 en server.properties).
┌─────────────────────────────────────┐
│ 1. Visualización del Banner ASCII │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ 2. Petición: Directorio raíz │
│ Entrada: files │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ 3. Validar Estructura │
│ ✓ Directorio raíz existe │
│ ✓ Usuarios: SQLite o TXT │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ 4. Iniciar Escucha │
│ Servidor escuchando en puerto 21 │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ 5. Listo para Conexiones │
│ [LOG] Esperando clientes... │
└─────────────────────────────────────┘
✓ Servidor escuchando en el puerto 21 # Servidor iniciado correctamente
✓ Cliente conectado: /127.0.0.1:54321 # Nueva conexión de cliente
✓ Usuario 'admin' ha iniciado sesión # Autenticación exitosa
⇄ LIST: /files/ # Solicitud de listado de archivos
⇄ RETR: document.txt # Descarga de archivo
⇄ STOR: upload.jpg # Carga de archivo
✗ Acceso denegado para el usuario 'alice' # Error de permisos
✓ Cliente desconectado: admin # Cliente cerró sesión
Para uso en producción (ver Seguridad):
-
Cambiar puerto (evitar privilegios en Unix): en
server.propertiesponftp.control.port=2121. -
Ejecutar como Servicio del Sistema:
# Crear un servicio systemd (Linux) sudo nano /etc/systemd/system/ftp-server.service -
Configurar el Firewall:
# Permitir puertos FTP sudo ufw allow 21/tcp sudo ufw allow 20/tcp sudo ufw allow 1024:65535/tcp # Puertos para modo pasivo
Nota: En Unix el puerto 21 requiere root/sudo. Para desarrollo, configura ftp.control.port=2121 en server.properties.
Comandos completos en Compilación y ejecución.
Windows: java -cp "bin;lib\*" FTP.Client.JavaFtpClient
Linux/macOS: java -cp "bin:lib/*" FTP.Client.JavaFtpClient
- Introduce el nombre de host (por defecto:
localhost). - Introduce el puerto (por defecto:
21). - Proporciona nombre de usuario y contraseña.
- Selecciona el modo de transferencia (
PASSIVEoACTIVE).
[1] Listar archivos
[2] Subir archivo
[3] Descargar archivo
[4] Eliminar archivo
[5] Crear directorio
[6] Eliminar directorio
[7] Renombrar archivo/directorio
[8] Cambiar directorio de trabajo
[9] Cambiar al directorio padre
[10] Salir
Windows: java -cp "bin;lib\*" FTP.Client.ClientGUI (o run-client-gui.bat)
Linux/macOS: java -cp "bin:lib/*" FTP.Client.ClientGUI (o ./run-client-gui.sh)
- Rellena los campos: Host, Puerto, Usuario, Contraseña.
- Selecciona el modo (PASSIVE/ACTIVE).
- Haz clic en "▶ CONNECT".
- Navega por los archivos usando la tabla y los botones de operación.
El servidor se configura mediante el archivo server.properties en la raíz del proyecto.
En server.properties, usa siempre / (barra normal) en las rutas, nunca \. En Java, el archivo de propiedades trata \ como carácter de escape, por lo que C:\Users\... se lee mal. Ejemplo correcto:
ftp.root.directory=C:/Users/elija/Desktop/servidorFTP
ftp.users.database=files/ftp_users.db| Propiedad | Descripción |
|---|---|
ftp.control.port |
Puerto de control (por defecto 21). |
ftp.root.directory |
Directorio raíz del servidor. Si está definido, el servidor no pide el root al arrancar. |
ftp.users.database |
Ruta a la base SQLite de usuarios. Si está definida, se usa SQLite y se ignora ftp.users.file. |
ftp.users.file |
Ruta al fichero TXT de usuarios (solo se usa si ftp.users.database está vacío). |
Para que el cliente pueda conectar con «Use FTPS (TLS)» marcado, el servidor debe tener un keystore y TLS habilitado en server.properties.
Dónde ejecutar todo: En la carpeta del proyecto (donde está server.properties y desde donde arrancas el servidor con java -cp ...). No uses el directorio raíz FTP (ftp.root.directory): ese es solo donde se sirven los archivos; el keystore y la configuración van en la carpeta del proyecto. La ruta ftp.tls.keystore.path en server.properties es relativa al directorio de trabajo al iniciar el servidor.
Resumen de pasos:
-
Crear el keystore (una vez). Ejecuta en la carpeta del proyecto:
Windows (PowerShell):
keytool -genkeypair -alias ftp -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore ftp.p12 -validity 3650 -storepass changeit -dname "CN=FTP Server, O=Dev, L=Local, C=ES"
Linux/macOS:
keytool -genkeypair -alias ftp -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore ftp.p12 -validity 3650 -storepass changeit -dname "CN=FTP Server, O=Dev, L=Local, C=ES"Se crea
ftp.p12en la carpeta del proyecto. Contraseña del keystore:changeit. -
Configurar en
server.properties. El proyecto incluye ya estas líneas (si no, añádelas):ftp.tls.enabled=true ftp.tls.keystore.path=ftp.p12 ftp.tls.keystore.password=changeit ftp.tls.required=false
Si guardas el keystore en otra ruta, actualiza
ftp.tls.keystore.path(en Windows usa/, no\). -
Reiniciar el servidor. A partir de entonces, cuando el cliente marque «Use FTPS (TLS)», el servidor aceptará
AUTH TLSy hará el handshake correctamente.
Sobre ftp.tls.required: Con false (por defecto) las conexiones sin TLS siguen funcionando. Si quieres forzar que todo sea cifrado, pon ftp.tls.required=true.
El servidor puede usar SQLite (recomendado) o un fichero TXT para la base de usuarios.
En server.properties define la ruta de la base de datos:
ftp.users.database=files/ftp_users.dbSi el fichero no existe, el servidor crea la base y la tabla al iniciar. Los usuarios tienen un flag enabled (activar/desactivar sin borrar).
-
Migración desde el fichero TXT:
java -cp "bin:lib/*" FTP.Server.MigrateUsersToDb files/users/users.txt files/ftp_users.db(Windows:bin;lib\*). El segundo argumento es opcional. -
Panel de administración: ejecuta
java -cp "bin:lib/*" FTP.Admin.AdminGUI(Windows:bin;lib\*) o los scriptsrun-admin-gui.sh/run-admin-gui.bat. El panel leeftp.users.databasedeserver.propertiessi existe; si no, indica la ruta del.dben la ventana.
Si ftp.users.database está vacío o no definido, el servidor usa el fichero de usuarios:
# Dejar ftp.users.database vacío y definir:
ftp.users.file=files/users/users.txtFormato de files/users/users.txt:
nombre_usuario:hash_bcrypt:PERFIL
Perfiles: BASICO, INTERMEDIO, ADMINISTRADOR
Para generar hashes sin escribir contraseñas en claro, usa PasswordTool:
java -cp "bin:lib/*" FTP.Server.PasswordTool adduser miUsuario miPassword ADMINISTRADOR files/users/users.txt (Windows: bin;lib\*).
Ejemplo de usuarios (TXT):
alice:hash_alice:BASICO
bob:hash_bob:INTERMEDIO
admin:hash_admin:ADMINISTRADOR
- Cuándo usarlo: Cliente detrás de un NAT/firewall.
- Cómo funciona:
- El cliente envía el comando
PASV. - El servidor abre un puerto aleatorio y envía los detalles de conexión.
- El cliente se conecta a ese puerto para la transferencia de datos.
- El cliente envía el comando
- Configuración: Manejado automáticamente por el cliente.
- Cuándo usarlo: Cliente con IP pública o redirección de puertos adecuada.
- Cómo funciona:
- El cliente abre un puerto local y envía el comando
PORTcon los detalles de conexión. - El servidor se conecta al puerto especificado por el cliente para la transferencia de datos.
- El cliente abre un puerto local y envía el comando
- Configuración: El cliente solicita un puerto de datos (se recomienda >5000).
| Comando | BASICO | INTERMEDIO | ADMINISTRADOR |
|---|---|---|---|
| LIST | ✓ | ✓ | ✓ |
| RETR | ✓ | ✓ | ✓ |
| SIZE | ✓ | ✓ | ✓ |
| MDTM | ✓ | ✓ | ✓ |
| CWD | ✓ | ✓ | ✓ |
| CDUP | ✓ | ✓ | ✓ |
| PWD | ✓ | ✓ | ✓ |
| STOR | ✗ | ✓ | ✓ |
| DELE | ✗ | ✓ | ✓ |
| MKD | ✗ | ✗ | ✓ |
| RMD | ✗ | ✗ | ✓ |
| RNFR | ✗ | ✗ | ✓ |
| RNTO | ✗ | ✗ | ✓ |
Los comandos no autorizados devuelven: 530 Not logged in o el código de error apropiado.
El servidor incluye medidas pensadas para uso en producción:
- Contraseñas: Almacenamiento con hash bcrypt (formato
username:bcryptHash:profileen el fichero de usuarios). Uso de la herramientaPasswordToolpara dar de alta usuarios sin escribir contraseñas en claro. - FTPS: Cifrado TLS opcional en canal de control (AUTH TLS) y de datos (PROT P). Configuración vía
server.properties(keystore PKCS12). - Path traversal: Validación de rutas con
resolveAndValidatePathy rechazo explícito de..y rutas absolutas en CWD. - Comando PORT: Validación anti-SSRF (la IP en PORT debe coincidir con la del cliente).
- Rate limiting: Límite de intentos de login fallidos por IP (
ftp.auth.max.attempts,ftp.auth.lockout.minutes). - Auditoría: Registro de autenticaciones, comandos sensibles (STOR, DELE, RMD, MKD, RNFR/RNTO) y rechazos (permisos, path).
- Rotación de logs: Por tamaño (
ftp.log.max.size.bytes,ftp.log.max.backups).
Para entornos muy sensibles se recomienda además: FTPS obligatorio (ftp.tls.required=true), firewall restringido al puerto de control y al rango pasivo, y revisión periódica de logs.
Para ejecutar el servidor como servicio sin interacción por consola:
-
Configuración completa en
server.propertiesftp.root.directory: directorio raíz absoluto (ej./var/ftp; en Windows usar/en la ruta).ftp.users.database(SQLite) oftp.users.file(TXT). Si están definidos, el servidor arranca sin preguntar (modo daemon).
-
Crear usuarios: con SQLite usa el panel Admin; con TXT usa PasswordTool:
java -cp "bin:lib/*" FTP.Server.PasswordTool adduser miUsuario miPassword ADMINISTRADOR files/users/users.txt -
TLS (opcional)
- Ver Habilitar FTPS (TLS) para generar keystore y configurar
server.properties.
- Ver Habilitar FTPS (TLS) para generar keystore y configurar
-
Firewall
- Abrir solo el puerto de control (ej. 21 o 2121) y el rango de puertos pasivos configurado en
ftp.passive.port.range(ej.50000-50100).
- Abrir solo el puerto de control (ej. 21 o 2121) y el rango de puertos pasivos configurado en
-
Servicio systemd (Linux)
- Copiar
deploy/ftp-server.servicea/etc/systemd/system/, ajustarWorkingDirectory,User/Groupy la ruta del JAR/lib. sudo systemctl daemon-reload && sudo systemctl enable ftp-server && sudo systemctl start ftp-server.
- Copiar
-
Build con Maven
mvn packagegenera el JAR entarget/y copia dependencias alib/.- Ejecutar:
java -cp "target/java-ftp-1.2.0-SNAPSHOT.jar:lib/*" FTP.Server.JavaFtpServer(o usar el script/documentación de tu despliegue).
El servidor admite graceful shutdown: al recibir SIGINT/SIGTERM deja de aceptar nuevas conexiones y espera a que las sesiones activas terminen (hasta 10 s) antes de cerrar.
Ejecuta con sudo o configura ftp.control.port=2121 en server.properties y usa ese puerto (sin privilegios).
- Verifica que el servidor esté en ejecución (
netstat -an | grep 21). - Comprueba que las reglas del firewall permitan el tráfico en el puerto 21.
- Confirma que el cliente se esté conectando al host/puerto correcto.
- Verifica que el fichero de usuarios exista (por defecto
files/users/users.txt) o queftp.users.databaseapunte a una base SQLite con usuarios. Formato TXT:username:bcryptHash:profile; usaPasswordTool adduserpara generar hashes. - Líneas vacías o que empiezan por
#se ignoran. El perfil debe serBASICO,INTERMEDIOoADMINISTRADOR.
Si el servidor no encuentra el directorio raíz o la base de datos aunque estén en server.properties, usa / en lugar de \ en todas las rutas. Ver Rutas en Windows.
Si con «Use FTPS (TLS)» el cliente falla: el servidor debe tener TLS habilitado (keystore y propiedades en server.properties). Ver Habilitar FTPS (TLS).
Comprueba JDK 8+ (java -version). Compila con mvn compile o con javac -d bin -cp "lib/*" ...; ver Compilación y ejecución. Asegúrate de tener en lib/ al menos: commons-net-3.11.1.jar, jbcrypt-0.4.jar, sqlite-jdbc-3.44.1.0.jar.
¡Las contribuciones son bienvenidas! Por favor, siéntete libre de enviar:
- Informes de errores a través de issues en el repositorio.
- Mejoras de funcionalidades a través de Pull Requests.
- Mejoras en la documentación.
- Informes de vulnerabilidades de seguridad (por favor, divúlguelos de manera responsable).
- Pruebas unitarias exhaustivas (path safety, throttle, bcrypt).
- Soportar comandos FTP adicionales (ABOR, REST, STAT).
- Soporte para IPv6.
- Haz un fork del repositorio.
- Crea una rama para tu nueva característica (
git checkout -b feature/amazing-feature). - Confirma tus cambios (
git commit -m 'Add amazing feature'). - Sube la rama a tu fork (
git push origin feature/amazing-feature). - Abre un Pull Request.
Este proyecto está licenciado bajo la Licencia MIT — consulta el archivo LICENSE para más detalles.
- Construido con la biblioteca Apache Commons Net.
- Protocolo FTP: RFC 959.
- Desarrollado con fines educativos para comprender los protocolos de red y la programación de sockets en Java.
- RFC 959 - File Transfer Protocol (FTP)
- Documentación de Apache Commons Net
- Guía de Programación de Red en Java
Desarrollado por Edu Díaz a.k.a RGiskard7 ❤️