How to Implement a Write-Only FTP Server: Secure File Uploads Without Directory Visibility


2 views

In data collection systems, IoT device logging, or secure document submission portals, we often need an FTP server that accepts uploads while preventing clients from viewing stored files. This write-only configuration enhances security by implementing the principle of least privilege.

Here are three proven methods to achieve write-only FTP functionality:

1. vsftpd Configuration

The most robust solution uses vsftpd with these settings in /etc/vsftpd.conf:

# Basic security
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES

# Write-only configuration
dirlist_enable=NO
download_enable=NO
force_dot_files=NO
hide_ids=YES

2. Pure-FTPd Method

For Pure-FTPd servers, create a dedicated user with restricted permissions:

pure-pw useradd uploader -u ftpuser -d /var/ftp/uploads -m \
  -N "::upload" -n 1000:10 -r 100:10 -t 1024 -T 50 -q 0

3. ProFTPD VirtualHost

ProFTPD supports virtual hosts with specific permissions:

<VirtualHost 192.168.1.100>
    ServerName "writeonly-ftp"
    DefaultRoot ~
    <Limit WRITE>
        AllowAll
    </Limit>
    <Limit READ LIST>
        DenyAll
    </Limit>
</VirtualHost>

For processing uploaded files, use this Python watchdog script:

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class UploadHandler(FileSystemEventHandler):
    def on_created(self, event):
        if not event.is_directory:
            print(f"New file uploaded: {event.src_path}")
            # Add your processing logic here

observer = Observer()
observer.schedule(UploadHandler(), path='/ftp/uploads')
observer.start()
  • Set correct directory permissions (chmod 730 /ftp/uploads)
  • Implement per-IP rate limiting
  • Enable TLS encryption (consider using Let's Encrypt)
  • Regularly audit uploaded files for malware

Verify the setup using this curl command:

curl -T testfile.txt ftp://user:pass@yourserver/ --ftp-create-dirs

Then attempt to list directory contents:

ftp> ls
550 Permission denied.

In data collection systems, IoT applications, or log aggregation scenarios, you often need an FTP server that accepts uploads while preventing clients from viewing directory contents. This write-only configuration is particularly useful when:

  • Collecting sensitive customer uploads
  • Building automated data pipelines
  • Creating anonymous dropboxes
  • Preventing information disclosure

vsftpd (Very Secure FTP Daemon) is ideal for this setup. Here's a complete configuration:

# /etc/vsftpd.conf
listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
allow_writeable_chroot=YES
hide_ids=YES
dirlist_enable=NO
download_enable=NO
force_dot_files=YES

Key parameters explained:

  • dirlist_enable=NO - Disables directory listing
  • download_enable=NO - Blocks file downloads
  • hide_ids=YES - Masks file ownership

For systems using Pure-FTPd, create this configuration:

# /etc/pure-ftpd/conf/NoDisplayFiles
yes
# /etc/pure-ftpd/conf/NoChmod
yes
# /etc/pure-ftpd/conf/NoRename
yes
# /etc/pure-ftpd/conf/CustomerProof
yes

Verify the setup using this Python test script:

from ftplib import FTP

def test_ftp():
    try:
        ftp = FTP('your.server.com')
        ftp.login('username', 'password')
        
        # Test upload
        with open('test.txt', 'wb') as f:
            ftp.storbinary('STOR test.txt', f)
            
        # Test listing (should fail)
        try:
            print(ftp.nlst())
            print("FAIL: Directory listing succeeded")
        except:
            print("PASS: Directory listing blocked")
            
        ftp.quit()
    except Exception as e:
        print(f"Error: {str(e)}")

test_ftp()
  • Implement rate limiting to prevent abuse
  • Set up proper disk quotas
  • Regularly monitor upload directories
  • Consider using FTPS (FTP over SSL) for encryption

For more control, you can build a minimal FTP server in Python:

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

class WriteOnlyHandler(FTPHandler):
    def on_file_received(self, file):
        # Process uploaded files here
        pass
        
    def ftp_LIST(self, path):
        return ""

authorizer = DummyAuthorizer()
authorizer.add_user("user", "password", "/upload/dir", perm="w")

handler = WriteOnlyHandler
handler.authorizer = authorizer

server = FTPServer(("0.0.0.0", 21), handler)
server.serve_forever()