Class ExternalIssuerAdmission
Why the decision is made here rather than at connect time
On this path the issuer is configuration: an operator writes it, and after that the SDK posts a credential to whatever it names. A rule enforced when the connection is attempted is a rule enforced after DNS, after TCP and after a TLS handshake with the host in question - which is already an outbound signal, already a reachability probe, and already enough to be useful to whoever chose the name. Admission happens once, before anything, and its refusals cost no packets.
The rules
- parseable as an HTTP URL at all;
https- a token crosses this hop;- no userinfo. Checked BEFORE the URI is retained anywhere, because the description of an issuer flows into log lines, exception messages and diagnostic context maps;
- no query and no fragment - an issuer is an origin plus a path, and anything else is either a mistake or an attempt to smuggle a parameter into a URL that gets extended later;
- the host resolves, once, and EVERY address it resolves to is outside the egress reject set
(
RejectedEgressAddresses.isRejectedEgressTarget(InetAddress)).
Why resolution happens here and not only literal screening
Screening the literal text catches https://169.254.169.254/ and nothing else. A name
whose A record is the cloud metadata address passes every textual rule that could be
written, so the shape of a name does not decide it - only resolving it does. The workspace's own
rule for this is that resolution belongs with the caller that will also PIN what it resolved,
which is why admit(String) returns the addresses rather than a verdict: a caller holding
them has no reason, and no way, to look the name up a second time.
The private-address opt-in
"ANKASECURE_ALLOW_PRIVATE_EXTERNAL_ISSUER"="1" in the PROCESS
ENVIRONMENT admits an issuer whose every address is LOOPBACK - the local Keycloak an integrator
develops against. Two deliberate narrowings:
- It is read with
System.getenv(String)directly and is absent fromAuthSetting, matchingCommandExternalTokenProvider.ALLOW_ENV_VARIABLE. A properties file must never be able to widen where this process posts a credential, and the injectable environment seam onAuthSettingsmust never reach it either - the point is precisely that no caller can supply its own answer. - It admits LOOPBACK only. RFC 1918, link-local, CGNAT, unique-local and the transition
prefixes stay refused with the opt-in set, because the environment of a container is not a
trusted input either: an
ENVline in an image would otherwise open that image's entire internal network, and a developer's local issuer is on loopback.
-
Field Summary
Fields -
Method Summary
Modifier and TypeMethodDescriptionstatic AdmittedIssuerAdmits an external issuer, or refuses it locally.static booleanWhether this process environment permits a loopback external issuer.static StringscreenIssuerText(String issuer) Applies the literal rules -- everything decidable from the text -- and nothing else.
-
Field Details
-
ALLOW_PRIVATE_ENV_VARIABLE
The one variable that admits a loopback issuer. Deliberately absent fromAuthSetting.- See Also:
-
ALLOW_PRIVATE_ENV_VALUE
The one value that enables it. Anything else - includingtrue- does not.- See Also:
-
-
Method Details
-
admit
Admits an external issuer, or refuses it locally.- Parameters:
issuer- the configured issuer URL- Returns:
- the admitted issuer, carrying the addresses a caller must dial
- Throws:
ExternalIssuerAdmissionException- naming the rule that refused it. No connection is opened on any path through this method other than the name resolution it performs once.
-
screenIssuerText
Applies the literal rules -- everything decidable from the text -- and nothing else.This is the half of
admit(String)that opens no socket and looks up no name, so it is the half a configuration-time caller may run.initin the crypto CLI is the one in the workspace: it declares an issuer into a file, offline, and must refuse the values the first authentication would refuse -- but it must not resolve a host to do it, because a declaration is not a connection and a laptop configuring a deployment it cannot reach is a legitimate case.It exists so that caller does not re-implement these rules. A second hand-written copy cannot be kept in step: the CLI's own
java.net.URIversion refused every internationalized domain name that this one admits, which is theinitcommand rejecting issuers the SDK would have accepted -- a refusal in the wrong direction, and invisible until someone measured both parsers against the same input.The return value is deliberately
Stringand not the parsed URL: the parser is an implementation detail of this class, and putting it in a signature would export it.- Parameters:
issuer- the configured issuer URL- Returns:
- the issuer as this class parsed it, which may differ from the input in case and in punycode encoding, and never carries userinfo, query or fragment
- Throws:
ExternalIssuerAdmissionException- naming the rule that refused it. No connection is opened and no name is resolved on any path.
-
privateIssuerAllowed
public static boolean privateIssuerAllowed()Whether this process environment permits a loopback external issuer.Reads
System.getenv(String)directly, at the point of use, and takes no argument. That is the control: nothing a caller passes in, and nothing a properties file contains, can change the answer.- Returns:
trueonly when the variable is present with exactly the enabling value
-