The one reading of an S3-compatible endpoint, and the one guard on where the server may connect to.
Three places name an endpoint: a bucket (Providers.S3), an
object_storage Integrations connection (Integrations.Validators), and the
bucket form. They must all read the same string the same way, or the server
would connect to a different host than the one that was validated.
Parsing
parse/1 accepts a bare host (s3.us-west-002.backblazeb2.com), host:port,
or an http/https URL with no path (http://minio.local:9000); the scheme
defaults to https. Refused rather than read as plain AWS: any other scheme, a
path (a gateway prefix would be dropped silently), a query, and an IPv6 zone
id (%eth0).
Guarding
The server connects to whatever endpoint it is given, so a user-supplied one
is a request-forgery primitive. check/3 classifies the addresses behind an
endpoint by policy:
:system— set by an admin. Loopback and private ranges are allowed (a MinIO on the same network is a normal setup); what no storage endpoint can be is unspecified, link-local (where cloud metadata lives), multicast or reserved space, so those are refused.:personal— set by an ordinary user. https only, and additionally no loopback, private, carrier-grade-NAT or unique-local address.
A hostname is resolved and every address it yields is checked, so a name with one public and one private record is refused. The check sees DNS as of the moment it runs; it is not a connect-time pin, so a name that changes its answer between the check and the request is not caught. Callers run it at save and test time, and again each time a personal endpoint is used.
Summary
Functions
A URL's audit representation, with credentials, query and fragment withheld.
Absolute local paths stay paths unless local_path: false (for CDN URLs).
Whether the server may connect to endpoint (a string or the result of
parse/1) under policy.
The class of an address: :public, or what makes it not one
(:unspecified, :loopback, :private, :shared, :link_local,
:unique_local, :multicast, :reserved). An IPv4 address wrapped in IPv6
(::ffff:a.b.c.d, NAT64 64:ff9b::/96) is classified as the IPv4 address.
An endpoint string, parsed: %{scheme:, host:, port:}; nil when none is
set (plain AWS); {:error, :invalid_endpoint} when one is set but cannot be
used.
Types
@type parsed() :: %{scheme: String.t(), host: String.t(), port: pos_integer()}
@type policy() :: :system | :personal
@type reason() ::
:invalid_endpoint | :insecure_scheme | :blocked_address | :blocked_host
Functions
A URL's audit representation, with credentials, query and fragment withheld.
Absolute local paths stay paths unless local_path: false (for CDN URLs).
Whether the server may connect to endpoint (a string or the result of
parse/1) under policy.
nil (no endpoint, plain AWS) is always allowed. Options:
:resolve— look a hostname up and check every address it yields (defaultfalse; a literal IP is always checked). Off on hot paths, on wherever an endpoint is saved, tested, or used for a personal bucket.:resolver—(charlist, :inet | :inet6 -> {:ok, [ip]} | {:error, _}), injectable for tests. Defaults to:inet.getaddrs/2.
@spec classify(:inet.ip_address()) :: atom()
The class of an address: :public, or what makes it not one
(:unspecified, :loopback, :private, :shared, :link_local,
:unique_local, :multicast, :reserved). An IPv4 address wrapped in IPv6
(::ffff:a.b.c.d, NAT64 64:ff9b::/96) is classified as the IPv4 address.
An endpoint string, parsed: %{scheme:, host:, port:}; nil when none is
set (plain AWS); {:error, :invalid_endpoint} when one is set but cannot be
used.