WebDAV Server with aiohttp

A comprehensive, production-ready WebDAV server implementation with full RFC 4918 compliance, Windows Explorer compatibility, and enterprise-grade features.

Features

Core WebDAV Protocol

  • Full RFC 4918 Compliance: All standard WebDAV methods implemented
  • Windows Explorer Integration: Seamless compatibility with Windows WebDAV Mini-Redirector
  • Multiple Authentication Methods: Basic, Digest, and Token-based authentication
  • Resource Locking: Exclusive and shared locks with timeout management
  • Custom Properties: Full property management (PROPFIND/PROPPATCH)
  • Collection Operations: MKCOL, COPY, MOVE with depth support

Enterprise Features

  • 🔒 Multi-User Support: Isolated user directories with permissions
  • 💾 SQLite Database: Robust backend for users, locks, and properties
  • 🌐 Web Management Interface: Admin dashboard and user portal
  • 🚀 High Performance: Async I/O with aiohttp and aiofiles
  • 🔐 Security Focused: Input validation, path traversal prevention, SQL injection protection
  • 📊 Monitoring Ready: Structured logging and metrics collection
  • 🐳 Docker Support: Container-ready with docker-compose
  • 📈 Production Ready: Gunicorn integration, health checks, graceful shutdown

Quick Start

Prerequisites

  • Python 3.8 or higher
  • pip (Python package manager)
  • Optional: Redis for caching
  • Optional: Docker for containerized deployment

Installation

  1. Clone or create the project directory:

    mkdir webdav-server
    cd webdav-server
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Configure environment:

    cp .env.example .env
    # Edit .env with your configuration
    nano .env
    
  4. Generate a secure secret key:

    python -c "import secrets; print(secrets.token_hex(32))"
    # Copy the output to JWT_SECRET_KEY in .env
    
  5. Run the server:

    python main.py
    

The server will start on http://0.0.0.0:8080 by default.

First Run

On first run, the server automatically creates:

  • A default admin user: admin / admin123
  • The WebDAV root directory structure
  • SQLite database with all required tables

⚠️ Important: Change the default admin password immediately!

Configuration

Environment Variables

All configuration is done through the .env file. Key settings:

Server Configuration

HOST=0.0.0.0              # Listen address
PORT=8080                 # Listen port
SSL_ENABLED=false         # Enable HTTPS

Authentication

AUTH_METHODS=basic,digest # Supported auth methods
JWT_SECRET_KEY=...        # Secret for sessions (required)
SESSION_TIMEOUT=3600      # Session duration in seconds

WebDAV Settings

WEBDAV_ROOT=./webdav         # Root directory for files
MAX_FILE_SIZE=104857600      # Max file size (100MB)
MAX_PROPFIND_DEPTH=3         # Depth limit for PROPFIND
LOCK_TIMEOUT_DEFAULT=3600    # Default lock timeout

See .env.example for complete configuration options.

Usage

Connecting with Windows Explorer

Method 1: Map Network Drive

  1. Open File Explorer
  2. Right-click "This PC" → "Map network drive"
  3. Choose a drive letter
  4. Enter the WebDAV URL:
    http://your-server:8080/
    
  5. Check "Connect using different credentials"
  6. Enter your username and password
  7. Click "Finish"

Method 2: Add Network Location

  1. Open File Explorer
  2. Right-click "This PC" → "Add a network location"
  3. Choose "Choose a custom network location"
  4. Enter the WebDAV URL:
    http://your-server:8080/
    
  5. Enter credentials when prompted

For HTTPS connections:

https://your-server:8443/

Note: Windows requires port 443 for HTTPS WebDAV by default. To use other ports, modify Windows registry:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
BasicAuthLevel = 2

Connecting with macOS Finder

  1. Open Finder
  2. Press Cmd+K (Go → Connect to Server)
  3. Enter the WebDAV URL:
    http://your-server:8080/
    
  4. Click "Connect"
  5. Enter your credentials

Connecting with Linux (davfs2)

  1. Install davfs2:

    sudo apt-get install davfs2  # Ubuntu/Debian
    
  2. Mount the WebDAV share:

    sudo mount -t davfs http://your-server:8080/ /mnt/webdav
    
  3. Enter credentials when prompted

Command Line Tools

Using curl

Upload a file:

curl -u username:password -T file.txt http://localhost:8080/file.txt

Download a file:

curl -u username:password http://localhost:8080/file.txt -o file.txt

Create a directory:

curl -u username:password -X MKCOL http://localhost:8080/newdir/

Delete a resource:

curl -u username:password -X DELETE http://localhost:8080/file.txt

Using cadaver

cadaver http://localhost:8080/
# Enter credentials
dav:/> ls
dav:/> put localfile.txt
dav:/> get remotefile.txt
dav:/> mkcol newfolder

User Management

Creating Users Programmatically

import asyncio
from main import Database

async def create_user():
    db = Database('./webdav.db')
    user_id = await db.create_user(
        username='john',
        password='SecurePass123!',
        email='john@example.com',
        role='user'
    )
    print(f"Created user with ID: {user_id}")

asyncio.run(create_user())

User Roles

  • admin: Full access to all features and user management
  • user: Standard user with access to their own directory
  • readonly: Read-only access (future implementation)

Directory Structure

webdav/
├── users/
│   ├── admin/          # Admin user directory
│   ├── john/           # John's private directory
│   └── jane/           # Jane's private directory
├── shared/             # Shared between all users (optional)
└── public/             # Public access (optional)

Production Deployment

Using Gunicorn

Create gunicorn_config.py:

import multiprocessing

bind = "0.0.0.0:8080"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "aiohttp.GunicornWebWorker"
keepalive = 30
timeout = 60
accesslog = "./logs/access.log"
errorlog = "./logs/error.log"
loglevel = "info"

Run with Gunicorn:

gunicorn main:init_app --config gunicorn_config.py

Using Docker

Create Dockerfile:

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Create directories
RUN mkdir -p /app/webdav /app/logs

# Expose port
EXPOSE 8080

# Run application
CMD ["python", "main.py"]

Create docker-compose.yml:

version: '3.8'

services:
  webdav:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ./webdav:/app/webdav
      - ./logs:/app/logs
      - ./webdav.db:/app/webdav.db
    environment:
      - HOST=0.0.0.0
      - PORT=8080
      - DB_PATH=/app/webdav.db
      - WEBDAV_ROOT=/app/webdav
    restart: unless-stopped

  # Optional: Redis for caching
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    restart: unless-stopped

Build and run:

docker-compose up -d

Nginx Reverse Proxy

Create /etc/nginx/sites-available/webdav:

server {
    listen 80;
    server_name webdav.example.com;

    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name webdav.example.com;

    ssl_certificate /etc/letsencrypt/live/webdav.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webdav.example.com/privkey.pem;

    # WebDAV specific settings
    client_max_body_size 100M;
    client_body_buffer_size 128k;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebDAV headers
        proxy_set_header Destination $http_destination;
        proxy_set_header Depth $http_depth;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Enable and reload:

sudo ln -s /etc/nginx/sites-available/webdav /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Security Best Practices

SSL/TLS Configuration

  1. Always use HTTPS in production
  2. Generate SSL certificates with Let's Encrypt:
    sudo certbot certonly --nginx -d webdav.example.com
    
  3. Update .env:
    SSL_ENABLED=true
    SSL_CERT_PATH=/etc/letsencrypt/live/webdav.example.com/fullchain.pem
    SSL_KEY_PATH=/etc/letsencrypt/live/webdav.example.com/privkey.pem
    

Authentication

  • Use Digest authentication over Basic when possible
  • Enforce strong passwords (min length, complexity)
  • Enable rate limiting to prevent brute force attacks
  • Implement account lockout after failed attempts

Firewall Rules

# Allow only necessary ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Regular Updates

# Update dependencies
pip install --upgrade -r requirements.txt

# Backup database before updates
cp webdav.db webdav.db.backup

Monitoring and Logging

Log Files

Logs are stored in ./logs/ directory:

  • webdav.log - Application logs
  • access.log - Access logs (requests)
  • error.log - Error logs

Log Format

JSON-structured logs for easy parsing:

{
  "timestamp": "2025-01-15T10:30:45Z",
  "level": "INFO",
  "user": "john",
  "method": "PROPFIND",
  "path": "/documents/",
  "status": 207,
  "duration_ms": 45
}

Health Check

Access the health check endpoint:

curl http://localhost:8080/health

Troubleshooting

Windows Explorer Connection Issues

Problem: "The network folder is no longer available"

Solutions:

  1. Increase Windows WebClient timeout:

    Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    FileSizeLimitInBytes = DWORD: 0xFFFFFFFF
    
  2. Enable Basic Authentication over HTTP (insecure - use only for testing):

    Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    BasicAuthLevel = 2
    
  3. Restart WebClient service:

    net stop webclient
    net start webclient
    

Problem: "The specified server cannot perform the requested operation"

Solution: Ensure the URL ends with a slash and doesn't include port 80:

  • http://server:80/path
  • http://server/path/

macOS Finder Issues

Problem: Connection refused or timeout

Solutions:

  1. Use HTTP URL with explicit protocol:

    http://server:8080/
    
  2. Check firewall settings on server

  3. Try connecting via IP address instead of hostname

Performance Issues

Problem: Slow PROPFIND responses

Solutions:

  1. Reduce MAX_PROPFIND_DEPTH in .env:

    MAX_PROPFIND_DEPTH=1
    
  2. Enable caching with Redis:

    CACHE_ENABLED=true
    REDIS_HOST=localhost
    
  3. Increase worker processes:

    WORKERS=8
    

Database Locked Errors

Problem: "database is locked" errors

Solutions:

  1. Enable WAL mode (automatic in code)
  2. Ensure only one process accesses the database
  3. Use separate databases for multiple instances

API Documentation

WebDAV Methods

PROPFIND - Property Discovery

PROPFIND /documents/ HTTP/1.1
Depth: 1
Content-Type: application/xml

<?xml version="1.0"?>
<D:propfind xmlns:D="DAV:">
    <D:allprop/>
</D:propfind>

PROPPATCH - Property Modification

PROPPATCH /file.txt HTTP/1.1
Content-Type: application/xml

<?xml version="1.0"?>
<D:propertyupdate xmlns:D="DAV:">
    <D:set>
        <D:prop>
            <D:displayname>My Document</D:displayname>
        </D:prop>
    </D:set>
</D:propertyupdate>

LOCK - Resource Locking

LOCK /file.txt HTTP/1.1
Timeout: Second-3600
Content-Type: application/xml

<?xml version="1.0"?>
<D:lockinfo xmlns:D="DAV:">
    <D:lockscope><D:exclusive/></D:lockscope>
    <D:locktype><D:write/></D:locktype>
    <D:owner>
        <D:href>mailto:john@example.com</D:href>
    </D:owner>
</D:lockinfo>

Development

Running Tests

# Install test dependencies
pip install pytest pytest-asyncio pytest-aiohttp pytest-cov

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=. --cov-report=html

Code Style

# Format code
black main.py

# Lint code
flake8 main.py

# Type checking
mypy main.py

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

MIT License - See LICENSE file for details

Support

For issues, questions, or contributions:

  • GitHub Issues: [Create an issue]
  • Documentation: [Wiki]
  • Community: [Discussions]

Changelog

Version 1.0.0 (2025-01-15)

  • Initial release
  • Full RFC 4918 compliance
  • Windows Explorer compatibility
  • Multi-user support with SQLite backend
  • Basic and Digest authentication
  • Resource locking
  • Custom properties
  • Production-ready with Gunicorn support

Acknowledgments

  • RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
  • aiohttp - Async HTTP client/server framework
  • Python community

Made with ❤️ for the WebDAV community

S
Description
Cloud Storage System Using WebDav as protocol. It's optimized to work with multiple workers, load balancing and is only bottle necked by server hardware. The software is lightweight.
Readme
183 KiB
Languages
Python 93.2%
Shell 5.7%
Dockerfile 1.1%