mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-29 01:29:00 +00:00
docs: document RUSTFS_OUTBOUND_ALLOW_ORIGINS outbound policy (#5334)
This commit is contained in:
@@ -163,6 +163,7 @@ docker run -d --name rustfs -p 9000:9000 \
|
||||
-e RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY=on \
|
||||
-e RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY=http://<host-ip>:3020/webhook \
|
||||
-e RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY=/tmp/rustfs-events \
|
||||
-e RUSTFS_OUTBOUND_ALLOW_ORIGINS=http://<host-ip>:3020 \
|
||||
rustfs/rustfs:latest
|
||||
```
|
||||
|
||||
@@ -171,6 +172,7 @@ Notes:
|
||||
- For ARN `arn:rustfs:sqs::primary:webhook`, use instance-scoped env vars with `_PRIMARY`.
|
||||
- If queue dir is omitted, default is `/opt/rustfs/events`; ensure it is writable by the container runtime user.
|
||||
- `RUSTFS_NOTIFY_WEBHOOK_SKIP_TLS_VERIFY_PRIMARY` defaults to `false`; enabling it skips webhook TLS certificate verification, allows MITM attacks, and emits a startup warning. Prefer `RUSTFS_NOTIFY_WEBHOOK_CLIENT_CA_PRIMARY` for private CAs.
|
||||
- Since `1.0.0-beta.11`, webhook endpoints on private or container networks (Docker Compose service names, `host.docker.internal`, RFC 1918 addresses) are blocked unless their exact `scheme://host:port` origin is listed in `RUSTFS_OUTBOUND_ALLOW_ORIGINS` (comma-separated; the origin only, without the path). See [Outbound Connection Policy](docs/operations/outbound-connection-policy.md).
|
||||
|
||||
**NOTE**: We recommend reviewing the `docker-compose.yml` file before running. It defines several services including Grafana, Prometheus, and Jaeger, which are helpful for RustFS observability. If you wish to start Redis or Nginx containers, you can specify the corresponding profiles.
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# Outbound Connection Policy
|
||||
|
||||
This document describes the outbound connection policy that RustFS applies to
|
||||
server-initiated HTTP(S) requests, and the `RUSTFS_OUTBOUND_ALLOW_ORIGINS`
|
||||
allowlist operators use to reach endpoints on private or container networks.
|
||||
|
||||
It is written for operators who upgraded to `1.0.0-beta.11` (or later) and found
|
||||
that event-notification webhooks, audit webhooks, or other outbound integrations
|
||||
stopped reaching endpoints that worked before — typically Docker Compose service
|
||||
names, `host.docker.internal`, or RFC 1918 addresses.
|
||||
|
||||
## Background: what the policy protects
|
||||
|
||||
Several RustFS subsystems open connections to operator-configured URLs. To close
|
||||
a server-side request forgery (SSRF) class of problem, RustFS validates every
|
||||
such destination and re-checks the addresses returned by DNS on each new
|
||||
connection, so a hostname cannot be rebound to a restricted address after it is
|
||||
first accepted.
|
||||
|
||||
The policy governs the outbound clients used by:
|
||||
|
||||
- event-notification webhooks (`RUSTFS_NOTIFY_WEBHOOK_*`);
|
||||
- audit webhooks (`RUSTFS_AUDIT_WEBHOOK_*`);
|
||||
- OIDC identity-provider requests;
|
||||
- S3 tiering (warm-backend) endpoints;
|
||||
- Keystone auth URLs.
|
||||
|
||||
The webhook and audit outbound clients also **disable proxies and do not follow
|
||||
redirects**, so the destination must be reachable directly at the configured
|
||||
URL.
|
||||
|
||||
## What changed in beta.11
|
||||
|
||||
| | beta.10 | beta.11+ |
|
||||
|---|---|---|
|
||||
| Literal `localhost` / private / loopback IPs | Rejected | Rejected |
|
||||
| Hostnames that resolve to private/loopback addresses (`logstash`, `host.docker.internal`, Compose service DNS, …) | Allowed | **Blocked at DNS/connect time** unless allowlisted |
|
||||
| Escape hatch for private destinations | None | `RUSTFS_OUTBOUND_ALLOW_ORIGINS` |
|
||||
| Proxies / redirects for outbound clients | Followed | Disabled |
|
||||
|
||||
Before beta.11 a webhook endpoint whose hostname happened to resolve to a
|
||||
private address was accepted. Beta.11 fails that resolution closed unless the
|
||||
exact origin is on the allowlist. This is why a Compose setup that delivered
|
||||
events on beta.10 can go silent after the upgrade even though the configuration
|
||||
is unchanged.
|
||||
|
||||
## Symptoms
|
||||
|
||||
- Bucket event rules and webhook configuration look correct.
|
||||
- Uploads and audited API calls succeed.
|
||||
- No HTTP POST reaches the internal webhook receiver.
|
||||
- The target may appear offline or fail activation when its endpoint resolves to
|
||||
a loopback, private, shared, or reserved address.
|
||||
- Startup or target validation reports `webhook endpoint is not allowed: ...`
|
||||
with a reason such as `private address` or `loopback host`.
|
||||
|
||||
## `RUSTFS_OUTBOUND_ALLOW_ORIGINS`
|
||||
|
||||
`RUSTFS_OUTBOUND_ALLOW_ORIGINS` is a comma-separated list of exact HTTP(S)
|
||||
origins that are permitted to resolve to otherwise-restricted addresses. It is an
|
||||
operator-owned process setting read once at startup; individual target
|
||||
configuration cannot extend it.
|
||||
|
||||
```bash
|
||||
# exact scheme://host:port — comma-separate multiple origins
|
||||
RUSTFS_OUTBOUND_ALLOW_ORIGINS=http://logstash:8080,http://host.docker.internal:3020
|
||||
```
|
||||
|
||||
### Origin format rules
|
||||
|
||||
Each entry is matched as an **exact origin** (`scheme://host:port`):
|
||||
|
||||
- The scheme must be `http` or `https`.
|
||||
- The host and port must match the destination exactly. An allowlisted
|
||||
`http://logstash:8080` does **not** authorize `http://logstash:9090` or
|
||||
`https://logstash:8080`.
|
||||
- If the port is omitted, the scheme's default is used (`80` for `http`, `443`
|
||||
for `https`); the destination must then use that same default port.
|
||||
- Entries must be origins only. A trailing `/` is accepted, but a path, query,
|
||||
or fragment (for example `http://logstash:8080/events`) is **rejected** as an
|
||||
invalid origin — the process fails closed rather than silently ignoring the
|
||||
path.
|
||||
- Userinfo (`http://user:pass@host`) is not allowed.
|
||||
- An empty entry (for example a trailing or doubled comma) is rejected.
|
||||
|
||||
An invalid list fails closed: the affected subsystem reports an
|
||||
`invalid outbound policy` / `invalid origin at position N` error instead of
|
||||
starting with a partially applied allowlist.
|
||||
|
||||
### What stays blocked even when allowlisted
|
||||
|
||||
Allowlisting an origin only relaxes the loopback, private, shared, and reserved
|
||||
address classes for that exact origin. The following remain forbidden for every
|
||||
origin, allowlisted or not:
|
||||
|
||||
- cloud metadata endpoints (for example `169.254.169.254` and the other
|
||||
well-known IMDS addresses);
|
||||
- link-local addresses (`169.254.0.0/16`, `fe80::/10`);
|
||||
- the unspecified address (`0.0.0.0`, `::`);
|
||||
- IPv4-mapped, IPv4-compatible, and NAT64/6to4-embedded forms of any of the
|
||||
above (RustFS classifies the embedded IPv4 destination, so `::ffff:127.0.0.1`
|
||||
and similar cannot be used to bypass the policy).
|
||||
|
||||
The allowlist authorizes only the exact host you name. A DNS answer for a
|
||||
different hostname that points at a private address is still rejected, and each
|
||||
new connection re-validates the resolved addresses so a rebinding answer fails
|
||||
closed.
|
||||
|
||||
## Docker Compose example
|
||||
|
||||
```yaml
|
||||
services:
|
||||
rustfs:
|
||||
image: rustfs/rustfs:latest
|
||||
environment:
|
||||
RUSTFS_NOTIFY_ENABLE: "true"
|
||||
RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY: "on"
|
||||
RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY: "http://logstash:8080/events"
|
||||
RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY: "/tmp/rustfs-events"
|
||||
# Allow the webhook host to resolve to the Compose private network.
|
||||
# Note: the allowlist takes the origin only, without the /events path.
|
||||
RUSTFS_OUTBOUND_ALLOW_ORIGINS: "http://logstash:8080"
|
||||
logstash:
|
||||
image: docker.elastic.co/logstash/logstash:8.15.0
|
||||
# ...
|
||||
```
|
||||
|
||||
The endpoint keeps its full path (`/events`); the allowlist entry is the origin
|
||||
(`http://logstash:8080`) only.
|
||||
|
||||
## Upgrade checklist (beta.10 → beta.11+)
|
||||
|
||||
1. List every outbound endpoint whose hostname resolves to a loopback, private,
|
||||
shared, or reserved address: notification webhooks, audit webhooks, OIDC
|
||||
providers, tiering endpoints, and Keystone auth URLs.
|
||||
2. Add each one to `RUSTFS_OUTBOUND_ALLOW_ORIGINS` as an exact
|
||||
`scheme://host:port` origin (no path).
|
||||
3. Ensure the endpoint is reachable directly — for webhook and audit targets,
|
||||
proxies are disabled and redirects are not followed.
|
||||
4. Restart RustFS; the policy is read at startup.
|
||||
5. Confirm delivery, and check the logs for `... is not allowed` messages if a
|
||||
target still fails to activate.
|
||||
Reference in New Issue
Block a user