Browse Source
Merge branch 'fix-ipv6-brackets-default-port' of https://github.com/seaweedfs/seaweedfs into fix-ipv6-brackets-default-port
pull/7414/head
Merge branch 'fix-ipv6-brackets-default-port' of https://github.com/seaweedfs/seaweedfs into fix-ipv6-brackets-default-port
pull/7414/head
3 changed files with 445 additions and 1 deletions
@ -0,0 +1,353 @@ |
|||||
|
# Nginx Reverse Proxy Configuration for SeaweedFS S3 API |
||||
|
|
||||
|
This guide explains how to properly configure nginx as a reverse proxy for SeaweedFS S3 API while maintaining AWS Signature V4 authentication compatibility. |
||||
|
|
||||
|
## The Challenge |
||||
|
|
||||
|
AWS Signature V4 authentication calculates a cryptographic signature based on the exact request including headers, URI, and body. When using nginx as a reverse proxy, any modification to the signed request components will cause signature verification to fail. |
||||
|
|
||||
|
## Critical Requirements |
||||
|
|
||||
|
1. **Preserve the Authorization header**: Must pass through untouched |
||||
|
2. **Preserve all X-Amz-* headers**: These are part of the signature calculation |
||||
|
3. **Preserve the Host header**: Use `$http_host` instead of `$host` to maintain the original port |
||||
|
4. **Do not modify the request URI**: Avoid path rewriting |
||||
|
5. **Disable buffering for chunked uploads**: Required for streaming uploads |
||||
|
6. **Preserve the request body**: Must not be modified |
||||
|
|
||||
|
## Recommended Nginx Configuration |
||||
|
|
||||
|
### Basic Configuration for S3 API |
||||
|
|
||||
|
```nginx |
||||
|
upstream seaweedfs_s3 { |
||||
|
server s3:8333; |
||||
|
keepalive 32; |
||||
|
} |
||||
|
|
||||
|
server { |
||||
|
listen 443 ssl http2; |
||||
|
server_name your-domain.com; |
||||
|
|
||||
|
# SSL Configuration |
||||
|
ssl_certificate /etc/nginx/certs/server.crt; |
||||
|
ssl_certificate_key /etc/nginx/certs/server.key; |
||||
|
|
||||
|
# Logging |
||||
|
access_log /var/log/nginx/s3-access.log; |
||||
|
error_log /var/log/nginx/s3-error.log; |
||||
|
|
||||
|
# Client upload limits |
||||
|
client_max_body_size 0; # No limit for S3 uploads |
||||
|
client_body_timeout 300s; |
||||
|
|
||||
|
# Disable buffering for AWS chunked uploads |
||||
|
proxy_buffering off; |
||||
|
proxy_request_buffering off; |
||||
|
|
||||
|
# HTTP version and connection settings |
||||
|
proxy_http_version 1.1; |
||||
|
proxy_set_header Connection ""; |
||||
|
|
||||
|
# Timeouts |
||||
|
proxy_connect_timeout 300s; |
||||
|
proxy_send_timeout 300s; |
||||
|
proxy_read_timeout 300s; |
||||
|
|
||||
|
location / { |
||||
|
proxy_pass http://seaweedfs_s3; |
||||
|
|
||||
|
# CRITICAL: Preserve original Host header including port |
||||
|
# Use $http_host instead of $host to preserve the port |
||||
|
proxy_set_header Host $http_host; |
||||
|
|
||||
|
# CRITICAL: Pass all headers through unchanged |
||||
|
# AWS Signature V4 includes these in signature calculation |
||||
|
proxy_pass_request_headers on; |
||||
|
|
||||
|
# Optional: Forward client IP information |
||||
|
# (These are NOT part of AWS signature) |
||||
|
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; |
||||
|
|
||||
|
# CRITICAL: Do not modify request body |
||||
|
proxy_pass_request_body on; |
||||
|
|
||||
|
# Ignore invalid headers (S3 may send non-standard headers) |
||||
|
ignore_invalid_headers off; |
||||
|
} |
||||
|
|
||||
|
# Health check endpoint |
||||
|
location /health { |
||||
|
add_header Content-Type text/plain; |
||||
|
return 200 "OK\n"; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Configuration with mTLS Client Certificate Authentication |
||||
|
|
||||
|
If you need to add mTLS authentication **in addition to** S3 authentication: |
||||
|
|
||||
|
```nginx |
||||
|
upstream seaweedfs_s3 { |
||||
|
server s3:8333; |
||||
|
keepalive 32; |
||||
|
} |
||||
|
|
||||
|
server { |
||||
|
listen 443 ssl http2; |
||||
|
server_name your-domain.com; |
||||
|
|
||||
|
# SSL Configuration |
||||
|
ssl_certificate /etc/nginx/certs/server.crt; |
||||
|
ssl_certificate_key /etc/nginx/certs/server.key; |
||||
|
|
||||
|
# mTLS Configuration |
||||
|
ssl_client_certificate /etc/nginx/certs/ca.crt; |
||||
|
ssl_verify_client optional; # or 'on' to require client certificates |
||||
|
ssl_verify_depth 2; |
||||
|
|
||||
|
# Logging with mTLS info |
||||
|
log_format mtls '$remote_addr - $remote_user [$time_local] ' |
||||
|
'"$request" $status $body_bytes_sent ' |
||||
|
'verify=$ssl_client_verify dn="$ssl_client_s_dn"'; |
||||
|
|
||||
|
access_log /var/log/nginx/s3-mtls-access.log mtls; |
||||
|
error_log /var/log/nginx/s3-error.log; |
||||
|
|
||||
|
# Client upload limits |
||||
|
client_max_body_size 0; |
||||
|
client_body_timeout 300s; |
||||
|
|
||||
|
# Disable buffering for AWS chunked uploads |
||||
|
proxy_buffering off; |
||||
|
proxy_request_buffering off; |
||||
|
|
||||
|
# HTTP version and connection settings |
||||
|
proxy_http_version 1.1; |
||||
|
proxy_set_header Connection ""; |
||||
|
|
||||
|
# Timeouts |
||||
|
proxy_connect_timeout 300s; |
||||
|
proxy_send_timeout 300s; |
||||
|
proxy_read_timeout 300s; |
||||
|
|
||||
|
location / { |
||||
|
proxy_pass http://seaweedfs_s3; |
||||
|
|
||||
|
# CRITICAL: Preserve original Host header |
||||
|
proxy_set_header Host $http_host; |
||||
|
|
||||
|
# CRITICAL: Pass all headers through unchanged |
||||
|
proxy_pass_request_headers on; |
||||
|
|
||||
|
# Forward client IP information |
||||
|
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; |
||||
|
|
||||
|
# Optional: Forward mTLS certificate info to backend |
||||
|
proxy_set_header X-SSL-Client-Verify $ssl_client_verify; |
||||
|
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn; |
||||
|
|
||||
|
# CRITICAL: Do not modify request body |
||||
|
proxy_pass_request_body on; |
||||
|
|
||||
|
# Ignore invalid headers |
||||
|
ignore_invalid_headers off; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Common Mistakes to Avoid |
||||
|
|
||||
|
### ❌ DO NOT explicitly set AWS headers |
||||
|
|
||||
|
```nginx |
||||
|
# WRONG - Do not do this! |
||||
|
proxy_set_header Authorization $http_authorization; |
||||
|
proxy_set_header X-Amz-Date $http_x_amz_date; |
||||
|
proxy_set_header X-Amz-Content-Sha256 $http_x_amz_content_sha256; |
||||
|
``` |
||||
|
|
||||
|
**Why?** When you explicitly set headers, nginx may normalize or modify them. Use `proxy_pass_request_headers on` instead to pass ALL headers through unchanged. |
||||
|
|
||||
|
### ❌ DO NOT use `proxy_set_header Host $host` |
||||
|
|
||||
|
```nginx |
||||
|
# WRONG - Loses port information |
||||
|
proxy_set_header Host $host; |
||||
|
``` |
||||
|
|
||||
|
**Why?** `$host` contains only the hostname without the port. AWS Signature V4 includes the Host header with port in signature calculation. Use `$http_host` instead. |
||||
|
|
||||
|
### ❌ DO NOT enable request buffering for large uploads |
||||
|
|
||||
|
```nginx |
||||
|
# WRONG for S3 uploads |
||||
|
proxy_request_buffering on; |
||||
|
``` |
||||
|
|
||||
|
**Why?** This breaks AWS chunked transfer encoding used for large file uploads. Keep it off for S3 API endpoints. |
||||
|
|
||||
|
### ❌ DO NOT rewrite paths |
||||
|
|
||||
|
```nginx |
||||
|
# WRONG - Path rewriting breaks signature |
||||
|
location /s3/ { |
||||
|
rewrite ^/s3/(.*) /$1 break; |
||||
|
proxy_pass http://seaweedfs_s3; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Why?** The URI is part of the AWS Signature V4 calculation. Any path modification will cause signature verification to fail. |
||||
|
|
||||
|
## Testing Your Configuration |
||||
|
|
||||
|
### Test with AWS CLI |
||||
|
|
||||
|
```bash |
||||
|
# Configure AWS CLI |
||||
|
aws configure set aws_access_key_id your_access_key |
||||
|
aws configure set aws_secret_access_key your_secret_key |
||||
|
aws configure set region us-east-1 |
||||
|
|
||||
|
# Test through nginx proxy |
||||
|
aws s3 ls s3://your-bucket/ --endpoint-url https://your-nginx-domain |
||||
|
``` |
||||
|
|
||||
|
### Test with boto3 (Python) |
||||
|
|
||||
|
```python |
||||
|
import boto3 |
||||
|
|
||||
|
s3_client = boto3.client( |
||||
|
's3', |
||||
|
endpoint_url='https://your-nginx-domain', |
||||
|
aws_access_key_id='your_access_key', |
||||
|
aws_secret_access_key='your_secret_key', |
||||
|
region_name='us-east-1' |
||||
|
) |
||||
|
|
||||
|
# List buckets |
||||
|
response = s3_client.list_buckets() |
||||
|
print(response) |
||||
|
|
||||
|
# Upload a file |
||||
|
s3_client.upload_file('local_file.txt', 'bucket-name', 'remote_file.txt') |
||||
|
``` |
||||
|
|
||||
|
## Debugging |
||||
|
|
||||
|
### Enable detailed logging |
||||
|
|
||||
|
Add to your nginx configuration: |
||||
|
|
||||
|
```nginx |
||||
|
error_log /var/log/nginx/error_debug.log debug; |
||||
|
|
||||
|
log_format detailed '$remote_addr - $remote_user [$time_local] ' |
||||
|
'"$request" $status $body_bytes_sent ' |
||||
|
'"$http_authorization" "$http_x_amz_date" ' |
||||
|
'upstream="$upstream_addr" ' |
||||
|
'upstream_status=$upstream_status'; |
||||
|
|
||||
|
access_log /var/log/nginx/detailed.log detailed; |
||||
|
``` |
||||
|
|
||||
|
### Check SeaweedFS logs |
||||
|
|
||||
|
Look for authentication errors in SeaweedFS S3 logs: |
||||
|
- `could not find accessKey` - Access key not configured in SeaweedFS |
||||
|
- `signature mismatch` - Request was modified by proxy |
||||
|
- `InvalidAccessKeyId` - Access key doesn't exist |
||||
|
- `SignatureDoesNotMatch` - Signature calculation failed |
||||
|
|
||||
|
### Common Issues |
||||
|
|
||||
|
1. **"Could not find accessKey" error** |
||||
|
- Verify the access key exists in SeaweedFS: `weed shell` → `s3.configure.list` |
||||
|
- Check that Authorization header is being forwarded |
||||
|
- Verify SeaweedFS can read its configuration |
||||
|
|
||||
|
2. **"SignatureDoesNotMatch" error** |
||||
|
- Check if nginx is modifying headers or URI |
||||
|
- Verify `proxy_set_header Host $http_host` is used |
||||
|
- Disable any header manipulation |
||||
|
- Check for path rewriting rules |
||||
|
|
||||
|
3. **"RequestTimeTooSkewed" error** |
||||
|
- Ensure server clocks are synchronized (use NTP) |
||||
|
- Check timezone settings on both nginx and SeaweedFS servers |
||||
|
|
||||
|
## Performance Tuning |
||||
|
|
||||
|
For production deployments with high throughput: |
||||
|
|
||||
|
```nginx |
||||
|
upstream seaweedfs_s3 { |
||||
|
server s3:8333; |
||||
|
keepalive 64; # Increase keepalive connections |
||||
|
keepalive_timeout 90s; |
||||
|
keepalive_requests 10000; |
||||
|
} |
||||
|
|
||||
|
server { |
||||
|
# ... other config ... |
||||
|
|
||||
|
# Increase worker connections if needed |
||||
|
# (in nginx.conf, not server block) |
||||
|
# worker_connections 10000; |
||||
|
|
||||
|
# Enable caching for GET requests (optional) |
||||
|
proxy_cache_path /var/cache/nginx/s3 levels=1:2 keys_zone=s3_cache:10m |
||||
|
max_size=10g inactive=60m use_temp_path=off; |
||||
|
|
||||
|
location / { |
||||
|
# Only cache GET requests |
||||
|
proxy_cache s3_cache; |
||||
|
proxy_cache_methods GET HEAD; |
||||
|
proxy_cache_valid 200 60m; |
||||
|
proxy_cache_key "$scheme$request_method$host$request_uri"; |
||||
|
|
||||
|
# Don't cache authenticated requests by default |
||||
|
proxy_cache_bypass $http_authorization; |
||||
|
proxy_no_cache $http_authorization; |
||||
|
|
||||
|
# ... rest of proxy config ... |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Docker Compose Example |
||||
|
|
||||
|
```yaml |
||||
|
version: '3' |
||||
|
|
||||
|
services: |
||||
|
seaweedfs-s3: |
||||
|
image: chrislusf/seaweedfs:latest |
||||
|
ports: |
||||
|
- "8333:8333" |
||||
|
command: 's3 -ip=0.0.0.0 -port=8333' |
||||
|
volumes: |
||||
|
- ./s3_config:/etc/seaweedfs |
||||
|
|
||||
|
nginx: |
||||
|
image: nginx:latest |
||||
|
ports: |
||||
|
- "443:443" |
||||
|
volumes: |
||||
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro |
||||
|
- ./certs:/etc/nginx/certs:ro |
||||
|
depends_on: |
||||
|
- seaweedfs-s3 |
||||
|
``` |
||||
|
|
||||
|
## Additional Resources |
||||
|
|
||||
|
- [AWS Signature Version 4 Documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) |
||||
|
- [SeaweedFS S3 Configuration](https://github.com/seaweedfs/seaweedfs/wiki/Amazon-S3-API) |
||||
|
- [Nginx Proxy Module Documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) |
||||
@ -0,0 +1,83 @@ |
|||||
|
# Example nginx configuration for SeaweedFS S3 API reverse proxy |
||||
|
# See README.md in this directory for detailed explanation |
||||
|
|
||||
|
upstream seaweedfs_s3 { |
||||
|
# Point to your SeaweedFS S3 service |
||||
|
server s3:8333; |
||||
|
# For local development: server 127.0.0.1:8333; |
||||
|
|
||||
|
# Keep connections alive for better performance |
||||
|
keepalive 32; |
||||
|
} |
||||
|
|
||||
|
server { |
||||
|
listen 443 ssl http2; |
||||
|
server_name _; # Replace with your domain |
||||
|
|
||||
|
# SSL Configuration |
||||
|
ssl_certificate /etc/nginx/certs/server.crt; |
||||
|
ssl_certificate_key /etc/nginx/certs/server.key; |
||||
|
|
||||
|
# Optional: Client certificate authentication (mTLS) |
||||
|
# ssl_client_certificate /etc/nginx/certs/ca.crt; |
||||
|
# ssl_verify_client optional; |
||||
|
# ssl_verify_depth 2; |
||||
|
|
||||
|
# Logging |
||||
|
access_log /var/log/nginx/s3-access.log; |
||||
|
error_log /var/log/nginx/s3-error.log; |
||||
|
|
||||
|
# Client upload limits |
||||
|
client_max_body_size 0; # No limit for S3 uploads |
||||
|
client_body_timeout 300s; |
||||
|
|
||||
|
# CRITICAL: Disable buffering for AWS chunked uploads |
||||
|
proxy_buffering off; |
||||
|
proxy_request_buffering off; |
||||
|
|
||||
|
# HTTP version and connection settings |
||||
|
proxy_http_version 1.1; |
||||
|
proxy_set_header Connection ""; |
||||
|
|
||||
|
# Timeouts |
||||
|
proxy_connect_timeout 300s; |
||||
|
proxy_send_timeout 300s; |
||||
|
proxy_read_timeout 300s; |
||||
|
|
||||
|
location / { |
||||
|
proxy_pass http://seaweedfs_s3; |
||||
|
|
||||
|
# CRITICAL: Preserve original Host header including port |
||||
|
# Use $http_host instead of $host to preserve the port |
||||
|
proxy_set_header Host $http_host; |
||||
|
|
||||
|
# CRITICAL: Pass all headers through unchanged |
||||
|
# AWS Signature V4 includes these in signature calculation |
||||
|
proxy_pass_request_headers on; |
||||
|
|
||||
|
# Optional: Forward client IP information |
||||
|
# (These are NOT part of AWS signature) |
||||
|
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; |
||||
|
|
||||
|
# CRITICAL: Do not modify request body |
||||
|
proxy_pass_request_body on; |
||||
|
|
||||
|
# Ignore invalid headers (S3 may send non-standard headers) |
||||
|
ignore_invalid_headers off; |
||||
|
} |
||||
|
|
||||
|
# Health check endpoint |
||||
|
location /health { |
||||
|
return 200 "OK\n"; |
||||
|
add_header Content-Type text/plain; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# Optional: HTTP to HTTPS redirect |
||||
|
server { |
||||
|
listen 80; |
||||
|
server_name _; |
||||
|
return 301 https://$host$request_uri; |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue