- Introduced a revolutionary 8-line minimal configuration system, reducing complexity by 93%. - Added auto-configuration generation with `--genconfig` for quick setup. - Enhanced file processing with fixed deduplication responses and optimized queue management. - Supported multi-architecture builds (AMD64, ARM64, ARM32v7) with an interactive builder. - Updated migration guide for seamless transition from 3.1.x to 3.2. - Overhauled user experience for new installations, emphasizing ease of use and performance.
1545 lines
44 KiB
Markdown
1545 lines
44 KiB
Markdown
# HMAC File Server 3.2 – Tremora del Terra
|
||
|
||
[](https://github.com/PlusOne/hmac-file-server)
|
||
[](LICENSE)
|
||
[](https://golang.org/)
|
||
[](https://github.com/PlusOne/hmac-file-server)
|
||
|
||
A high-performance, secure file server implementing XEP-0363 (HTTP File Upload) with HMAC authentication, deduplication, and multi-architecture support.
|
||
|
||
---
|
||
|
||
## What's New in 3.2 "Tremora del Terra"
|
||
|
||
### Configuration Revolution
|
||
- **93% Config Reduction**: From 112-line complex configs to 8-line minimal configs
|
||
- **Smart Defaults**: Production-ready settings built into the application
|
||
- **Auto-Generation**: `--genconfig` creates minimal configs instantly
|
||
- **Zero Breaking Changes**: Existing configs continue working
|
||
|
||
### Enhanced Performance
|
||
- **Fixed Deduplication**: Existing files return success instead of "file not found"
|
||
- **Queue Optimization**: Doubled capacity (50→100), faster scaling (80%→40% threshold)
|
||
- **Extended Timeouts**: 4800s defaults for large file reliability
|
||
- **Session Persistence**: 60-minute timeouts for enterprise transfers
|
||
|
||
### Multi-Architecture Support
|
||
- **Cross-Platform**: AMD64, ARM64, ARM32v7 with native performance
|
||
- **Interactive Builder**: Easy architecture targeting with menu system
|
||
- **Production Ready**: All platforms enterprise-grade
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
### Option 1: Minimal Configuration (Recommended)
|
||
```bash
|
||
# Download HMAC File Server 3.2
|
||
wget https://github.com/PlusOne/hmac-file-server/releases/download/v3.2/hmac-file-server-linux-amd64
|
||
chmod +x hmac-file-server-linux-amd64
|
||
|
||
# Generate minimal config
|
||
./hmac-file-server-linux-amd64 -genconfig > config.toml
|
||
|
||
# Edit 3 essential settings:
|
||
# - listen_address = "8080"
|
||
# - storage_path = "/your/storage/path"
|
||
# - secret = "your-secure-secret"
|
||
|
||
# Start server
|
||
./hmac-file-server-linux-amd64 -config config.toml
|
||
```
|
||
|
||
### Option 2: Zero-Config Startup
|
||
```bash
|
||
# Auto-creates minimal config
|
||
./hmac-file-server-linux-amd64
|
||
# Follow prompts to customize settings
|
||
```
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Release Information](#-release-information)
|
||
- [Configuration Generation](#-configuration-generation)
|
||
- [Configuration Documentation](#-configuration-documentation)
|
||
- [Build Options](#-build-options)
|
||
- [Docker Compose Examples](#-docker-compose-examples)
|
||
- [Nginx Reverse Proxy](#-nginx-reverse-proxy)
|
||
- [Apache2 Reverse Proxy](#-apache2-reverse-proxy)
|
||
- [Prosody XMPP Integration](#-prosody-xmpp-integration)
|
||
- [Ejabberd XMPP Integration](#-ejabberd-xmpp-integration)
|
||
- [XEP-0363 Implementation](#-xep-0363-implementation)
|
||
- [API Versions (V1, V2, V3)](#-api-versions)
|
||
|
||
---
|
||
|
||
## Release Information
|
||
|
||
### HMAC File Server 3.2 – Tremora del Terra
|
||
|
||
**Release Date**: July 18, 2025
|
||
**Codename**: Tremora del Terra (powerful, balanced, ready to shake the ground)
|
||
|
||
#### Key Improvements
|
||
- **Configuration Simplification**: 93% reduction in required configuration
|
||
- **Enhanced Deduplication**: Fixed "file not found" errors for existing files
|
||
- **Performance Optimization**: Doubled queue capacity, optimized worker scaling
|
||
- **Multi-Architecture Support**: Native builds for AMD64, ARM64, ARM32v7
|
||
- **Developer Experience**: Minimal config-first approach with comprehensive defaults
|
||
|
||
#### Migration Notes
|
||
- **Backward Compatible**: All existing 3.1.x configs work unchanged
|
||
- **Performance Boost**: Automatic optimizations with existing configurations
|
||
- **Optional Migration**: Users can optionally migrate to simplified 8-line configs
|
||
|
||
#### System Requirements
|
||
- **Memory**: 512MB minimum, 2GB+ recommended for large files
|
||
- **Storage**: 100MB application + user data storage
|
||
- **Network**: Standard TCP/IP connectivity
|
||
- **OS**: Linux (primary), Windows/macOS (experimental)
|
||
|
||
---
|
||
|
||
## Configuration Generation
|
||
|
||
### Generate Minimal Configuration
|
||
```bash
|
||
# Create minimal 8-line config (recommended for most users)
|
||
./hmac-file-server -genconfig > config.toml
|
||
```
|
||
|
||
**Output Example:**
|
||
```toml
|
||
# HMAC File Server - Minimal Configuration
|
||
[server]
|
||
listen_address = "8080"
|
||
storage_path = "./uploads"
|
||
|
||
[security]
|
||
secret = "your-very-secret-hmac-key"
|
||
|
||
[logging]
|
||
level = "info"
|
||
file = "/var/log/hmac-file-server.log"
|
||
```
|
||
|
||
### Generate Advanced Configuration
|
||
```bash
|
||
# Create comprehensive config template with all options
|
||
./hmac-file-server -genconfig-advanced > config-advanced.toml
|
||
```
|
||
|
||
### Write Configuration to File
|
||
```bash
|
||
# Write minimal config to specific file
|
||
./hmac-file-server -genconfig-path /etc/hmac-file-server/config.toml
|
||
|
||
# Write advanced config to specific file
|
||
./hmac-file-server -genconfig-advanced -genconfig-path /etc/hmac-file-server/config-full.toml
|
||
```
|
||
|
||
### Configuration Validation
|
||
```bash
|
||
# Validate configuration without starting server
|
||
./hmac-file-server -validate-config -config config.toml
|
||
|
||
# Quiet validation (errors only)
|
||
./hmac-file-server -validate-quiet -config config.toml
|
||
|
||
# Verbose validation with system checks
|
||
./hmac-file-server -validate-verbose -config config.toml
|
||
|
||
# Security-focused validation
|
||
./hmac-file-server -check-security -config config.toml
|
||
|
||
# Performance-focused validation
|
||
./hmac-file-server -check-performance -config config.toml
|
||
```
|
||
|
||
---
|
||
|
||
## Configuration Documentation
|
||
|
||
### Complete config.toml Line-by-Line Documentation
|
||
|
||
```toml
|
||
[server]
|
||
# Network binding configuration
|
||
listen_address = "8080" # Port to listen on (string: "8080", "0.0.0.0:8080")
|
||
bind_ip = "" # IP to bind to (empty = all interfaces)
|
||
|
||
# Storage and file handling
|
||
storage_path = "./uploads" # Directory for uploaded files
|
||
max_upload_size = "10GB" # Maximum file size (supports B, KB, MB, GB, TB)
|
||
max_header_bytes = 1048576 # HTTP header size limit (1MB default)
|
||
file_naming = "original" # File naming: "original" or "HMAC"
|
||
min_free_bytes = "1GB" # Minimum free space required
|
||
|
||
# Server behavior
|
||
cleanup_interval = "24h" # How often to run cleanup (Go duration format)
|
||
max_file_age = "720h" # Auto-delete files older than this (30 days)
|
||
force_protocol = "" # Force protocol: "", "http", "https"
|
||
|
||
# Performance and scaling
|
||
enable_dynamic_workers = true # Enable automatic worker scaling
|
||
worker_scale_up_thresh = 40 # Queue % to scale up workers (40% default)
|
||
worker_scale_down_thresh = 10 # Queue % to scale down workers (10% default)
|
||
|
||
# Metrics and monitoring
|
||
metrics_enabled = true # Enable Prometheus metrics
|
||
metrics_path = "/metrics" # Metrics endpoint path
|
||
metrics_port = "9090" # Metrics server port
|
||
|
||
# System integration
|
||
pid_file = "/var/run/hmac-file-server.pid" # Process ID file location
|
||
unix_socket = false # Use Unix socket instead of TCP
|
||
|
||
# Caching and performance
|
||
pre_cache = true # Enable file pre-caching
|
||
pre_cache_workers = 4 # Number of pre-cache workers
|
||
pre_cache_interval = "1h" # Pre-cache refresh interval
|
||
|
||
# File type handling
|
||
global_extensions = [".txt", ".pdf"] # Global allowed extensions (overrides upload/download)
|
||
deduplication_enabled = true # Enable file deduplication
|
||
|
||
# Network resilience (cluster-aware settings)
|
||
graceful_shutdown_timeout = "300s" # Time for graceful shutdown
|
||
connection_drain_timeout = "120s" # Time to drain connections
|
||
max_idle_conns_per_host = 5 # Max idle connections per client
|
||
idle_conn_timeout = "90s" # Idle connection timeout
|
||
disable_keep_alives = false # Disable HTTP keep-alives
|
||
client_timeout = "300s" # Client request timeout
|
||
restart_grace_period = "60s" # Grace period after restart
|
||
|
||
[uploads]
|
||
# File upload configuration
|
||
allowed_extensions = [".zip", ".rar"] # Permitted upload file extensions
|
||
chunked_uploads_enabled = true # Enable chunked/resumable uploads
|
||
chunk_size = "10MB" # Upload chunk size
|
||
resumable_uploads_enabled = true # Enable upload resumption
|
||
max_resumable_age = "48h" # How long to keep resumable uploads
|
||
sessiontimeout = "60m" # Upload session timeout
|
||
maxretries = 3 # Maximum upload retry attempts
|
||
|
||
# Upload resilience
|
||
session_persistence = true # Persist sessions across restarts
|
||
session_recovery_timeout = "300s" # Session recovery timeout
|
||
client_reconnect_window = "120s" # Client reconnection window
|
||
upload_slot_ttl = "3600s" # Upload slot validity time
|
||
retry_failed_uploads = true # Auto-retry failed uploads
|
||
max_upload_retries = 3 # Maximum retry attempts
|
||
|
||
[downloads]
|
||
# File download configuration
|
||
allowed_extensions = [".txt", ".pdf"] # Permitted download file extensions
|
||
chunked_downloads_enabled = true # Enable chunked downloads
|
||
chunk_size = "10MB" # Download chunk size
|
||
resumable_downloads_enabled = true # Enable download resumption
|
||
|
||
[security]
|
||
# Authentication and security
|
||
secret = "your-very-secret-hmac-key" # HMAC secret key (REQUIRED - change in production!)
|
||
enablejwt = false # Enable JWT authentication
|
||
jwtsecret = "your-256-bit-secret" # JWT signing secret
|
||
jwtalgorithm = "HS256" # JWT algorithm
|
||
jwtexpiration = "24h" # JWT token expiration
|
||
|
||
[logging]
|
||
# Logging configuration
|
||
level = "info" # Log level: debug, info, warn, error
|
||
file = "/var/log/hmac-file-server.log" # Log file path
|
||
max_size = 100 # Max log file size (MB)
|
||
max_backups = 7 # Number of log files to keep
|
||
max_age = 30 # Days to keep log files
|
||
compress = true # Compress rotated logs
|
||
|
||
[deduplication]
|
||
# File deduplication settings
|
||
enabled = true # Enable deduplication
|
||
directory = "./dedup_store" # Deduplication storage directory
|
||
maxsize = "1GB" # Maximum deduplication storage
|
||
|
||
[iso]
|
||
# ISO file handling
|
||
enabled = false # Enable ISO functionality
|
||
size = "1GB" # ISO size limit
|
||
mountpoint = "/mnt/iso" # ISO mount point
|
||
charset = "utf-8" # ISO character set
|
||
containerfile = "/mnt/iso/container.iso" # ISO container file path
|
||
|
||
[timeouts]
|
||
# Network timeout configuration
|
||
readtimeout = "4800s" # HTTP read timeout (80 minutes)
|
||
writetimeout = "4800s" # HTTP write timeout (80 minutes)
|
||
idletimeout = "4800s" # HTTP idle timeout (80 minutes)
|
||
shutdown = "30s" # Server shutdown timeout
|
||
|
||
[versioning]
|
||
# File versioning
|
||
enableversioning = false # Enable file versioning
|
||
backend = "simple" # Versioning backend
|
||
maxversions = 1 # Maximum versions to keep
|
||
|
||
[clamav]
|
||
# Antivirus scanning with ClamAV
|
||
clamavenabled = false # Enable ClamAV scanning
|
||
clamavsocket = "/var/run/clamav/clamd.ctl" # ClamAV socket path
|
||
numscanworkers = 2 # Number of scan workers
|
||
scanfileextensions = [".exe", ".zip"] # File types to scan
|
||
maxscansize = "200MB" # Maximum file size to scan
|
||
|
||
[redis]
|
||
# Redis integration
|
||
redisenabled = false # Enable Redis
|
||
redisdbindex = 0 # Redis database index
|
||
redisaddr = "localhost:6379" # Redis server address
|
||
redispassword = "" # Redis password
|
||
redishealthcheckinterval = "120s" # Redis health check interval
|
||
|
||
[workers]
|
||
# Worker pool configuration
|
||
numworkers = 4 # Number of worker threads
|
||
uploadqueuesize = 100 # Upload queue size (doubled in 3.2)
|
||
|
||
[build]
|
||
# Build information
|
||
version = "3.2" # Application version
|
||
```
|
||
|
||
---
|
||
|
||
## Build Options
|
||
|
||
### Interactive Build Script
|
||
```bash
|
||
# Use interactive build menu
|
||
./build-multi-arch.sh
|
||
```
|
||
|
||
**Menu Options:**
|
||
```
|
||
HMAC File Server Multi-Architecture Builder
|
||
==========================================
|
||
1) Build for current platform (auto-detect)
|
||
2) Build for Linux AMD64
|
||
3) Build for Linux ARM64
|
||
4) Build for Linux ARM32v7
|
||
5) Build for Windows AMD64
|
||
6) Build for macOS AMD64 (Intel)
|
||
7) Build for macOS ARM64 (Apple Silicon)
|
||
8) Build all supported architectures
|
||
9) Clean build artifacts
|
||
0) Exit
|
||
```
|
||
|
||
### Manual Go Build Commands
|
||
```bash
|
||
# Current platform
|
||
go build -o hmac-file-server ./cmd/server/
|
||
|
||
# Linux AMD64
|
||
GOOS=linux GOARCH=amd64 go build -o hmac-file-server-linux-amd64 ./cmd/server/
|
||
|
||
# Linux ARM64
|
||
GOOS=linux GOARCH=arm64 go build -o hmac-file-server-linux-arm64 ./cmd/server/
|
||
|
||
# Linux ARM32v7
|
||
GOOS=linux GOARCH=arm GOARM=7 go build -o hmac-file-server-linux-arm32v7 ./cmd/server/
|
||
|
||
# Windows AMD64
|
||
GOOS=windows GOARCH=amd64 go build -o hmac-file-server-windows-amd64.exe ./cmd/server/
|
||
|
||
# macOS Intel
|
||
GOOS=darwin GOARCH=amd64 go build -o hmac-file-server-darwin-amd64 ./cmd/server/
|
||
|
||
# macOS Apple Silicon
|
||
GOOS=darwin GOARCH=arm64 go build -o hmac-file-server-darwin-arm64 ./cmd/server/
|
||
```
|
||
|
||
### Build with Optimizations
|
||
```bash
|
||
# Production build with optimizations
|
||
go build -ldflags="-w -s" -o hmac-file-server ./cmd/server/
|
||
|
||
# Build with version info
|
||
VERSION=$(git describe --tags --always)
|
||
go build -ldflags="-X main.version=${VERSION} -w -s" -o hmac-file-server ./cmd/server/
|
||
|
||
# Static binary (for containers)
|
||
CGO_ENABLED=0 GOOS=linux go build -a -ldflags="-w -s" -o hmac-file-server ./cmd/server/
|
||
```
|
||
|
||
### Docker Build
|
||
```bash
|
||
# Build Docker image
|
||
docker build -t hmac-file-server:3.2 .
|
||
|
||
# Multi-platform Docker build
|
||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t hmac-file-server:3.2 .
|
||
```
|
||
|
||
---
|
||
|
||
## Docker Compose Examples
|
||
|
||
### Basic Docker Compose
|
||
```yaml
|
||
# docker-compose.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
hmac-file-server:
|
||
image: hmac-file-server:3.2
|
||
container_name: hmac-file-server
|
||
restart: unless-stopped
|
||
ports:
|
||
- "8080:8080"
|
||
- "9090:9090" # Metrics port
|
||
volumes:
|
||
- ./data/uploads:/app/uploads
|
||
- ./data/logs:/app/logs
|
||
- ./data/dedup:/app/dedup_store
|
||
- ./config.toml:/app/config.toml:ro
|
||
environment:
|
||
- HMAC_CONFIG_PATH=/app/config.toml
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 40s
|
||
```
|
||
|
||
### Production Docker Compose with Redis and Monitoring
|
||
```yaml
|
||
# docker-compose.prod.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
hmac-file-server:
|
||
image: hmac-file-server:3.2
|
||
container_name: hmac-file-server
|
||
restart: unless-stopped
|
||
depends_on:
|
||
- redis
|
||
- prometheus
|
||
ports:
|
||
- "8080:8080"
|
||
volumes:
|
||
- uploads:/app/uploads
|
||
- dedup:/app/dedup_store
|
||
- logs:/app/logs
|
||
- ./config/config.toml:/app/config.toml:ro
|
||
environment:
|
||
- HMAC_CONFIG_PATH=/app/config.toml
|
||
networks:
|
||
- hmac-network
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
container_name: hmac-redis
|
||
restart: unless-stopped
|
||
volumes:
|
||
- redis_data:/data
|
||
networks:
|
||
- hmac-network
|
||
command: redis-server --appendonly yes
|
||
|
||
prometheus:
|
||
image: prom/prometheus:latest
|
||
container_name: hmac-prometheus
|
||
restart: unless-stopped
|
||
ports:
|
||
- "9090:9090"
|
||
volumes:
|
||
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||
- prometheus_data:/prometheus
|
||
networks:
|
||
- hmac-network
|
||
command:
|
||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||
- '--storage.tsdb.path=/prometheus'
|
||
|
||
grafana:
|
||
image: grafana/grafana:latest
|
||
container_name: hmac-grafana
|
||
restart: unless-stopped
|
||
ports:
|
||
- "3000:3000"
|
||
environment:
|
||
- GF_SECURITY_ADMIN_PASSWORD=admin123
|
||
volumes:
|
||
- grafana_data:/var/lib/grafana
|
||
- ./config/grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro
|
||
networks:
|
||
- hmac-network
|
||
|
||
nginx:
|
||
image: nginx:alpine
|
||
container_name: hmac-nginx
|
||
restart: unless-stopped
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
volumes:
|
||
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
|
||
- ./config/ssl:/etc/nginx/ssl:ro
|
||
depends_on:
|
||
- hmac-file-server
|
||
networks:
|
||
- hmac-network
|
||
|
||
volumes:
|
||
uploads:
|
||
dedup:
|
||
logs:
|
||
redis_data:
|
||
prometheus_data:
|
||
grafana_data:
|
||
|
||
networks:
|
||
hmac-network:
|
||
driver: bridge
|
||
```
|
||
|
||
### Docker Compose with ClamAV
|
||
```yaml
|
||
# docker-compose.clamav.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
hmac-file-server:
|
||
image: hmac-file-server:3.2
|
||
container_name: hmac-file-server
|
||
restart: unless-stopped
|
||
depends_on:
|
||
- clamav
|
||
ports:
|
||
- "8080:8080"
|
||
volumes:
|
||
- ./data/uploads:/app/uploads
|
||
- ./data/logs:/app/logs
|
||
- ./config.toml:/app/config.toml:ro
|
||
- clamav_socket:/var/run/clamav
|
||
networks:
|
||
- hmac-network
|
||
|
||
clamav:
|
||
image: clamav/clamav:latest
|
||
container_name: hmac-clamav
|
||
restart: unless-stopped
|
||
volumes:
|
||
- clamav_db:/var/lib/clamav
|
||
- clamav_socket:/var/run/clamav
|
||
networks:
|
||
- hmac-network
|
||
environment:
|
||
- CLAMAV_NO_FRESHCLAMD=false
|
||
- CLAMAV_NO_CLAMD=false
|
||
|
||
volumes:
|
||
clamav_db:
|
||
clamav_socket:
|
||
|
||
networks:
|
||
hmac-network:
|
||
driver: bridge
|
||
```
|
||
|
||
---
|
||
|
||
## Nginx Reverse Proxy
|
||
|
||
### Basic Nginx Configuration
|
||
```nginx
|
||
# /etc/nginx/sites-available/hmac-file-server
|
||
server {
|
||
listen 80;
|
||
server_name files.example.com;
|
||
return 301 https://$server_name$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name files.example.com;
|
||
|
||
# SSL Configuration
|
||
ssl_certificate /etc/ssl/certs/files.example.com.crt;
|
||
ssl_certificate_key /etc/ssl/private/files.example.com.key;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||
ssl_prefer_server_ciphers off;
|
||
ssl_session_cache shared:SSL:10m;
|
||
|
||
# File upload limits
|
||
client_max_body_size 10G;
|
||
client_body_timeout 300s;
|
||
client_header_timeout 300s;
|
||
|
||
# Proxy settings for large files
|
||
proxy_connect_timeout 300s;
|
||
proxy_send_timeout 300s;
|
||
proxy_read_timeout 300s;
|
||
proxy_request_buffering off;
|
||
proxy_buffering off;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1: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;
|
||
|
||
# WebSocket support (if needed)
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
}
|
||
|
||
# Metrics endpoint (optional - restrict access)
|
||
location /metrics {
|
||
proxy_pass http://127.0.0.1:9090;
|
||
allow 10.0.0.0/8;
|
||
allow 172.16.0.0/12;
|
||
allow 192.168.0.0/16;
|
||
deny all;
|
||
}
|
||
|
||
# Health check
|
||
location /health {
|
||
proxy_pass http://127.0.0.1:8080;
|
||
access_log off;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Advanced Nginx Configuration with Load Balancing
|
||
```nginx
|
||
# /etc/nginx/nginx.conf
|
||
upstream hmac_backend {
|
||
least_conn;
|
||
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
|
||
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s backup;
|
||
keepalive 32;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name files.example.com;
|
||
|
||
# Security headers
|
||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||
add_header X-Frame-Options DENY always;
|
||
add_header X-Content-Type-Options nosniff always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
|
||
# Rate limiting
|
||
limit_req_zone $binary_remote_addr zone=upload:10m rate=10r/m;
|
||
limit_req_zone $binary_remote_addr zone=download:10m rate=100r/m;
|
||
|
||
location /upload {
|
||
limit_req zone=upload burst=5 nodelay;
|
||
proxy_pass http://hmac_backend;
|
||
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;
|
||
}
|
||
|
||
location /download {
|
||
limit_req zone=download burst=20 nodelay;
|
||
proxy_pass http://hmac_backend;
|
||
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;
|
||
}
|
||
|
||
location / {
|
||
proxy_pass http://hmac_backend;
|
||
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;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Apache2 Reverse Proxy
|
||
|
||
### Basic Apache2 Configuration
|
||
```apache
|
||
# /etc/apache2/sites-available/hmac-file-server.conf
|
||
<VirtualHost *:80>
|
||
ServerName files.example.com
|
||
Redirect permanent / https://files.example.com/
|
||
</VirtualHost>
|
||
|
||
<VirtualHost *:443>
|
||
ServerName files.example.com
|
||
|
||
# SSL Configuration
|
||
SSLEngine on
|
||
SSLCertificateFile /etc/ssl/certs/files.example.com.crt
|
||
SSLCertificateKeyFile /etc/ssl/private/files.example.com.key
|
||
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
|
||
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305
|
||
SSLHonorCipherOrder off
|
||
SSLSessionTickets off
|
||
|
||
# Security Headers
|
||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||
Header always set X-Frame-Options DENY
|
||
Header always set X-Content-Type-Options nosniff
|
||
Header always set X-XSS-Protection "1; mode=block"
|
||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||
|
||
# File upload limits
|
||
LimitRequestBody 10737418240 # 10GB
|
||
|
||
# Proxy configuration
|
||
ProxyPreserveHost On
|
||
ProxyRequests Off
|
||
|
||
# Main proxy
|
||
ProxyPass / http://127.0.0.1:8080/
|
||
ProxyPassReverse / http://127.0.0.1:8080/
|
||
|
||
# Set headers for the backend
|
||
ProxyPassReverse / http://127.0.0.1:8080/
|
||
ProxySetEnv proxy-initial-not-pooled 1
|
||
|
||
# Timeout settings
|
||
ProxyTimeout 300
|
||
|
||
# Metrics endpoint (restricted)
|
||
<Location "/metrics">
|
||
ProxyPass http://127.0.0.1:9090/
|
||
ProxyPassReverse http://127.0.0.1:9090/
|
||
Require ip 10.0.0.0/8
|
||
Require ip 172.16.0.0/12
|
||
Require ip 192.168.0.0/16
|
||
</Location>
|
||
|
||
# Logging
|
||
ErrorLog ${APACHE_LOG_DIR}/hmac-file-server_error.log
|
||
CustomLog ${APACHE_LOG_DIR}/hmac-file-server_access.log combined
|
||
</VirtualHost>
|
||
```
|
||
|
||
### Advanced Apache2 Configuration with Load Balancing
|
||
```apache
|
||
# Enable required modules
|
||
# a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests ssl headers
|
||
|
||
# /etc/apache2/sites-available/hmac-file-server-lb.conf
|
||
<Proxy "balancer://hmac-cluster">
|
||
BalancerMember http://127.0.0.1:8080 status=+H
|
||
BalancerMember http://127.0.0.1:8081 status=+H
|
||
ProxySet lbmethod=byrequests
|
||
</Proxy>
|
||
|
||
<VirtualHost *:443>
|
||
ServerName files.example.com
|
||
|
||
# SSL Configuration (same as basic)
|
||
SSLEngine on
|
||
SSLCertificateFile /etc/ssl/certs/files.example.com.crt
|
||
SSLCertificateKeyFile /etc/ssl/private/files.example.com.key
|
||
|
||
# Rate limiting (requires mod_evasive)
|
||
DOSHashTableSize 8192
|
||
DOSPageCount 3
|
||
DOSPageInterval 1
|
||
DOSEmailNotify admin@example.com
|
||
|
||
# Proxy to load balancer
|
||
ProxyPreserveHost On
|
||
ProxyPass / balancer://hmac-cluster/
|
||
ProxyPassReverse / balancer://hmac-cluster/
|
||
|
||
# Balancer manager (restrict access)
|
||
<Location "/balancer-manager">
|
||
SetHandler balancer-manager
|
||
Require ip 127.0.0.1
|
||
Require ip 10.0.0.0/8
|
||
</Location>
|
||
ProxyPass /balancer-manager !
|
||
|
||
# Health check
|
||
<Location "/health">
|
||
ProxyPass http://127.0.0.1:8080/health
|
||
ProxyPassReverse http://127.0.0.1:8080/health
|
||
</Location>
|
||
</VirtualHost>
|
||
```
|
||
|
||
---
|
||
|
||
## Prosody XMPP Integration
|
||
|
||
### Prosody Configuration
|
||
```lua
|
||
-- /etc/prosody/prosody.cfg.lua
|
||
-- HMAC File Server integration for XEP-0363
|
||
|
||
-- Enable HTTP file upload module
|
||
modules_enabled = {
|
||
-- Core modules
|
||
"roster";
|
||
"saslauth";
|
||
"tls";
|
||
"dialback";
|
||
"disco";
|
||
"carbons";
|
||
"pep";
|
||
"private";
|
||
"blocklist";
|
||
"vcard4";
|
||
"vcard_legacy";
|
||
"version";
|
||
"uptime";
|
||
"time";
|
||
"ping";
|
||
"admin_adhoc";
|
||
|
||
-- HTTP file upload
|
||
"http_upload_external";
|
||
}
|
||
|
||
-- VirtualHost configuration
|
||
VirtualHost "example.com"
|
||
enabled = true
|
||
|
||
-- SSL configuration
|
||
ssl = {
|
||
key = "/etc/prosody/certs/example.com.key";
|
||
certificate = "/etc/prosody/certs/example.com.crt";
|
||
}
|
||
|
||
-- HTTP file upload configuration
|
||
http_upload_external_base_url = "https://files.example.com"
|
||
http_upload_external_secret = "your-very-secret-hmac-key"
|
||
http_upload_external_file_size_limit = 10737418240 -- 10GB
|
||
http_upload_external_quota = 1073741824000 -- 1TB per user
|
||
|
||
-- Custom upload URL patterns (for HMAC File Server)
|
||
http_upload_external_put_url = "https://files.example.com/upload/{filename}"
|
||
http_upload_external_get_url = "https://files.example.com/download/{filename}"
|
||
|
||
-- Component for file upload service
|
||
Component "upload.example.com" "http_upload_external"
|
||
http_upload_external_base_url = "https://files.example.com"
|
||
http_upload_external_secret = "your-very-secret-hmac-key"
|
||
http_upload_external_file_size_limit = 10737418240
|
||
|
||
-- Logging
|
||
log = {
|
||
info = "/var/log/prosody/prosody.log";
|
||
error = "/var/log/prosody/prosody.err";
|
||
"*syslog";
|
||
}
|
||
```
|
||
|
||
### Prosody Module Configuration
|
||
```lua
|
||
-- /usr/lib/prosody/modules/mod_http_upload_external.lua
|
||
-- Custom module for HMAC File Server integration
|
||
|
||
local hmac_sha256 = require "util.hashes".hmac_sha256;
|
||
local base64 = require "util.encodings".base64;
|
||
local uuid = require "util.uuid".generate;
|
||
local http = require "net.http";
|
||
|
||
module:depends("disco");
|
||
|
||
local external_base_url = module:get_option_string("http_upload_external_base_url");
|
||
local external_secret = module:get_option_string("http_upload_external_secret");
|
||
local file_size_limit = module:get_option_number("http_upload_external_file_size_limit", 100*1024*1024);
|
||
local quota = module:get_option_number("http_upload_external_quota", 1024*1024*1024);
|
||
|
||
-- XEP-0363 disco feature
|
||
module:add_feature("urn:xmpp:http:upload:0");
|
||
|
||
-- Handle upload requests
|
||
function handle_upload_request(event)
|
||
local stanza = event.stanza;
|
||
local filename = stanza:get_child_text("filename", "urn:xmpp:http:upload:0");
|
||
local filesize = tonumber(stanza:get_child_text("size", "urn:xmpp:http:upload:0"));
|
||
local content_type = stanza:get_child_text("content-type", "urn:xmpp:http:upload:0") or "application/octet-stream";
|
||
|
||
if not filename or not filesize then
|
||
return st.error_reply(stanza, "modify", "bad-request", "Missing filename or size");
|
||
end
|
||
|
||
if filesize > file_size_limit then
|
||
return st.error_reply(stanza, "modify", "not-acceptable", "File too large");
|
||
end
|
||
|
||
-- Generate HMAC authentication
|
||
local timestamp = os.time();
|
||
local upload_id = uuid();
|
||
local message = filename .. filesize .. timestamp .. upload_id;
|
||
local signature = base64.encode(hmac_sha256(external_secret, message));
|
||
|
||
-- Construct URLs
|
||
local put_url = string.format("%s/upload?filename=%s×tamp=%d&uploadid=%s&signature=%s",
|
||
external_base_url,
|
||
filename,
|
||
timestamp,
|
||
upload_id,
|
||
signature
|
||
);
|
||
|
||
local get_url = string.format("%s/download/%s", external_base_url, filename);
|
||
|
||
-- Return slot
|
||
local reply = st.reply(stanza)
|
||
:tag("slot", {xmlns="urn:xmpp:http:upload:0"})
|
||
:tag("put", {url=put_url}):up()
|
||
:tag("get", {url=get_url}):up()
|
||
:up();
|
||
|
||
return reply;
|
||
end
|
||
|
||
module:hook("iq-get/host/urn:xmpp:http:upload:0:request", handle_upload_request);
|
||
```
|
||
|
||
---
|
||
|
||
## Ejabberd XMPP Integration
|
||
|
||
### Ejabberd Configuration
|
||
```yaml
|
||
# /etc/ejabberd/ejabberd.yml
|
||
# HMAC File Server integration
|
||
|
||
hosts:
|
||
- "example.com"
|
||
|
||
listen:
|
||
-
|
||
port: 5222
|
||
ip: "::"
|
||
module: ejabberd_c2s
|
||
starttls: true
|
||
certfile: "/etc/ejabberd/certs/example.com.pem"
|
||
|
||
-
|
||
port: 5269
|
||
ip: "::"
|
||
module: ejabberd_s2s_in
|
||
|
||
-
|
||
port: 5443
|
||
ip: "::"
|
||
module: ejabberd_http
|
||
tls: true
|
||
certfile: "/etc/ejabberd/certs/example.com.pem"
|
||
request_handlers:
|
||
"/upload": mod_http_upload
|
||
"/admin": ejabberd_web_admin
|
||
"/api": mod_http_api
|
||
|
||
modules:
|
||
mod_adhoc: {}
|
||
mod_admin_extra: {}
|
||
mod_announce: {}
|
||
mod_avatar: {}
|
||
mod_blocking: {}
|
||
mod_bosh: {}
|
||
mod_caps: {}
|
||
mod_carboncopy: {}
|
||
mod_client_state: {}
|
||
mod_configure: {}
|
||
mod_disco: {}
|
||
mod_fail2ban: {}
|
||
mod_http_api: {}
|
||
mod_http_upload:
|
||
put_url: "https://files.example.com/upload"
|
||
get_url: "https://files.example.com/download"
|
||
external_secret: "your-very-secret-hmac-key"
|
||
max_size: 10737418240 # 10GB
|
||
thumbnail: false
|
||
custom_headers:
|
||
"Access-Control-Allow-Origin": "*"
|
||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
|
||
"Access-Control-Allow-Headers": "Content-Type"
|
||
mod_last: {}
|
||
mod_mam: {}
|
||
mod_mqtt: {}
|
||
mod_muc: {}
|
||
mod_muc_admin: {}
|
||
mod_offline: {}
|
||
mod_ping: {}
|
||
mod_privacy: {}
|
||
mod_private: {}
|
||
mod_proxy65: {}
|
||
mod_pubsub: {}
|
||
mod_push: {}
|
||
mod_register: {}
|
||
mod_roster: {}
|
||
mod_shared_roster: {}
|
||
mod_stats: {}
|
||
mod_time: {}
|
||
mod_vcard: {}
|
||
mod_version: {}
|
||
|
||
# Authentication
|
||
auth_method: internal
|
||
|
||
# Database
|
||
default_db: mnesia
|
||
|
||
# Access rules
|
||
access_rules:
|
||
local:
|
||
- allow: local
|
||
c2s:
|
||
- deny: blocked
|
||
- allow
|
||
announce:
|
||
- allow: admin
|
||
configure:
|
||
- allow: admin
|
||
muc_create:
|
||
- allow: local
|
||
pubsub_createnode:
|
||
- allow: local
|
||
register:
|
||
- allow
|
||
trusted_network:
|
||
- allow: loopback
|
||
|
||
# ACL
|
||
acl:
|
||
local:
|
||
user_regexp: ""
|
||
loopback:
|
||
ip:
|
||
- "127.0.0.0/8"
|
||
- "::1/128"
|
||
- "::FFFF:127.0.0.1/128"
|
||
admin:
|
||
user:
|
||
- "admin@example.com"
|
||
|
||
# Logging
|
||
loglevel: 4
|
||
log_rotate_size: 10485760
|
||
log_rotate_count: 5
|
||
```
|
||
|
||
### Custom Ejabberd HTTP Upload Module
|
||
```erlang
|
||
% /opt/ejabberd/lib/ejabberd-23.01/ebin/mod_http_upload_external.erl
|
||
% Custom module for HMAC File Server integration
|
||
|
||
-module(mod_http_upload_external).
|
||
-author('admin@example.com').
|
||
|
||
-behaviour(gen_mod).
|
||
|
||
-export([start/2, stop/1, process_iq/1, mod_opt_type/1, mod_options/1]).
|
||
|
||
-include("ejabberd.hrl").
|
||
-include("logger.hrl").
|
||
-include("xmpp.hrl").
|
||
|
||
start(Host, Opts) ->
|
||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||
?NS_HTTP_UPLOAD_0, ?MODULE,
|
||
process_iq).
|
||
|
||
stop(Host) ->
|
||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_HTTP_UPLOAD_0).
|
||
|
||
process_iq(#iq{type = get, sub_els = [#upload_request{filename = Filename,
|
||
size = Size,
|
||
'content-type' = ContentType}]} = IQ) ->
|
||
Host = ejabberd_config:get_myname(),
|
||
|
||
% Get configuration
|
||
PutURL = gen_mod:get_module_opt(Host, ?MODULE, put_url),
|
||
GetURL = gen_mod:get_module_opt(Host, ?MODULE, get_url),
|
||
Secret = gen_mod:get_module_opt(Host, ?MODULE, external_secret),
|
||
MaxSize = gen_mod:get_module_opt(Host, ?MODULE, max_size),
|
||
|
||
% Validate file size
|
||
case Size =< MaxSize of
|
||
true ->
|
||
% Generate HMAC signature
|
||
Timestamp = erlang:system_time(second),
|
||
UploadId = uuid:uuid_to_string(uuid:get_v4()),
|
||
Message = <<Filename/binary, (integer_to_binary(Size))/binary,
|
||
(integer_to_binary(Timestamp))/binary, UploadId/binary>>,
|
||
Signature = base64:encode(crypto:mac(hmac, sha256, Secret, Message)),
|
||
|
||
% Construct URLs
|
||
PutURLFinal = <<PutURL/binary, "?filename=", Filename/binary,
|
||
"×tamp=", (integer_to_binary(Timestamp))/binary,
|
||
"&uploadid=", UploadId/binary,
|
||
"&signature=", Signature/binary>>,
|
||
GetURLFinal = <<GetURL/binary, "/", Filename/binary>>,
|
||
|
||
% Return slot
|
||
Slot = #upload_slot{get = GetURLFinal, put = PutURLFinal},
|
||
xmpp:make_iq_result(IQ, Slot);
|
||
false ->
|
||
xmpp:make_error(IQ, xmpp:err_not_acceptable(<<"File too large">>, ?MYLANG))
|
||
end;
|
||
|
||
process_iq(IQ) ->
|
||
xmpp:make_error(IQ, xmpp:err_bad_request()).
|
||
|
||
mod_opt_type(put_url) -> fun iolist_to_binary/1;
|
||
mod_opt_type(get_url) -> fun iolist_to_binary/1;
|
||
mod_opt_type(external_secret) -> fun iolist_to_binary/1;
|
||
mod_opt_type(max_size) -> fun(I) when is_integer(I), I > 0 -> I end.
|
||
|
||
mod_options(_Host) ->
|
||
[{put_url, <<"">>},
|
||
{get_url, <<"">>},
|
||
{external_secret, <<"">>},
|
||
{max_size, 104857600}].
|
||
```
|
||
|
||
---
|
||
|
||
## XEP-0363 Implementation
|
||
|
||
### XEP-0363: HTTP File Upload
|
||
|
||
HMAC File Server implements [XEP-0363: HTTP File Upload](https://xmpp.org/extensions/xep-0363.html) with HMAC authentication for secure file sharing in XMPP environments.
|
||
|
||
#### Protocol Flow
|
||
|
||
1. **Discovery**: Client discovers upload service
|
||
2. **Request Slot**: Client requests upload/download URLs
|
||
3. **Upload**: Client uploads file to provided PUT URL
|
||
4. **Share**: Client shares GET URL with contacts
|
||
5. **Download**: Recipients download using GET URL
|
||
|
||
#### Implementation Details
|
||
|
||
```xml
|
||
<!-- 1. Service Discovery -->
|
||
<iq type='get' to='upload.example.com' id='discover'>
|
||
<query xmlns='http://jabber.org/protocol/disco#info'/>
|
||
</iq>
|
||
|
||
<!-- Response -->
|
||
<iq type='result' from='upload.example.com' id='discover'>
|
||
<query xmlns='http://jabber.org/protocol/disco#info'>
|
||
<feature var='urn:xmpp:http:upload:0'/>
|
||
<x xmlns='jabber:x:data' type='result'>
|
||
<field var='max-file-size'>
|
||
<value>10737418240</value> <!-- 10GB -->
|
||
</field>
|
||
</x>
|
||
</query>
|
||
</iq>
|
||
|
||
<!-- 2. Request Upload Slot -->
|
||
<iq type='get' to='upload.example.com' id='upload1'>
|
||
<request xmlns='urn:xmpp:http:upload:0' filename='document.pdf' size='1048576' content-type='application/pdf'/>
|
||
</iq>
|
||
|
||
<!-- Response with URLs -->
|
||
<iq type='result' from='upload.example.com' id='upload1'>
|
||
<slot xmlns='urn:xmpp:http:upload:0'>
|
||
<put url='https://files.example.com/upload?filename=document.pdf&timestamp=1642678800&uploadid=550e8400-e29b-41d4-a716-446655440000&signature=YWJjZGVmZ2hpams='/>
|
||
<get url='https://files.example.com/download/document.pdf'/>
|
||
</slot>
|
||
</iq>
|
||
|
||
<!-- 3. HTTP Upload -->
|
||
PUT /upload?filename=document.pdf×tamp=1642678800&uploadid=550e8400-e29b-41d4-a716-446655440000&signature=YWJjZGVmZ2hpams= HTTP/1.1
|
||
Host: files.example.com
|
||
Content-Type: application/pdf
|
||
Content-Length: 1048576
|
||
|
||
[Binary file data]
|
||
|
||
<!-- 4. Share Download URL -->
|
||
<message to='friend@example.com' type='chat'>
|
||
<body>Check out this document:</body>
|
||
<x xmlns='jabber:x:oob'>
|
||
<url>https://files.example.com/download/document.pdf</url>
|
||
</x>
|
||
</message>
|
||
```
|
||
|
||
#### HMAC Authentication
|
||
|
||
HMAC File Server uses HMAC-SHA256 for authentication:
|
||
|
||
```
|
||
message = filename + filesize + timestamp + upload_id
|
||
signature = base64(hmac_sha256(secret, message))
|
||
```
|
||
|
||
#### Configuration for XEP-0363
|
||
|
||
```toml
|
||
[server]
|
||
listen_address = "8080"
|
||
storage_path = "/var/lib/hmac-file-server/uploads"
|
||
|
||
[security]
|
||
secret = "your-xmpp-upload-secret"
|
||
|
||
[uploads]
|
||
allowed_extensions = [".jpg", ".png", ".gif", ".pdf", ".doc", ".zip"]
|
||
max_upload_size = "10GB"
|
||
chunked_uploads_enabled = true
|
||
resumable_uploads_enabled = true
|
||
|
||
[timeouts]
|
||
readtimeout = "300s"
|
||
writetimeout = "300s"
|
||
idletimeout = "300s"
|
||
```
|
||
|
||
---
|
||
|
||
## API Versions
|
||
|
||
### Overview
|
||
|
||
HMAC File Server supports multiple API versions to ensure compatibility with different XMPP clients and custom integrations.
|
||
|
||
### V1 API (Legacy Support)
|
||
|
||
**Endpoint**: `/api/v1/upload`
|
||
**Authentication**: Basic HMAC
|
||
**Usage**: Legacy XMPP clients, basic integrations
|
||
|
||
```bash
|
||
# V1 Upload
|
||
curl -X POST "https://files.example.com/api/v1/upload" \
|
||
-H "Authorization: HMAC-SHA256 signature" \
|
||
-H "Content-Type: multipart/form-data" \
|
||
-F "file=@document.pdf"
|
||
|
||
# Response
|
||
{
|
||
"success": true,
|
||
"url": "https://files.example.com/download/document.pdf",
|
||
"filename": "document.pdf",
|
||
"size": 1048576
|
||
}
|
||
```
|
||
|
||
### V2 API (Enhanced)
|
||
|
||
**Endpoint**: `/api/v2/upload`
|
||
**Authentication**: Enhanced HMAC with timestamps
|
||
**Usage**: Modern XMPP clients, advanced features
|
||
|
||
```bash
|
||
# V2 Upload with metadata
|
||
curl -X POST "https://files.example.com/api/v2/upload" \
|
||
-H "Authorization: HMAC-SHA256-V2 signature" \
|
||
-H "X-Upload-Timestamp: 1642678800" \
|
||
-H "X-Upload-ID: 550e8400-e29b-41d4-a716-446655440000" \
|
||
-H "Content-Type: multipart/form-data" \
|
||
-F "file=@document.pdf" \
|
||
-F "metadata={\"title\":\"My Document\",\"tags\":[\"important\"]}"
|
||
|
||
# Response
|
||
{
|
||
"success": true,
|
||
"version": "v2",
|
||
"upload_id": "550e8400-e29b-41d4-a716-446655440000",
|
||
"url": "https://files.example.com/download/document.pdf",
|
||
"thumbnail_url": "https://files.example.com/thumbnail/document.pdf",
|
||
"filename": "document.pdf",
|
||
"size": 1048576,
|
||
"content_type": "application/pdf",
|
||
"checksum": "sha256:d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2",
|
||
"expires_at": "2024-01-21T12:00:00Z",
|
||
"metadata": {
|
||
"title": "My Document",
|
||
"tags": ["important"]
|
||
}
|
||
}
|
||
```
|
||
|
||
### V3 API (Custom/Experimental)
|
||
|
||
**Endpoint**: `/api/v3/upload`
|
||
**Authentication**: JWT or Enhanced HMAC
|
||
**Usage**: Custom clients, experimental features
|
||
**Note**: V3 is not a defined standard - custom implementation
|
||
|
||
```bash
|
||
# V3 Upload with JWT
|
||
curl -X POST "https://files.example.com/api/v3/upload" \
|
||
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"filename": "document.pdf",
|
||
"content_type": "application/pdf",
|
||
"size": 1048576,
|
||
"checksum": "sha256:d2d2d2d2...",
|
||
"options": {
|
||
"chunked": true,
|
||
"resumable": true,
|
||
"encryption": "aes256",
|
||
"compression": "gzip"
|
||
}
|
||
}'
|
||
|
||
# Response
|
||
{
|
||
"success": true,
|
||
"version": "v3",
|
||
"session_id": "sess_550e8400-e29b-41d4-a716-446655440000",
|
||
"upload_urls": {
|
||
"put": "https://files.example.com/api/v3/upload/sess_550e8400-e29b-41d4-a716-446655440000",
|
||
"chunks": [
|
||
"https://files.example.com/api/v3/chunk/1",
|
||
"https://files.example.com/api/v3/chunk/2"
|
||
]
|
||
},
|
||
"download_url": "https://files.example.com/api/v3/download/document.pdf",
|
||
"websocket_url": "wss://files.example.com/api/v3/progress/sess_550e8400-e29b-41d4-a716-446655440000",
|
||
"capabilities": [
|
||
"chunked_upload",
|
||
"resumable_upload",
|
||
"deduplication",
|
||
"encryption",
|
||
"compression",
|
||
"real_time_progress"
|
||
],
|
||
"expires_at": "2024-01-21T12:00:00Z"
|
||
}
|
||
```
|
||
|
||
### Token Formats
|
||
|
||
#### V1 Token (Basic HMAC)
|
||
```
|
||
Authorization: HMAC-SHA256 <base64(hmac_sha256(secret, message))>
|
||
message = filename + content_type + timestamp
|
||
```
|
||
|
||
#### V2 Token (Enhanced HMAC)
|
||
```
|
||
Authorization: HMAC-SHA256-V2 <base64(hmac_sha256(secret, message))>
|
||
message = method + uri + timestamp + upload_id + content_hash
|
||
```
|
||
|
||
#### V3 Token (JWT)
|
||
```javascript
|
||
// JWT Header
|
||
{
|
||
"alg": "HS256",
|
||
"typ": "JWT"
|
||
}
|
||
|
||
// JWT Payload
|
||
{
|
||
"iss": "hmac-file-server",
|
||
"sub": "user@example.com",
|
||
"aud": "files.example.com",
|
||
"exp": 1642682400,
|
||
"iat": 1642678800,
|
||
"jti": "550e8400-e29b-41d4-a716-446655440000",
|
||
"scope": ["upload", "download", "delete"],
|
||
"upload_quota": 10737418240,
|
||
"allowed_types": ["image/*", "application/pdf"]
|
||
}
|
||
```
|
||
|
||
### Client Implementation Examples
|
||
|
||
#### JavaScript (V2 API)
|
||
```javascript
|
||
class HMACFileClient {
|
||
constructor(baseUrl, secret) {
|
||
this.baseUrl = baseUrl;
|
||
this.secret = secret;
|
||
}
|
||
|
||
async upload(file, metadata = {}) {
|
||
const timestamp = Math.floor(Date.now() / 1000);
|
||
const uploadId = this.generateUUID();
|
||
const message = `POST/api/v2/upload${timestamp}${uploadId}${file.name}${file.size}`;
|
||
const signature = await this.hmacSHA256(this.secret, message);
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
formData.append('metadata', JSON.stringify(metadata));
|
||
|
||
const response = await fetch(`${this.baseUrl}/api/v2/upload`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `HMAC-SHA256-V2 ${signature}`,
|
||
'X-Upload-Timestamp': timestamp.toString(),
|
||
'X-Upload-ID': uploadId
|
||
},
|
||
body: formData
|
||
});
|
||
|
||
return response.json();
|
||
}
|
||
|
||
async hmacSHA256(key, message) {
|
||
const encoder = new TextEncoder();
|
||
const keyData = encoder.encode(key);
|
||
const messageData = encoder.encode(message);
|
||
|
||
const cryptoKey = await crypto.subtle.importKey(
|
||
'raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
|
||
);
|
||
|
||
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
|
||
return btoa(String.fromCharCode(...new Uint8Array(signature)));
|
||
}
|
||
|
||
generateUUID() {
|
||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||
const r = Math.random() * 16 | 0;
|
||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||
return v.toString(16);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Python (V3 API with JWT)
|
||
```python
|
||
import jwt
|
||
import requests
|
||
import json
|
||
from datetime import datetime, timedelta
|
||
|
||
class HMACFileClientV3:
|
||
def __init__(self, base_url, jwt_secret):
|
||
self.base_url = base_url
|
||
self.jwt_secret = jwt_secret
|
||
|
||
def generate_token(self, user_id, scopes=['upload', 'download']):
|
||
payload = {
|
||
'iss': 'hmac-file-server',
|
||
'sub': user_id,
|
||
'aud': self.base_url,
|
||
'exp': datetime.utcnow() + timedelta(hours=1),
|
||
'iat': datetime.utcnow(),
|
||
'scope': scopes
|
||
}
|
||
return jwt.encode(payload, self.jwt_secret, algorithm='HS256')
|
||
|
||
def upload(self, filename, content_type, size, user_id='anonymous'):
|
||
token = self.generate_token(user_id)
|
||
|
||
headers = {
|
||
'Authorization': f'Bearer {token}',
|
||
'Content-Type': 'application/json'
|
||
}
|
||
|
||
data = {
|
||
'filename': filename,
|
||
'content_type': content_type,
|
||
'size': size,
|
||
'options': {
|
||
'chunked': True,
|
||
'resumable': True
|
||
}
|
||
}
|
||
|
||
response = requests.post(
|
||
f'{self.base_url}/api/v3/upload',
|
||
headers=headers,
|
||
json=data
|
||
)
|
||
|
||
return response.json()
|
||
```
|
||
|
||
---
|
||
|
||
## Quick Reference
|
||
|
||
### Essential Commands
|
||
```bash
|
||
# Generate minimal config
|
||
./hmac-file-server -genconfig > config.toml
|
||
|
||
# Start with config validation
|
||
./hmac-file-server -validate-config -config config.toml
|
||
|
||
# Start server
|
||
./hmac-file-server -config config.toml
|
||
|
||
# Build for multiple architectures
|
||
./build-multi-arch.sh
|
||
```
|
||
|
||
### Minimal Production Config
|
||
```toml
|
||
[server]
|
||
listen_address = "8080"
|
||
storage_path = "/opt/hmac-file-server/uploads"
|
||
|
||
[security]
|
||
secret = "your-production-secret-here"
|
||
|
||
[logging]
|
||
level = "info"
|
||
file = "/var/log/hmac-file-server.log"
|
||
```
|
||
|
||
### Health Check
|
||
```bash
|
||
curl -f http://localhost:8080/health
|
||
```
|
||
|
||
### Metrics
|
||
```bash
|
||
curl http://localhost:9090/metrics
|
||
```
|
||
|
||
---
|
||
|
||
## Contributing
|
||
|
||
1. Fork the repository
|
||
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
||
3. Commit changes: `git commit -m 'Add amazing feature'`
|
||
4. Push to branch: `git push origin feature/amazing-feature`
|
||
5. Open a Pull Request
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||
|
||
---
|
||
|
||
## Links
|
||
|
||
- **GitHub**: https://github.com/PlusOne/hmac-file-server
|
||
- **Documentation**: https://hmac-file-server.readthedocs.io
|
||
- **Issues**: https://github.com/PlusOne/hmac-file-server/issues
|
||
- **Releases**: https://github.com/PlusOne/hmac-file-server/releases
|
||
|
||
---
|
||
|
||
*HMAC File Server 3.2 "Tremora del Terra" – Where enterprise power meets user simplicity*
|