Skip to content

Commit 8d8515e

Browse files
authored
docs: clarify allow_private_registry_fetch is a blanket SSRF opt-out (#751)
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 1c31d33 commit 8d8515e

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
@@ -985,6 +985,15 @@ intentionally run a trusted registry mirror on an internal/private address:
985985
{ "allow_private_registry_fetch": true }
986986
```
987987

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

990999
**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
@@ -212,8 +212,16 @@ type Config struct {
212212
// resolves to — a non-routable address (loopback, RFC1918/CGNAT private,
213213
// link-local incl. the 169.254.169.254 cloud-metadata endpoint), so a
214214
// malicious or typo'd registry source cannot turn the daemon into a
215-
// request-forgery vector against internal services. Set true ONLY when you
216-
// intentionally run a trusted registry mirror on an internal/private address.
215+
// request-forgery vector against internal services.
216+
//
217+
// This opt-out is BLANKET (all-or-nothing): setting it true disables the
218+
// guard for EVERY non-routable range at once — loopback, RFC1918/CGNAT
219+
// private, link-local AND the 169.254.169.254 cloud-metadata endpoint. There
220+
// is no way to allow only loopback; enabling it for a localhost dev registry
221+
// also re-opens the cloud-metadata SSRF vector. Set true ONLY when you
222+
// intentionally run a trusted registry mirror on an internal/private address,
223+
// ideally on a host with no cloud-metadata exposure. The change takes effect
224+
// only on daemon (re)start or config reload.
217225
AllowPrivateRegistryFetch bool `json:"allow_private_registry_fetch,omitempty" mapstructure:"allow-private-registry-fetch"`
218226

219227
// 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)