A Starlette-based proxy service that enables you to serve your custom S3 service under a different domain while maintaining AWS Signature V4 compatibility.
When you have a custom S3 service running on s3.example.com and want to serve it under s3.mydomain.com, AWS Signature V4 authentication breaks because the signature includes the host header in its calculation. Simply proxying requests fails because:
s3.mydomain.com but the origin server expects s3.example.comThis proxy provides a dual-credential system that:
This allows clients to use a single set of credentials while the proxy manages multiple S3 backends transparently.
/healthz)# Pull from Docker Hub
docker pull s4l3h1/s3proxy:latest
# Run directly
docker run -p 8000:8000 \
-e AWS_ACCESS_KEY="your_key" \
-e AWS_SECRET_KEY="your_secret" \
-e ORIGIN_DOMAIN="s3.example.com" \
s4l3h1/s3proxy:latest
# Clone the repository
git clone https://github.com/salehi/s3proxy.git
cd s3proxy
# Install dependencies
pip install -r requirements.txt
| Variable | Description | Default |
|---|---|---|
CLIENT_ACCESS_KEY | Access key clients use to sign requests | your_client_access_key_here |
CLIENT_SECRET_KEY | Secret key clients use to sign requests | your_client_secret_key_here |
| Variable | Description | Default |
|---|---|---|
ORIGIN_ACCESS_KEY | S3 backend access key | your_origin_access_key_here |
ORIGIN_SECRET_KEY | S3 backend secret key | your_origin_secret_key_here |
ORIGIN_DOMAIN | S3 backend domain | s3.example.com |
| Variable | Description | Default |
|---|---|---|
AWS_REGION | AWS region | us-east-1 |
PORT | Server port | 8000 |
export CLIENT_ACCESS_KEY="your_client_access_key"
export CLIENT_SECRET_KEY="your_client_secret_key"
export ORIGIN_ACCESS_KEY="backend_access_key"
export ORIGIN_SECRET_KEY="backend_secret_key"
export ORIGIN_DOMAIN="s3.example.com"
export AWS_REGION="us-east-1"
export PORT="8000"
python main.py
docker run -p 8000:8000 \
-e CLIENT_ACCESS_KEY="your_client_key" \
-e CLIENT_SECRET_KEY="your_client_secret" \
-e ORIGIN_ACCESS_KEY="backend_key" \
-e ORIGIN_SECRET_KEY="backend_secret" \
-e ORIGIN_DOMAIN="s3.example.com" \
s4l3h1/s3proxy:latest
For production deployments with multiple S3 backends and automatic failover:
.env file# Client-facing credentials (shared across all backends)
CLIENT_ACCESS_KEY=your_shared_client_access_key
CLIENT_SECRET_KEY=your_shared_client_secret_key
# Server A credentials
SERVERA_ACCESS_KEY=backend_a_access_key
SERVERA_SECRET_KEY=backend_a_secret_key
SERVERA_ENDPOINT=s3.backend-a.com
# Server B credentials
SERVERB_ACCESS_KEY=backend_b_access_key
SERVERB_SECRET_KEY=backend_b_secret_key
SERVERB_ENDPOINT=s3.backend-b.com
docker-compose.ymlversion: '3.8'
services:
s3-proxy-serverA:
image: s4l3h1/s3proxy:latest
ports:
- "8001:8000"
environment:
- CLIENT_ACCESS_KEY=${CLIENT_ACCESS_KEY}
- CLIENT_SECRET_KEY=${CLIENT_SECRET_KEY}
- ORIGIN_ACCESS_KEY=${SERVERA_ACCESS_KEY}
- ORIGIN_SECRET_KEY=${SERVERA_SECRET_KEY}
- ORIGIN_DOMAIN=${SERVERA_ENDPOINT}
- AWS_REGION=us-east-1
- PORT=8000
restart: unless-stopped
s3-proxy-serverB:
image: s4l3h1/s3proxy:latest
ports:
- "8002:8000"
environment:
- CLIENT_ACCESS_KEY=${CLIENT_ACCESS_KEY}
- CLIENT_SECRET_KEY=${CLIENT_SECRET_KEY}
- ORIGIN_ACCESS_KEY=${SERVERB_ACCESS_KEY}
- ORIGIN_SECRET_KEY=${SERVERB_SECRET_KEY}
- ORIGIN_DOMAIN=${SERVERB_ENDPOINT}
- AWS_REGION=us-east-1
- PORT=8000
restart: unless-stopped
haproxy:
image: haproxy:2.9-alpine
ports:
- "8000:8000"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
depends_on:
- s3-proxy-serverA
- s3-proxy-serverB
restart: unless-stopped
haproxy.cfgglobal
log stdout format raw local0
maxconn 4096
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend s3_frontend
bind *:8000
default_backend s3_backends
backend s3_backends
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server serverA s3-proxy-serverA:8000 check inter 5000 rise 2 fall 3
server serverB s3-proxy-serverB:8000 check inter 5000 rise 2 fall 3
docker-compose up -d
The proxy provides a health check endpoint:
# Check backend health
curl http://localhost:8000/healthz
# Response: {"status": "ok"} with HTTP 200 if origin is healthy
# Response: {"status": "nok"} with HTTP 450 if origin is down
HAProxy automatically removes unhealthy backends from rotation based on health checks (every 5 seconds).
X-Amz-Credential matches CLIENT_ACCESS_KEYCLIENT_SECRET_KEYORIGIN_SECRET_KEY/healthz on each backend (every 5 seconds)The proxy handles all paths and HTTP methods:
GET /{path:path} - Get objects, list buckets, etc.POST /{path:path} - Create multipart uploads, etc.PUT /{path:path} - Upload objects, create buckets, etc.DELETE /{path:path} - Delete objects, buckets, etc.HEAD /{path:path} - Get object metadataGET /healthz - Check origin server health
{"status": "ok"} with HTTP 200 if origin returns 2xx{"status": "nok"} with HTTP 450 if origin is unreachable or returns non-2xxClient Request (signed with CLIENT credentials):
GET https://s3.mydomain.com/bucket/object.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=CLIENT_KEY.../20241201/us-east-1/s3/aws4_request&X-Amz-Date=20241201T120000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=client_signature
Proxy Processing:
1. Validates CLIENT_KEY matches CLIENT_ACCESS_KEY
2. Verifies client_signature using CLIENT_SECRET_KEY
3. Re-calculates signature for origin using ORIGIN_ACCESS_KEY/ORIGIN_SECRET_KEY
4. Replaces X-Amz-Signature and X-Amz-Credential
Forwarded Request (signed with ORIGIN credentials):
GET https://s3.backend.com/bucket/object.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ORIGIN_KEY.../20241201/us-east-1/s3/aws4_request&X-Amz-Date=20241201T120000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=origin_signature
The proxy returns appropriate HTTP error responses for:
CLIENT_SECRET_KEY and ORIGIN_SECRET_KEY securely (use secrets management in production).env files for local development, never commit to version controlMIT License
Content type
Image
Digest
sha256:5661907bf…
Size
43 MB
Last updated
5 months ago
docker pull s4l3h1/s3proxy