Class RemoteUrlPolicy
@Remote/@RemoteOp proxy engines.
Replaces the earlier scheme-only guard (@Remote URL (an explicit @Url value, a baseUrl()
override, or an already-absolute default-path fullPath) is additionally checked against a deny-list of
loopback, RFC1918/link-local, and cloud-metadata targets — both lexically (the literal host string) and,
separately, against the resolved address actually connected to ("pin-on-connect", see
selectAllowedAddress(String, boolean, AddressResolver)).
Scheme-less (relative) values are unaffected — they resolve against the operator-configured client root, which is not re-checked by this policy.
Both engines (next-generation RestClient.remote(...) and classic RestClient.getRemote(...)) call
into this single shared class so the deny-list and pin-selection logic are defined exactly once.
- Since:
- 10.0.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceResolves theInetAddresscandidates for a hostname.static final classA validated, parsed per-client allow-list of special-purpose address ranges, the per-client counterpart of the "RestClient.allowAddressRanges" system property. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe system property holding the narrow lab allow-list: a comma- or whitespace-separated list of CIDR ranges (e.g.static final StringTheRestClientbuilder/settings key for theallowPrivateUrlsopt-in (system property fallback).static final intThe maximum number of redirect hops a policy-covered@Remotecall will follow before failing. -
Method Summary
Modifier and TypeMethodDescriptionstatic booleanisDeniedAddress(InetAddress addr) Returnstrue ifaddris denied by the default policy -- the deny-list applied both to literal-IP hosts (pre-check) and to resolved addresses (pin-on-connect).static booleanisDeniedAddress(InetAddress addr, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asisDeniedAddress(InetAddress), additionally re-permitting special-purpose addresses inside the per-clientallowedRanges.static booleanisDeniedHost(String host) Returnstrue if the literalhoststring is denied by the default policy: a dotted-decimal IPv4/bracketed-IPv6 literal classified as denied byisDeniedAddress(InetAddress)(an IPv6 zone id is ignored, and an unparseable literal containing a:is denied), or a case-insensitive match onlocalhost/localhost.localdomain/metadata.google.internal(and its subdomains).static booleanisDeniedHost(String host, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asisDeniedHost(String), taking the per-clientallowedRangesinto account.static StringrequireAllowedUrl(String url, boolean allowPrivateUrls) Enforces the SSRF guardrail on an effective@RemoteURL: whenurlcarries a URI scheme, it must be a canonical absolutehttp/httpsURL with a nonempty host, and — unlessallowPrivateUrlsis set — the literal host must not match the deny-list (see class javadoc).static StringrequireAllowedUrl(String url, boolean allowPrivateUrls, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asrequireAllowedUrl(String, boolean), additionally re-permitting literal hosts that fall inside the per-clientallowedRanges(unioned with the "RestClient.allowAddressRanges" system property).static URIresolveRedirectLocation(URI from, String location) Resolves a redirectLocationheader value against the current request URI, per RFC 7231 §7.1.2 (relative locations are resolved against the request URI).static StringReturns the URI scheme of a URL (the token before the first:when it precedes any/,?or#), ornull if the value has no scheme (e.g. a relative path or a value beginning with a{var}token).static InetAddressselectAllowedAddress(String host, boolean allowPrivateUrls, RemoteUrlPolicy.AddressResolver resolver) Resolveshostand returns the first candidate address that is allowed by the default policy ("pin-on-connect") -- the address the caller should actually connect to.static InetAddressselectAllowedAddress(String host, boolean allowPrivateUrls, RemoteUrlPolicy.AddressResolver resolver, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asselectAllowedAddress(String, boolean, AddressResolver), additionally re-permitting candidates inside the per-clientallowedRanges(unioned with the "RestClient.allowAddressRanges" system property).
-
Field Details
-
MAX_REDIRECT_HOPS
The maximum number of redirect hops a policy-covered@Remotecall will follow before failing.- See Also:
-
ALLOW_PRIVATE_URLS_PROPERTY
TheRestClientbuilder/settings key for theallowPrivateUrlsopt-in (system property fallback).- See Also:
-
ALLOW_ADDRESS_RANGES_PROPERTY
The system property holding the narrow lab allow-list: a comma- or whitespace-separated list of CIDR ranges (e.g.198.18.0.0/15,192.0.0.8) that re-permit addresses denied only because they fall in one of the special-purpose ranges192.0.0.0/24 ,198.18.0.0/15 ,224.0.0.0/4 ,240.0.0.0/4 orff00::/8 . Unset or blank (the default) allows nothing.The allow is narrow by construction: every entry must lie entirely inside one of those five ranges (anything else, including
0.0.0.0/0 and::/0 , is rejected with anIllegalArgumentException), and it never re-permits loopback, link-local/metadata, private, CGNAT, this-network, Teredo, local-use NAT64 or any other denied address, unlike the all-or-nothingALLOW_PRIVATE_URLS_PROPERTYopt-in. An IPv4 entry matches an IPv4 address or the IPv4 address embedded in an IPv6 form; an IPv6 entry matches only a nativeff00::/8 address.- See Also:
-
-
Method Details
-
requireAllowedUrl
Enforces the SSRF guardrail on an effective@RemoteURL: whenurlcarries a URI scheme, it must be a canonical absolutehttp/httpsURL with a nonempty host, and — unlessallowPrivateUrlsis set — the literal host must not match the deny-list (see class javadoc). Scheme-less (relative) values pass through unchanged (they resolve against the client root, which is not re-checked here).This is the pre-check stage only. It does not resolve DNS; a hostname that is not itself denied but resolves to a denied address is instead caught by
selectAllowedAddress(String, boolean, AddressResolver)("pin-on-connect") at connect time, including on every followed redirect hop.- Parameters:
url- The effective URL/path to validate.allowPrivateUrls- Whentrue , the deny-list check is skipped (the http/https scheme requirement and canonical-URI requirement still apply).- Returns:
- The unchanged
url. - Throws:
IllegalArgumentException- Ifurlis absolute and violates the policy.
-
requireAllowedUrl
public static String requireAllowedUrl(String url, boolean allowPrivateUrls, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asrequireAllowedUrl(String, boolean), additionally re-permitting literal hosts that fall inside the per-clientallowedRanges(unioned with the "RestClient.allowAddressRanges" system property).- Parameters:
url- The effective URL/path to validate.allowPrivateUrls- Whentrue , the deny-list check is skipped.allowedRanges- The per-client narrow allow-list. Must not benull .- Returns:
- The unchanged
url. - Throws:
IllegalArgumentException- Ifurlis absolute and violates the policy.
-
isDeniedHost
Returnstrue if the literalhoststring is denied by the default policy: a dotted-decimal IPv4/bracketed-IPv6 literal classified as denied byisDeniedAddress(InetAddress)(an IPv6 zone id is ignored, and an unparseable literal containing a:is denied), or a case-insensitive match onlocalhost/localhost.localdomain/metadata.google.internal(and its subdomains).A bare decimal/octal/hex IP literal (e.g.
http://2130706433) does not look like a dotted IPv4 literal and is intentionally left toselectAllowedAddress(String, boolean, AddressResolver)(resolved at pin-on-connect time), not this lexical pre-check.- Parameters:
host- The literal host string (fromURI.getHost()).- Returns:
true if the host is denied by the default policy.
-
isDeniedHost
Same asisDeniedHost(String), taking the per-clientallowedRangesinto account.- Parameters:
host- The literal host string (fromURI.getHost()).allowedRanges- The per-client narrow allow-list. Must not benull .- Returns:
true if the host is denied.
-
isDeniedAddress
Returnstrue ifaddris denied by the default policy -- the deny-list applied both to literal-IP hosts (pre-check) and to resolved addresses (pin-on-connect).Denied addresses:
- Loopback, unspecified, link-local (including the
169.254.169.254 metadata address) and RFC1918 site-local addresses. - IPv4
0.0.0.0/8 ("this network") and100.64.0.0/10 (carrier-grade NAT shared space). - IPv4 special-purpose ranges that are never legitimate remote targets:
192.0.0.0/24 (IETF protocol assignments),198.18.0.0/15 (benchmarking; often lab or appliance networks — seeALLOW_ADDRESS_RANGES_PROPERTY),224.0.0.0/4 (multicast) and240.0.0.0/4 (reserved, including the limited broadcast address255.255.255.255 ). - IPv6 unique-local
fc00::/7 and multicastff00::/8 . - IPv6 Teredo
2001::/32 , denied outright because the embedded client IPv4 address is obfuscated. - IPv6 local-use NAT64
64:ff9b:1::/48 (RFC 8215), denied outright because the embedding length (/48, /56, /64 or /96) is operator-defined and cannot be determined from the address.
IPv6 forms that embed an IPv4 address are unwrapped and classified by the embedded IPv4 address: IPv4-mapped (
::ffff:a.b.c.d ), IPv4-compatible (::a.b.c.d ), IPv4-translated (::ffff:0:a.b.c.d ), NAT64 (64:ff9b::/96 ) and 6to4 (2002::/16 , IPv4 in bytes 2-5). A public embedded address (e.g.64:ff9b::808:808 ) remains allowed.Addresses inside a range listed in the "RestClient.allowAddressRanges" system property (empty by default) are re-permitted if they are denied only as a special-purpose range; see
ALLOW_ADDRESS_RANGES_PROPERTY.- Parameters:
addr- The address to classify. Must not benull .- Returns:
true if the address is denied, taking the "RestClient.allowAddressRanges" allow-list into account.- Throws:
IllegalArgumentException- If the "RestClient.allowAddressRanges" property is malformed.
- Loopback, unspecified, link-local (including the
-
isDeniedAddress
public static boolean isDeniedAddress(InetAddress addr, RemoteUrlPolicy.AllowedRanges allowedRanges) Same asisDeniedAddress(InetAddress), additionally re-permitting special-purpose addresses inside the per-clientallowedRanges. The effective allow-list is the union of those ranges and the "RestClient.allowAddressRanges" system property; both are bounded to the same five special-purpose ranges, so the union can never re-open any other deny.- Parameters:
addr- The address to classify. Must not benull .allowedRanges- The per-client narrow allow-list. Must not benull .- Returns:
true if the address is denied.- Throws:
IllegalArgumentException- If the "RestClient.allowAddressRanges" property is malformed.
-
selectAllowedAddress
public static InetAddress selectAllowedAddress(String host, boolean allowPrivateUrls, RemoteUrlPolicy.AddressResolver resolver) throws UnknownHostException Resolveshostand returns the first candidate address that is allowed by the default policy ("pin-on-connect") -- the address the caller should actually connect to. When multiple candidates are returned (e.g. mixed A/AAAA), the first allowed one wins; a candidate that is itself denied is skipped rather than rejecting the whole resolution, so a name with one public and one loopback address may still be used.- Parameters:
host- The hostname (or IP literal) to resolve and pin.allowPrivateUrls- Whentrue , the deny-list check is skipped and the first resolved candidate is returned unconditionally.resolver- The address resolver to use. PassRemoteUrlPolicy.AddressResolver.DEFAULTfor real DNS, or an injectable stub in tests to simulate rebinding/mixed-family resolution without live DNS.- Returns:
- The selected address to connect to. Never
null . - Throws:
UnknownHostException- Ifhostcannot be resolved.IllegalArgumentException- If every resolved candidate is denied (andallowPrivateUrlsisfalse ).
-
selectAllowedAddress
public static InetAddress selectAllowedAddress(String host, boolean allowPrivateUrls, RemoteUrlPolicy.AddressResolver resolver, RemoteUrlPolicy.AllowedRanges allowedRanges) throws UnknownHostException Same asselectAllowedAddress(String, boolean, AddressResolver), additionally re-permitting candidates inside the per-clientallowedRanges(unioned with the "RestClient.allowAddressRanges" system property).- Parameters:
host- The hostname (or IP literal) to resolve and pin.allowPrivateUrls- Whentrue , the deny-list check is skipped.resolver- The address resolver to use.allowedRanges- The per-client narrow allow-list. Must not benull .- Returns:
- The selected address to connect to. Never
null . - Throws:
UnknownHostException- Ifhostcannot be resolved.IllegalArgumentException- If every resolved candidate is denied.
-
resolveRedirectLocation
Resolves a redirectLocationheader value against the current request URI, per RFC 7231 §7.1.2 (relative locations are resolved against the request URI).- Parameters:
from- The current (pre-redirect) request URI. Must be absolute.location- TheLocationheader value. Must not benull or blank.- Returns:
- The resolved absolute redirect-target URI.
- Throws:
IllegalArgumentException- Iflocationis blank or does not resolve to an absolute URI.
-
schemeOf
Returns the URI scheme of a URL (the token before the first:when it precedes any/,?or#), ornull if the value has no scheme (e.g. a relative path or a value beginning with a{var}token).- Parameters:
url- The URL to inspect.- Returns:
- The scheme, or
null if none.
-