| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Security.Egress
Description
The egress posture for registry traffic: https-only by construction, with TLS certificate validation as the endpoint-authentication boundary.
Every outbound registry URL the proxy dials is an RegistryUrl, built through the
https-only mkRegistryUrl. A non-https registry endpoint cannot be represented, so a
plain-HTTP target is refused at the configuration boundary (a non-https configured
upstream fails closed at boot) and a packument's dist.tarball is normalised through
resolveTarballUrl before it is ever dialled. The data-plane Manager
is the standard validating tls manager, so the certificate presented by the dialled
host is checked against the system trust store for the requested name. An attacker who
can steer a name to an internal or rebound address cannot make that address present a
CA-trusted certificate for the host, so the credential-exfiltration and
resolve-to-internal SSRF class is closed by certificate validation rather than by a
resolved-IP pin.
Two complementary controls live alongside this and are not part of this module: the
outbound host allowlist (isAllowedUpstreamHost), which is the
load-bearing egress-policy control (the proxy dials only configured upstream hosts), and
the pure literal internal-range block (isBlockedTarget), kept as
cheap defence-in-depth on the dist.tarball host gate. No data-plane request follows an
upstream redirect (withToken pins redirectCount = 0), so there
is no hop that could downgrade the scheme or escape the allowlist after the URL is built.
A test- and dev-only loopback constructor lives in Ecluse.Core.Security.Egress.DevHttp,
compiled only under the dev-http-egress Cabal flag, so the loopback test suites can dial
an in-process http://127.0.0.1 server without weakening the production posture; a
release build does not compile it.
Synopsis
- data RegistryUrl
- mkRegistryUrl :: Text -> Either Text RegistryUrl
- registryUrlText :: RegistryUrl -> Text
- resolveTarballUrl :: Text -> Text -> Either Text RegistryUrl
The https-only egress URL
data RegistryUrl Source #
An outbound registry-egress URL that is https by construction. The only
production constructor, mkRegistryUrl, rejects any non-https scheme, so a
plain-HTTP registry target cannot be represented in a running system: every
configured upstream, mirror, and publication endpoint, and every dist.tarball
target, is one of these. Stored normalised (surrounding whitespace trimmed).
Instances
| Show RegistryUrl Source # | |
Defined in Ecluse.Core.Security.Egress.Internal Methods showsPrec :: Int -> RegistryUrl -> ShowS # show :: RegistryUrl -> String # showList :: [RegistryUrl] -> ShowS # | |
| Eq RegistryUrl Source # | |
Defined in Ecluse.Core.Security.Egress.Internal | |
| Ord RegistryUrl Source # | |
Defined in Ecluse.Core.Security.Egress.Internal Methods compare :: RegistryUrl -> RegistryUrl -> Ordering # (<) :: RegistryUrl -> RegistryUrl -> Bool # (<=) :: RegistryUrl -> RegistryUrl -> Bool # (>) :: RegistryUrl -> RegistryUrl -> Bool # (>=) :: RegistryUrl -> RegistryUrl -> Bool # max :: RegistryUrl -> RegistryUrl -> RegistryUrl # min :: RegistryUrl -> RegistryUrl -> RegistryUrl # | |
mkRegistryUrl :: Text -> Either Text RegistryUrl Source #
Build a RegistryUrl, accepting only an https:// URL (the scheme matched
case-insensitively, since URI schemes are). A non-https or empty value is rejected
with a reason that names the requirement, so the aggregating configuration layer can
fail closed at boot and report the offending value.
>>>mkRegistryUrl "https://registry.npmjs.org"Right (RegistryUrl "https://registry.npmjs.org")
>>>mkRegistryUrl "http://registry.npmjs.org"Left "registry URL must use https (got http://registry.npmjs.org)"
registryUrlText :: RegistryUrl -> Text Source #
The underlying URL text.
Packument dist.tarball normalisation
resolveTarballUrl :: Text -> Text -> Either Text RegistryUrl Source #
Resolve a packument's dist.tarball URL against the https-only egress policy,
given the bare host the packument itself was served from.
An upstream's dist.tarball is server-chosen data, so its scheme is normalised before
the proxy will dial it:
- an
https://target is kept (validated throughmkRegistryUrl); - an
http://target on the same host as the packument is upgraded tohttps://, the legacy case of an older registry that still advertises plaintext artifact URLs on its own host; - an
http://target on any other host is refused (aLeftcarrying a reason), so a foreign plaintext artifact location is dropped rather than dialled; - anything that is not an
http(s)URL is refused.
The Left reason feeds the per-entry drop record; the Right is the https
RegistryUrl the artifact is fetched from. Hosts are compared by the same bare-host
extraction the allowlist uses (hostAddress). This composes with,
and never replaces, the host allowlist and the same-host tarball policy applied at serve
time.