diff --git a/S3-Nginx-Proxy.md b/S3-Nginx-Proxy.md index 00ba19c..2ed1861 100644 --- a/S3-Nginx-Proxy.md +++ b/S3-Nginx-Proxy.md @@ -5,6 +5,19 @@ For virtual-hosted style URL buckets, you'll need to add a [wildcard DNS record] Make sure the config sets the `X-Forwarded-Host` and optionally the `X-Forwarded-Port` if you are using a non-standard port. SeaweedFS will automatically combine these headers to reconstruct the correct host information for signature verification. +## aws:SourceIp and Trusted Proxies + +The `X-Forwarded-For` and `X-Real-IP` headers set by the proxy are also used for `aws:SourceIp` condition evaluation in S3 bucket policies and IAM policies — but **only** when the proxy's IP is in the configured trusted-proxy allowlist. Without an allowlist, SeaweedFS uses the direct TCP peer address for `aws:SourceIp` (matching AWS S3 semantics) to prevent spoofing. + +To enable forwarded-header trust, add the proxy's IP or CIDR to `security.toml`: + +```toml +[s3.trusted_proxies] +white_list = "10.10.10.0/24" # comma separated; bare IPs or CIDRs +``` + +See [S3 Policy Conditions — Source IP Handling](S3-Policy-Conditions#source-ip-handling) for the full resolution logic. + ## Explicit External URL for Signature Verification In scenarios where the reverse proxy cannot easily be configured to send the necessary headers, or in complex multi-hop environments, you can use the `-s3.externalUrl` flag to explicitly set the public-facing URL of the S3 service. diff --git a/S3-Policy-Conditions.md b/S3-Policy-Conditions.md index ecf73ca..6a11383 100644 --- a/S3-Policy-Conditions.md +++ b/S3-Policy-Conditions.md @@ -134,15 +134,25 @@ Prefix any string operator with `ForAnyValue:` or `ForAllValues:` for multi-valu ## Source IP Handling -When SeaweedFS runs behind a reverse proxy, the `aws:SourceIp` condition key is resolved using the following logic: +By default, the `aws:SourceIp` condition key is resolved from the **direct TCP peer address** (`RemoteAddr`), matching AWS S3 semantics. Forwarding headers (`X-Forwarded-For`, `X-Real-IP`) are intentionally ignored — without an explicit allowlist they are client-controlled and spoofable, which would let a caller behind a private-looking peer bypass any `IpAddress`/`NotIpAddress` restriction. -1. If the direct connection is from a **private/loopback IP** (RFC 1918, IPv6 ULA), forwarding headers are trusted: - - `X-Forwarded-For` is checked for the rightmost non-private IP - - `X-Real-IP` is used as a fallback -2. If the direct connection is from a **public IP**, forwarding headers are ignored to prevent spoofing -3. Final fallback is always `RemoteAddr` +### Trusted Proxy Allowlist -This ensures correct behavior whether SeaweedFS is accessed directly or through a load balancer. +When SeaweedFS runs behind a reverse proxy, configure a trusted-proxy allowlist in `security.toml` so that forwarded headers are honored only from known proxies: + +```toml +[s3.trusted_proxies] +white_list = "10.10.10.0/24" # comma separated; bare IPs or CIDRs +``` + +Can also be set via the environment variable `WEED_S3_TRUSTED_PROXIES_WHITE_LIST`. Reloaded on `SIGHUP`. + +When the direct TCP peer is in the allowlist: +1. `X-Forwarded-For` is walked right-to-left, skipping trusted hops, to find the first non-trusted (public) IP +2. If all hops are trusted, the leftmost valid IP is used +3. `X-Real-IP` is used as a fallback when `X-Forwarded-For` is absent or malformed + +When the direct TCP peer is **not** in the allowlist (or no allowlist is configured), forwarded headers are ignored and the direct peer address is used. ## Examples diff --git a/Security-Configuration.md b/Security-Configuration.md index 1f4f417..df0a710 100644 --- a/Security-Configuration.md +++ b/Security-Configuration.md @@ -164,6 +164,15 @@ key = "" ca = "" # disable_tls_verify_client_cert = true|false (default: false) +# Trusted reverse proxies allowed to set X-Forwarded-For / X-Real-Ip for +# aws:SourceIp condition evaluation in S3 bucket and IAM policies. When the +# direct TCP peer is in this list, forwarded headers are honored; otherwise +# the direct peer address is used (matching AWS S3 semantics). Leave empty to +# always use the direct peer, which is the safe default. +# Can also be set via env var: WEED_S3_TRUSTED_PROXIES_WHITE_LIST +[s3.trusted_proxies] +white_list = "" # comma separated; bare IPs or CIDRs, e.g. "10.10.10.0/24" + # white list. It's checking request ip address. [guard] white_list = ""