Skip to content

Commit f4d706d

Browse files
committed
docs: clarify allow_private_registry_fetch is a blanket SSRF opt-out
The allow_private_registry_fetch flag is all-or-nothing: enabling it lifts the registry SSRF guard for every non-routable range at once, including the 169.254.169.254 cloud-metadata endpoint. Enabling it for a localhost dev registry therefore also re-opens the cloud-metadata SSRF vector. Document this sharp edge in the config doc comment, docs/configuration.md, and docs/registries.md, plus note the flag only takes effect on daemon (re)start / config reload. Related #745 Related MCP-3206
1 parent 11621df commit f4d706d

5 files changed

Lines changed: 39 additions & 5 deletions

File tree

docs/configuration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,15 @@ intentionally run a trusted registry mirror on an internal/private address:
942942
{ "allow_private_registry_fetch": true }
943943
```
944944

945+
> ⚠️ **The opt-out is blanket (all-or-nothing).** Setting it `true` lifts the
946+
> guard for **every** non-routable range at once — loopback, RFC1918/CGNAT
947+
> private, link-local **and** the `169.254.169.254` cloud-metadata endpoint.
948+
> There is no way to allow only loopback: enabling it for a localhost dev
949+
> registry also re-opens the cloud-metadata SSRF vector (e.g.
950+
> `registry add-source https://169.254.169.254/...` will then succeed). Enable
951+
> it only for trusted local/dev use, ideally on hosts with no cloud-metadata
952+
> exposure. The flag takes effect only on daemon (re)start / config reload.
953+
945954
Default `false` (secure). See [Registries Documentation](registries.md#adding-your-own-registry-source).
946955

947956
**Default Registries** (shipped built-in, no configuration required):

docs/registries.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ into a request-forgery vector against internal services:
9494
rebinding. The official protocol's cursor-follow pagination is also pinned to
9595
the configured host so a hostile `nextCursor` cannot redirect the request.
9696

97+
The top-level `allow_private_registry_fetch` flag (default `false`) is a **blanket
98+
opt-out**: setting it `true` disables this guard for **every** non-routable range
99+
at once — loopback, RFC1918/CGNAT private, link-local **and** the
100+
`169.254.169.254` cloud-metadata endpoint. It cannot be scoped to loopback only,
101+
so enabling it for a localhost dev registry also re-opens the cloud-metadata SSRF
102+
vector; enable it only for trusted local/dev use, ideally on hosts with no
103+
cloud-metadata exposure. The change takes effect on daemon (re)start / config
104+
reload. See [Configuration](configuration.md#registries).
105+
97106
Equivalent surfaces:
98107

99108
- **REST:** `POST /api/v1/registries` with `{ "url": "https://…", "protocol": "…", "id": "…", "name": "…" }`.

internal/config/config.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,16 @@ type Config struct {
182182
// resolves to — a non-routable address (loopback, RFC1918/CGNAT private,
183183
// link-local incl. the 169.254.169.254 cloud-metadata endpoint), so a
184184
// malicious or typo'd registry source cannot turn the daemon into a
185-
// request-forgery vector against internal services. Set true ONLY when you
186-
// intentionally run a trusted registry mirror on an internal/private address.
185+
// request-forgery vector against internal services.
186+
//
187+
// This opt-out is BLANKET (all-or-nothing): setting it true disables the
188+
// guard for EVERY non-routable range at once — loopback, RFC1918/CGNAT
189+
// private, link-local AND the 169.254.169.254 cloud-metadata endpoint. There
190+
// is no way to allow only loopback; enabling it for a localhost dev registry
191+
// also re-opens the cloud-metadata SSRF vector. Set true ONLY when you
192+
// intentionally run a trusted registry mirror on an internal/private address,
193+
// ideally on a host with no cloud-metadata exposure. The change takes effect
194+
// only on daemon (re)start or config reload.
187195
AllowPrivateRegistryFetch bool `json:"allow_private_registry_fetch,omitempty" mapstructure:"allow-private-registry-fetch"`
188196

189197
// Deprecated: Features flags are unused and have no runtime effect. Kept for backward compatibility.

oas/docs.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

oas/swagger.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,16 @@ components:
2525
resolves to — a non-routable address (loopback, RFC1918/CGNAT private,
2626
link-local incl. the 169.254.169.254 cloud-metadata endpoint), so a
2727
malicious or typo'd registry source cannot turn the daemon into a
28-
request-forgery vector against internal services. Set true ONLY when you
29-
intentionally run a trusted registry mirror on an internal/private address.
28+
request-forgery vector against internal services.
29+
30+
This opt-out is BLANKET (all-or-nothing): setting it true disables the
31+
guard for EVERY non-routable range at once — loopback, RFC1918/CGNAT
32+
private, link-local AND the 169.254.169.254 cloud-metadata endpoint. There
33+
is no way to allow only loopback; enabling it for a localhost dev registry
34+
also re-opens the cloud-metadata SSRF vector. Set true ONLY when you
35+
intentionally run a trusted registry mirror on an internal/private address,
36+
ideally on a host with no cloud-metadata exposure. The change takes effect
37+
only on daemon (re)start or config reload.
3038
type: boolean
3139
allow_server_add:
3240
type: boolean

0 commit comments

Comments
 (0)