private FormValidation checkUrl()

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()));
        }
    }