A Kubernetes-native FTP service that provides secure file transfer capabilities using Custom Resource Definitions (CRDs) for user and backend management.
KubeFTPd is designed to replace traditional FTP solutions like SFTPGo with a cloud-native approach that leverages Kubernetes for configuration and management. It supports multiple storage backends including MinIO (S3-compatible), WebDAV, and local filesystem storage, making it suitable for various use cases from document scanning workflows to general file transfer needs.
- Kubernetes-Native: Uses CRDs for user and backend configuration
- Multiple Storage Backends: Support for MinIO/S3, WebDAV endpoints, and local filesystem storage
- Built-in User Types: Anonymous FTP (RFC 1635) and admin users with automatic User CR management
- PASV Mode Support: Currently supports passive FTP mode with active mode planned
- Gateway API Support: Modern alternative to LoadBalancer with standardized TCP routing
- Cozystack Integration: Native support for Cozystack PaaS platform with FluxCD deployment
- RBAC Integration: Full Kubernetes RBAC support for access control
- Health & Metrics: Built-in health checks, JSON logging, and metrics endpoints
- Security First: TLS support, dual password authentication (plaintext/secrets), webhook validation
- Password Security: Kubernetes Secrets integration with production restrictions and strength validation
- Webhook Validation: Admission controllers for password policies and production compliance
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ FTP Client │────│ KubeFTPd │────│ Storage Backend │
│ │ │ (Controller) │ │ (MinIO/WebDAV/ │
└─────────────────┘ └──────────────────┘ │ Filesystem) │
└─────────────────┘
│
▼
┌──────────────────┐
│ Kubernetes API │
│ (User/Backend │
│ CRDs) │
└──────────────────┘
Pre-built container images are available from GitHub Container Registry:
# Latest release
ghcr.io/rossigee/kubeftpd:latest
# Specific version
ghcr.io/rossigee/kubeftpd:v0.7.0Supported architectures:
linux/amd64linux/arm64
- Kubernetes cluster (v1.25+)
- kubectl configured
- Go 1.25+ (for development)
- Install CRDs:
kubectl apply -f config/crd/bases/- Deploy the controller:
kubectl apply -f config/rbac/
kubectl apply -f config/manager/- Create a storage backend:
For MinIO:
apiVersion: ftp.golder.org/v1
kind: MinioBackend
metadata:
name: minio-backend
namespace: default
spec:
endpoint: "https://minio.example.com"
bucket: "ftp-storage"
region: "us-east-1"
credentials:
accessKeyID: "admin"
secretAccessKey: "password123"
tls:
insecureSkipVerify: false
# Inline PEM CA bundle (takes effect when caSecretRef is absent):
# caCert: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# Or reference a Kubernetes Secret containing the CA bundle (takes precedence over caCert):
# caSecretRef:
# name: minio-ca
# namespace: certs # optional; defaults to this resource's namespace
# key: ca.crt # optional; defaults to "ca.crt"For WebDAV:
apiVersion: ftp.golder.org/v1
kind: WebDavBackend
metadata:
name: webdav-backend
namespace: default
spec:
endpoint: "https://webdav.example.com"
basePath: "/ftp-data"
credentials:
username: "ftpuser"
password: "password123"
tls:
insecureSkipVerify: false
# Inline PEM CA bundle (takes effect when caSecretRef is absent):
# caCert: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# Or reference a Kubernetes Secret containing the CA bundle (takes precedence over caCert):
# caSecretRef:
# name: webdav-ca
# namespace: certs # optional; defaults to this resource's namespace
# key: ca.crt # optional; defaults to "ca.crt"For Filesystem:
apiVersion: ftp.golder.org/v1
kind: FilesystemBackend
metadata:
name: filesystem-backend
namespace: default
spec:
basePath: "/data/ftp"
readOnly: false
fileMode: "0644"
dirMode: "0755"
maxFileSize: 0
volumeClaimRef:
name: ftp-storage
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ftp-storage
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi- Create FTP users:
Option A - Using plaintext password (development only):
apiVersion: ftp.golder.org/v1
kind: User
metadata:
name: scanner-receipts
namespace: default
spec:
username: "scanner"
password: "secure-password"
homeDirectory: "/receipts"
enabled: true
backend:
kind: "MinioBackend"
name: "minio-backend"
permissions:
read: true
write: true
delete: falseOption B - Using Kubernetes Secret (recommended):
# Create secret first
kubectl create secret generic scanner-password \
--from-literal=password="MySecurePassword123!"apiVersion: ftp.golder.org/v1
kind: User
metadata:
name: scanner-receipts
namespace: default
spec:
username: "scanner"
passwordSecret:
name: "scanner-password"
key: "password"
homeDirectory: "/receipts"
enabled: true
backend:
kind: "MinioBackend"
name: "minio-backend"
permissions:
read: true
write: true
delete: false- Connect with FTP client:
ftp ftp.example.com 21
# Use passive mode (PASV)
# Login with username/password from User CRDDefines FTP users with their credentials, permissions, and backend configuration. Supports both plaintext passwords (development) and Kubernetes Secrets (production). Three user types are supported:
- regular: Standard FTP users (default)
- anonymous: RFC 1635 compliant anonymous FTP access
- admin: Administrative users with full permissions
KubeFTPd supports automatic management of built-in users through configuration flags. These users are created as User CRs and managed by the BuiltInUserManager controller.
Enable RFC 1635 compliant anonymous FTP access:
# Enable anonymous user with filesystem backend
kubeftpd --enable-anonymous \
--anonymous-home-dir="/pub" \
--anonymous-backend-kind="FilesystemBackend" \
--anonymous-backend-name="anonymous-backend"Anonymous User Characteristics:
- Username:
anonymous - Password: Any password accepted (RFC 1635 compliance)
- Permissions: Read-only access
- Created as User CR:
builtin-anonymous
Enable built-in admin user with secret-based authentication:
# First create the admin password secret
kubectl create secret generic admin-secret \
--from-literal=password="AdminPassword123!"
# Enable admin user
kubeftpd --enable-admin \
--admin-password-secret="admin-secret" \
--admin-home-dir="/" \
--admin-backend-kind="FilesystemBackend" \
--admin-backend-name="admin-backend"Admin User Characteristics:
- Username:
admin - Password: Retrieved from Kubernetes Secret
- Permissions: Full access (read, write, delete, list)
- Created as User CR:
builtin-admin
Built-in users are automatically:
- Created when enabled via configuration flags
- Updated when configuration changes
- Deleted when disabled
- Labeled with
kubeftpd.golder.org/builtin: true
Example of automatically created User CR:
apiVersion: ftp.golder.org/v1
kind: User
metadata:
name: builtin-anonymous
namespace: default
labels:
kubeftpd.golder.org/builtin: "true"
kubeftpd.golder.org/type: "anonymous"
spec:
type: "anonymous"
username: "anonymous"
homeDirectory: "/pub"
enabled: true
backend:
kind: "FilesystemBackend"
name: "anonymous-backend"
permissions:
read: true
write: false
delete: false
list: truePassword Methods (mutually exclusive):
Option A - Plaintext password:
apiVersion: ftp.golder.org/v1
kind: User
metadata:
name: example-user
namespace: default
spec:
username: "ftpuser"
password: "secure-password" # Not recommended for production
homeDirectory: "/home/ftpuser"
enabled: true
backend:
kind: "MinioBackend" # or "WebDavBackend"
name: "my-backend"
namespace: "default" # optional, defaults to User namespace
permissions:
read: true
write: true
delete: false
status:
ready: true
message: "User configured successfully"Option B - Kubernetes Secret (recommended):
apiVersion: ftp.golder.org/v1
kind: User
metadata:
name: example-user
namespace: default
spec:
username: "ftpuser"
passwordSecret:
name: "user-credentials"
key: "password"
homeDirectory: "/home/ftpuser"
enabled: true
backend:
kind: "MinioBackend"
name: "my-backend"
permissions:
read: true
write: true
delete: false
status:
ready: true
message: "User configured successfully"Security Notes:
- Production environments with
environment: productionnamespace labels require secret-based passwords - Webhook validation enforces password strength requirements
- Secret names in production must follow pattern:
.*-ftp-(password|credentials)$
Configures MinIO/S3-compatible storage backends.
apiVersion: ftp.golder.org/v1
kind: MinioBackend
metadata:
name: example-minio
namespace: default
spec:
endpoint: "https://minio.example.com"
bucket: "ftp-storage"
region: "us-east-1"
pathPrefix: "ftp-data/" # optional
credentials:
accessKeyID: "minioadmin"
secretAccessKey: "minioadmin"
# Or use Kubernetes Secret:
useSecret:
name: "minio-credentials"
namespace: "custom-namespace" # optional, defaults to MinioBackend's namespace
accessKeyIDKey: "access-key" # optional, defaults to "accessKeyID"
secretAccessKeyKey: "secret-key" # optional, defaults to "secretAccessKey"
tls:
insecureSkipVerify: false
# Option 1: inline PEM CA bundle
caCert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# Option 2: reference a Kubernetes Secret (takes precedence over caCert)
caSecretRef:
name: minio-ca
namespace: certs # optional; defaults to MinioBackend's namespace
key: ca.crt # optional; defaults to "ca.crt"
status:
ready: true
message: "Backend connection established"Configures WebDAV storage backends.
apiVersion: ftp.golder.org/v1
kind: WebDavBackend
metadata:
name: example-webdav
namespace: default
spec:
endpoint: "https://webdav.example.com"
basePath: "/ftp-storage" # optional
credentials:
username: "webdavuser"
password: "webdavpass"
# Or use Kubernetes Secret:
useSecret:
secretName: "webdav-credentials"
usernameKey: "username"
passwordKey: "password"
tls:
insecureSkipVerify: false
# Option 1: inline PEM CA bundle
caCert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# Option 2: reference a Kubernetes Secret (takes precedence over caCert)
caSecretRef:
name: webdav-ca
namespace: certs # optional; defaults to WebDavBackend's namespace
key: ca.crt # optional; defaults to "ca.crt"
status:
ready: true
message: "Backend connection established"Configures local filesystem storage backends with Kubernetes persistent volumes.
apiVersion: ftp.golder.org/v1
kind: FilesystemBackend
metadata:
name: example-filesystem
namespace: default
spec:
basePath: "/data/ftp"
readOnly: false
fileMode: "0644" # File permissions (octal)
dirMode: "0755" # Directory permissions (octal)
maxFileSize: 0 # Maximum file size in bytes (0 = no limit)
volumeClaimRef: # Optional PVC reference
name: "ftp-storage"
namespace: "default" # defaults to same namespace
status:
ready: true
message: "Filesystem backend ready"
mountPath: "/data/ftp"Required PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ftp-storage
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# storageClassName: fast-ssd # specify storage class if needed| Variable | Description | Default |
|---|---|---|
FTP_BIND_ADDRESS |
FTP server bind address (empty = all interfaces) | "" |
FTP_PORT |
FTP server port | 21 (root), 2121 (non-root) |
FTP_PASSIVE_PORTS |
FTP passive mode port range | 10000-10020 |
FTP_PASSIVE_PORT_MIN |
Minimum passive port range (alternative) | 30000 |
FTP_PASSIVE_PORT_MAX |
Maximum passive port range (alternative) | 30100 |
FTP_PUBLIC_IP |
Public IP for FTP PASV responses | "" |
FTP_WELCOME_MESSAGE |
FTP welcome message | "Welcome to KubeFTPd" |
FTP_IDLE_TIMEOUT |
FTP connection idle timeout (seconds) | 300 |
FTP_MAX_CONNECTIONS |
Maximum concurrent FTP connections | 100 |
LOG_LEVEL |
Logging level (debug, info, warn, error) | info |
LOG_FORMAT |
Log format (json, text) | json |
HTTP_BIND_ADDRESS |
HTTP server bind address (with port, e.g., :8080) |
:8080 |
ENABLE_PROFILING |
Enable Go profiling endpoints | false |
FTP Bind Address Examples:
# Bind to all interfaces (default)
./kubeftpd --ftp-port=21
# Bind to specific IP (CLI flag)
./kubeftpd --ftp-bind-address=192.168.1.10 --ftp-port=21
# Bind to localhost only (environment variable)
FTP_BIND_ADDRESS=127.0.0.1 FTP_PORT=2121 ./kubeftpd
# Bind to IPv6 all interfaces
FTP_BIND_ADDRESS=:: FTP_PORT=2121 ./kubeftpdHTTP Bind Address Examples:
# Default (all interfaces on port 8080)
./kubeftpd
# Specific IP and port (CLI flag - uses default --http-bind-address flag)
./kubeftpd --http-bind-address=127.0.0.1:8080
# Specific IP and port (environment variable)
HTTP_BIND_ADDRESS=192.168.1.10:8080 ./kubeftpd
# HTTPS on all interfaces
./kubeftpd --http-bind-address=:8443 --metrics-secure| Variable | Description | Default |
|---|---|---|
--enable-anonymous |
Enable anonymous FTP access (RFC 1635) | false |
--anonymous-home-dir |
Home directory for anonymous users | /pub |
--anonymous-backend-kind |
Backend kind for anonymous users | FilesystemBackend |
--anonymous-backend-name |
Backend name for anonymous users | anonymous-backend |
--enable-admin |
Enable built-in admin user | false |
--admin-password-secret |
Kubernetes Secret name for admin password | "" |
--admin-home-dir |
Home directory for admin user | / |
--admin-backend-kind |
Backend kind for admin user | FilesystemBackend |
--admin-backend-name |
Backend name for admin user | admin-backend |
| Variable | Description | Default |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP endpoint for traces and metrics | "" (disabled) |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
OTLP endpoint for traces only | "" (disabled) |
OTEL_SERVICE_NAME |
Service name for telemetry | "" (disabled) |
OTEL_RESOURCE_ATTRIBUTES |
Additional resource attributes | "" |
Note: OpenTelemetry tracing is automatically enabled when any OTEL_* environment variables are configured.
-
Secure Port 21 Binding: KubeFTPd uses
CAP_NET_BIND_SERVICEcapability for secure port binding:- Non-root Execution: Runs as non-root user (UID 65532) for enhanced security
- Privileged Port Access:
CAP_NET_BIND_SERVICEallows binding to port 21 without root privileges - Minimal Capabilities: Drops all capabilities except
NET_BIND_SERVICEfor least privilege principle - Standard FTP Port: Uses port 21 by default while maintaining security best practices
-
Fail-Fast Architecture: KubeFTPd implements fail-fast startup behavior:
- FTP server binding failures cause immediate application termination
- Kubernetes will restart the pod, providing clear feedback about configuration issues
- Health checks only pass when FTP service is actually functional
-
Use Kubernetes Secrets for storing credentials instead of plain text
-
Enable Webhook Validation for password policies and production compliance:
webhook: enabled: true validation: passwordStrength: enabled: true minLength: 8 requireComplexity: true production: enabled: true requireSecrets: true
-
Production Environment Setup:
- Label production namespaces:
kubectl label namespace production environment=production - Use strong password patterns avoiding common words
- Follow secret naming convention:
*-ftp-passwordor*-ftp-credentials
- Label production namespaces:
-
Enable TLS for all backend connections
-
Set appropriate RBAC permissions for the KubeFTPd service account
-
Use NetworkPolicies to restrict FTP traffic
-
Regularly rotate credentials and certificates
- Go 1.25+
- Docker
- Kubernetes cluster (kind/minikube for local development)
- kubebuilder v3.0+
- Clone the repository:
git clone https://github.com/rossigee/kubeftpd.git
cd kubeftpd- Install dependencies:
go mod download- Set up pre-commit hooks:
make setup-pre-commit- Run tests:
make test
make test-coverage- Lint code:
make lint # Basic linting with go vet, gofmt
make lint-advanced # Comprehensive linting (requires golangci-lint Go 1.25+ support)- Build and run locally:
make build
make run| Target | Description |
|---|---|
make build |
Build the kubeftpd binary |
make run |
Run the controller locally |
make test |
Run unit tests |
make test-coverage |
Run tests with coverage report |
make lint |
Run golangci-lint |
make security-scan |
Run gosec security scanner |
make manifests |
Generate CRD manifests |
make generate |
Generate code |
make docker-build |
Build Docker image |
make docker-push |
Push Docker image |
make install |
Install CRDs to cluster |
make uninstall |
Remove CRDs from cluster |
make deploy |
Deploy controller to cluster |
make undeploy |
Remove controller from cluster |
The project includes comprehensive testing:
- Unit Tests: Test individual components and functions
- Integration Tests: Test CRD controllers and storage backends
- E2E Tests: Test complete FTP workflows
- Security Tests: Scan for security vulnerabilities
Run all tests:
make test-allWe maintain high code quality standards:
- golangci-lint: Comprehensive linting
- gosec: Security vulnerability scanning
- Pre-commit hooks: Automated quality checks
- Code coverage: Minimum 80% coverage requirement
- Create namespace:
kubectl create namespace kubeftpd-system- Install CRDs:
kubectl apply -f https://github.com/rossigee/kubeftpd/releases/latest/download/crds.yaml- Deploy using Helm (recommended):
# Install directly from GHCR (GitHub Container Registry)
helm install kubeftpd oci://ghcr.io/rossigee/kubeftpd/kubeftpd \
--version 0.6.0 \
--namespace kubeftpd-system \
--create-namespace \
--set controller.image.tag=latest \
--set webhook.enabled=true \
--set ftp.service.port=2121 # Example: override default portAlternative: Add GHCR as Helm repository
# Note: GHCR doesn't support traditional helm repo add yet
# Use OCI protocol directly as shown aboveHelm Configuration Options:
# values.yaml
ftp:
service:
port: 21 # Configurable for non-root deployment
passive:
service:
portRange:
min: 30000
max: 30100
webhook:
enabled: true # Enable password validation
validation:
passwordStrength:
enabled: true
minLength: 8
requireComplexity: true
production:
enabled: true
requireSecrets: true
security:
passwordPolicy:
enforceStrong: true
minLength: 8
requireComplexity: true- Or deploy using kubectl:
kubectl apply -f https://github.com/rossigee/kubeftpd/releases/latest/download/kubeftpd.yamlFor production use, configure a LoadBalancer service:
apiVersion: v1
kind: Service
metadata:
name: kubeftpd-ftp
namespace: kubeftpd-system
spec:
type: LoadBalancer
ports:
- name: ftp
port: 21
targetPort: 21
- name: ftp-passive
port: 30000
targetPort: 30000
# Add range for passive ports 30000-30100
selector:
app: kubeftpdFor modern Kubernetes deployments, use Gateway API as an alternative to LoadBalancer:
# Enable Gateway API support
ftp:
service:
port: 21
passivePortRange:
min: 10000
max: 10019
gateway:
enabled: true
config:
gatewayClassName: "cilium" # or istio, nginx-gateway, etc.Benefits of Gateway API:
- Standardized: Vendor-neutral configuration across different Gateway implementations
- Advanced: Rich traffic management and policy capabilities
- Secure: Fine-grained access control and multi-tenancy support
- Efficient: Shared infrastructure reduces resource overhead
See GATEWAY_API_SUPPORT.md for detailed configuration and examples.
For Cozystack PaaS platform deployment with FluxCD GitOps:
# Deploy with FluxCD HelmRelease
kubectl apply -f examples/cozystack/helmrelease.yaml
# Or deploy with Kustomize
kubectl apply -k examples/cozystack/
# Or use Cozystack-optimized values
helm install kubeftpd ./chart/kubeftpd \
--values chart/kubeftpd/examples/values-cozystack.yamlCozystack Features:
- FluxCD Integration: Native HelmRelease resources for GitOps workflows
- Multi-tenant Security: Enhanced security configurations for shared environments
- Resource Optimization: Conservative resource limits suitable for PaaS platforms
- Network Policies: Automatic network isolation for multi-tenant deployments
See COZYSTACK_INTEGRATION.md for detailed deployment guide and examples.
- Export existing users from SFTPGo configuration
- Create equivalent User CRDs using the migration script:
scripts/migrate-from-sftpgo.sh users.json
- Update backend configurations to use MinioBackend/WebDavBackend CRDs
- Test connections with existing FTP clients
- Update DNS/load balancer to point to KubeFTPd service
- Decommission SFTPGo after validation
- Protocol: KubeFTPd currently supports PASV mode only (EPSV planned)
- Authentication: Migrates to Kubernetes-native user management
- Storage: Direct compatibility with existing MinIO/S3 buckets
- Permissions: Enhanced permission model with Kubernetes RBAC integration
KubeFTPd provides comprehensive structured logging for all FTP operations with detailed success/failure information:
Log Format Examples:
[testuser] UPLOAD SUCCESS: /docs/file.pdf (1024 bytes, 450ms)
[testuser] DOWNLOAD SUCCESS: /images/photo.jpg (2560 bytes, 120ms)
[testuser] DELETE FAILED: /protected/secret.txt - permission denied
[testuser] LIST SUCCESS: /documents/
[testuser] MKDIR SUCCESS: /newfolder/
Logged Operations:
- File Operations: UPLOAD, DOWNLOAD, DELETE with size, duration, and status
- Directory Operations: LIST, MKDIR, RMDIR with success/failure status
- Authentication: User login/logout with session duration
- Errors: Detailed error information with context
Distributed tracing support for FTP operations when OpenTelemetry is configured:
Traced Operations:
ftp.upload- File uploads with size, duration, backend typeftp.download- File downloads with offset, size, timingftp.append- File append operationsftp.delete- File and directory deletions
Trace Attributes:
ftp.user- Authenticated usernameftp.path- File/directory pathftp.backend- Storage backend typeftp.bytes- Transfer size in bytesftp.duration_ms- Operation duration
- Liveness:
/healthzon port 8080 - Readiness:
/readyzon port 8080 - Status:
/on port 8080 (service information)
Prometheus metrics available on /metrics endpoint (port 8080):
Connection & Session Metrics:
kubeftpd_active_connections- Number of active FTP connectionskubeftpd_connections_total- Total FTP connections (by username, client_ip)kubeftpd_connection_duration_seconds- Duration of FTP connections (histogram)kubeftpd_user_session_duration_seconds- Duration of user sessions (histogram)
Authentication Metrics:
kubeftpd_user_logins_total- Total user login attempts (by username, result)kubeftpd_authentication_attempts_total- Authentication attempts by method and resultkubeftpd_password_retrieval_duration_seconds- Password retrieval latency from secrets
File Operation Metrics:
kubeftpd_file_operations_total- Total file operations (by username, operation, backend_type, result)kubeftpd_file_transfer_bytes_total- Total bytes transferred (by username, direction, backend_type)kubeftpd_file_transfer_duration_seconds- Duration of file transfers (histogram)
Backend Performance Metrics:
kubeftpd_backend_operations_total- Backend operations (by backend_name, backend_type, operation, result)kubeftpd_backend_response_time_seconds- Backend operation response times (histogram)
System Metrics:
kubeftpd_errors_total- Error counters by type and componentkubeftpd_config_reloads_total- Configuration reload events
Structured JSON logging with configurable levels:
{
"timestamp": "2024-01-15T10:30:00Z",
"level": "info",
"msg": "User authenticated successfully",
"username": "scanner",
"backend": "minio-backend",
"client_ip": "192.168.1.100"
}-
Connection refused
- Check FTP port (21) is accessible
- Verify LoadBalancer/NodePort configuration
- Check firewall rules
-
Authentication failures
- Verify User CRD exists and is enabled
- Check credentials in User spec (password or passwordSecret)
- For secret-based auth: verify secret exists and contains correct key
- Check webhook validation logs if enabled
- Review controller logs for errors
-
Backend connection errors
- Verify Backend CRD status
- Check network connectivity to storage backend
- Validate credentials and permissions
-
PASV data connection failures (
No route to hoston passive mode)- Problem: Separate LoadBalancer services for control/data ports get different external IPs
- Solution: Use combined LoadBalancer service (default in v0.5.0+)
- Check: Verify both port 21 and passive ports (10000-10019) are on same service
- Configuration: Set
FTP_PUBLIC_IPenvironment variable to LoadBalancer external IP - Legacy: Ensure passive port range is accessible and check NAT/firewall configuration
- See PASV_LOADBALANCER_FIX.md for detailed migration instructions
-
Webhook validation issues
- Check webhook pod status:
kubectl get pods -l app.kubernetes.io/component=webhook - Review webhook logs:
kubectl logs -l app.kubernetes.io/component=webhook - Verify webhook configuration:
kubectl get validatingadmissionwebhook - Test user creation with detailed error messages
- Check webhook pod status:
Enable debug logging:
kubectl set env deployment/kubeftpd-controller LOG_LEVEL=debug -n kubeftpd-systemView controller logs:
kubectl logs -f deployment/kubeftpd-controller -n kubeftpd-systemWe welcome contributions! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch
- Make changes with tests
- Run quality checks:
make lint test security-scan - Submit a pull request
NOTE: Run make help for more information on all potential make targets
More information can be found via the Kubebuilder Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Wiki
- Active FTP mode support (PORT command)
- Extended Passive mode (EPSV) support
- FTPS (FTP over TLS/SSL) support
- SFTP protocol support
- Multi-tenancy with namespace isolation
- Advanced user quota management
- Audit logging and compliance features
- Integration with external identity providers (LDAP, OIDC)
Copyright 2025.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.