in core/src/main/java/jenkins/security/ResourceDomainConfiguration.java [92:190]
private FormValidation checkUrl(String resourceRootUrlString, boolean allowOnlineIdentityCheck) {
String jenkinsRootUrlString = JenkinsLocationConfiguration.get().getUrl();
if (ExtensionList.lookupSingleton(RootUrlNotSetMonitor.class).isActivated() || jenkinsRootUrlString == null) {
// This is needed to round-trip expired resource URLs through regular URLs to refresh them,
// so while it's not required in the strictest sense, it is required.
return FormValidation.warning(Messages.ResourceDomainConfiguration_NeedsRootURL());
}
resourceRootUrlString = Util.fixEmptyAndTrim(resourceRootUrlString);
if (resourceRootUrlString == null) {
return FormValidation.ok(Messages.ResourceDomainConfiguration_Empty());
}
if (!UrlHelper.isValidRootUrl(resourceRootUrlString)) {
return FormValidation.error(Messages.ResourceDomainConfiguration_Invalid());
}
if (!resourceRootUrlString.endsWith("/")) {
resourceRootUrlString += '/';
}
URL resourceRootUrl;
try {
resourceRootUrl = new URL(resourceRootUrlString);
} catch (MalformedURLException ex) {
return FormValidation.error(Messages.ResourceDomainConfiguration_Invalid());
}
String resourceRootUrlHost = resourceRootUrl.getHost();
try {
String jenkinsRootUrlHost = new URL(jenkinsRootUrlString).getHost();
if (jenkinsRootUrlHost.equals(resourceRootUrlHost)) {
// We do not allow the same host for Jenkins and resource root URLs even if there's some other difference.
// This is a conservative choice and prohibits same host/different proto/different port/different path:
// - Different path still counts as the same origin for same-origin policy
// - Cookies are shared across ports, and non-Secure cookies get sent to HTTPS sites
return FormValidation.error(Messages.ResourceDomainConfiguration_SameAsJenkinsRoot());
}
} catch (Exception ex) {
LOGGER.log(Level.CONFIG, "Failed to create URL from the existing Jenkins root URL", ex);
return FormValidation.error(Messages.ResourceDomainConfiguration_InvalidRootURL(ex.getMessage()));
}
StaplerRequest currentRequest = Stapler.getCurrentRequest();
if (currentRequest != null) {
String currentRequestHost = currentRequest.getHeader("Host");
if (currentRequestHost.equals(resourceRootUrlHost)) {
return FormValidation.error(Messages.ResourceDomainConfiguration_SameAsCurrent());
}
}
if (!allowOnlineIdentityCheck) {
return FormValidation.ok();
}
// Send a request to /instance-identity/ at the resource root URL and check whether it is this Jenkins
try {
URLConnection urlConnection = new URL(resourceRootUrlString + "instance-identity/").openConnection();
if (urlConnection instanceof HttpURLConnection) {
HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == 200) {
String identityHeader = urlConnection.getHeaderField("X-Instance-Identity");
if (identityHeader == null) {
return FormValidation.warning(Messages.ResourceDomainConfiguration_NotJenkins());
}
// URL points to a Jenkins instance
RSAPublicKey publicKey = InstanceIdentityProvider.RSA.getPublicKey();
if (publicKey != null) {
String identity = Base64.getEncoder().encodeToString(publicKey.getEncoded());
if (identity.equals(identityHeader)) {
return FormValidation.ok(Messages.ResourceDomainConfiguration_ThisJenkins());
}
return FormValidation.warning(Messages.ResourceDomainConfiguration_OtherJenkins());
} // the current instance has no public key
return FormValidation.warning(Messages.ResourceDomainConfiguration_SomeJenkins());
}
// response is error
String responseMessage = httpURLConnection.getResponseMessage();
if (responseCode == 404) {
String responseBody = String.join("", IOUtils.readLines(httpURLConnection.getErrorStream(), StandardCharsets.UTF_8));
if (responseMessage.contains(ERROR_RESPONSE) || responseBody.contains(ERROR_RESPONSE)) {
return FormValidation.ok(Messages.ResourceDomainConfiguration_ResourceResponse());
}
}
return FormValidation.error(Messages.ResourceDomainConfiguration_FailedIdentityCheck(responseCode, responseMessage));
}
return FormValidation.error(Messages.ResourceDomainConfiguration_Invalid()); // unlikely to ever be hit
} catch (MalformedURLException ex) {
// Not expected to be hit
LOGGER.log(Level.FINE, "MalformedURLException occurred during instance identity check for " + resourceRootUrlString, ex);
return FormValidation.error(Messages.ResourceDomainConfiguration_Exception(ex.getMessage()));
} catch (IOException ex) {
LOGGER.log(Level.FINE, "IOException occurred during instance identity check for " + resourceRootUrlString, ex);
return FormValidation.warning(Messages.ResourceDomainConfiguration_IOException(ex.getMessage()));
}
}