Spring-boot-admin: Security: disable https/TLS certificate hostname check for SBA client only

Created on 7 Mar 2019  路  14Comments  路  Source: codecentric/spring-boot-admin

Problem: when developing self-signed certificate is used and SBA Server can be localhost or LAN ip.
Then SBA client cannot connect to SBA server

09:23:39.045 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator#register:94 - Failed to register application as Application ... : I/O error on POST request for "https://10.10.10.90:9000/api/applications": java.security.cert.CertificateException: No subject alternative names present; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present. Further attempts are logged on DEBUG level

When looking for solutions, e.g. below, they point to disabling host verification for whole app.
However it should be possible to disable/overwrite such check for SBA client only, i.e. not effecting other parts of project application.

  1. from https://stackoverflow.com/questions/19540289/how-to-fix-the-java-security-cert-certificateexception-no-subject-alternative
    HttpsURLConnection.setDefaultHostnameVerifier(
        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

Most helpful comment

Hello @joshiste ,

Thank you for your answer.
I join paulvi comment about SBA.
It is a real struggle to enable two way TLS and to rewrite this ApplicationRegistrator.
Even if the custom RestTemplate is written, injecting it to the ApplicationRegistrator is a tedious task.
The examples purely with http are really good though.
Thank you

All 14 comments

Other option may be to import certificate used by SBA server,
but again it should be affecting only SBA client, not the rest of project application.

Trying to use NoopHostnameVerifier as configuration before Spring context loading, had other exception

Code

    //FIXME this should be for SBA client only
    HttpsURLConnection.setDefaultHostnameVerifier(
            //SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
            // * @deprecated (4.4) Use {@link org.apache.http.conn.ssl.NoopHostnameVerifier}
            new NoopHostnameVerifier()
    );

Exception

10:24:00.931 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator#register:94 - Failed to register application as Application [name=...] at spring-boot-admin ([https://10.10.10.90:9000/api/applications]): I/O error on POST request for "https://10.10.10.90:9000/api/applications": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target. Further attempts are logged on DEBUG level

Instantiate the ApplicationRegistrator bean using a custom configured RestTemplate, the client's autoconfig will back off then.

OK, need to try...

Still in progress.

Could not yet succeed with RestTemplate

public static void main(String[] args) {
        // we are using httpclient 4.5.6.
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();
        HttpComponentsClientHttpRequestFactory requestFactory  = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

        String url ="https://10.10.10.10:9000";
        String response = restTemplate.getForObject(url, String.class);

        //Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://10.10.10.10:9000":
        // sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
        // unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
        // PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    }

@patrick-vonsteht you just disabled the hostname verification, the trust chain is still validated, but this fails.

OK, I succeeded with custom restTemplate (see code below),
but there is problem with ApplicationRegistrator instantiation:

Field admin in com.company.SBAConfig required a bean of type 'de.codecentric.boot.admin.client.config.AdminProperties' that could not be found.

@Component
public class SBAConfig { // restTemplate with TrustSelfSignedStrategy
    @Autowired
    AdminProperties admin;
    @Autowired
    ApplicationFactory applicationFactory;
    private static RestTemplate restTemplate = getCustomRestTemplate();

    //Field admin in SBAConfig required a bean of type 'de.codecentric.boot.admin.client.config.AdminProperties' that could not be found.
    @Bean
    public ApplicationRegistrator getCustomApplicationRegistrator(){
        return new ApplicationRegistrator(restTemplate, admin, applicationFactory);
    }

    static RestTemplate getCustomRestTemplate(){
        // https://stackoverflow.com/questions/19517538/ignoring-ssl-certificate-in-apache-httpclient-4-3
        SSLContextBuilder builder = new SSLContextBuilder();
        try {
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); // disable trust chain validation
            // to solve
            // PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } catch (KeyStoreException e) {
            e.printStackTrace();
            return null;
        }
        SSLContext sslContext = null;
        try {
            sslContext = builder.build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } catch (KeyManagementException e) {
            e.printStackTrace();
            return null;
        }

        //SSLConnectionSocketFactory sslsf1 = new SSLConnectionSocketFactory(sslContext); //disable chain validation, but still has hostname validation:

        SSLConnectionSocketFactory sslsf2 = new SSLConnectionSocketFactory( sslContext, new X509HostnameVerifier() {
            @Override
            public void verify(String host, SSLSocket ssl)
                    throws IOException {
            }

            @Override
            public void verify(String host, X509Certificate cert)
                    throws SSLException {
            }

            @Override
            public void verify(String host, String[] cns,
                               String[] subjectAlts) throws SSLException {
            }

            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });

        // we are using httpclient 4.5.6.
        CloseableHttpClient httpClient = HttpClients.custom()
                //.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // disable the hostname verification
                .setSSLSocketFactory(sslsf2)
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory  = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        return restTemplate;
     } // getCustomRestTemplate()


    // for testing custom RestTemplate
    public static void main(String[] args){
        restTemplate = getCustomRestTemplate();
        String url ="https://10.10.10.10:9000";
        String response = restTemplate.getForObject(url, String.class);

//        AdminProperties admin = null;
//        ApplicationFactory applicationFactory = null;
//
//        ApplicationRegistrator applicationRegistrator = new ApplicationRegistrator(restTemplate, admin, applicationFactory);
    }
}

@joshiste Continuing from https://github.com/codecentric/spring-boot-admin/commit/60c027e2a337df61d591bf620bf360188c39e2ba

Just use the DefaultApplicationFactory provided by sba client

How to use it? It requires even more classes to be created.

    public DefaultApplicationFactory(AdminClientProperties client,
                                     ManagementServerProperties management,
                                     ServerProperties server,
                                     ServletContext servletContext,
                                     String healthEndpointPath) {

See above @Bean creation it fails over 'AdminProperties', now there would be even more.
Should be some correct and easy way....

Yeah you need to add a @EnableConfigurationPropreties for these..

With 2.2.0 it will be easier to customize this as you don't need to instantiate the factory, just a thin wrapper class wrapping the RestTemplate....

I should probably stop here with SBA completely,
as I am spending much more time for it, than getting value from it.
( leaving managements port of apps and SBA server unsecured even during development is not option )
and dev VM don't have fixed names that a certificate should prove.

For those who might follow, there is SpringBootAdminClientAutoConfiguration.java to look at
(choose carefully used version)

https://github.com/codecentric/spring-boot-admin/blob/1.5.x/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java

( found via https://github.com/codecentric/spring-boot-admin/commit/e5bb797c1c107a66b5b53a764da0c8d2e3267d8d )

and RestTemplate getCustomRestTemplate() suggested and implemented above has no use,
as it actually should be RestTemplateBulder to fit instatiation of ApplicationRegistrator.

Hello @joshiste ,

Thank you for this very cool project.
You said, on March 8: "Instantiate the ApplicationRegistrator bean using a custom configured RestTemplate, the client's autoconfig will back off then."

Would you have any example of this?
Because you give many examples over http (again many thanks, it helps a lot) but as soon as we want to do a two way TLS SBA server <-> client, there is no more documentations.

Also, you said on March 19th:"Yeah you need to add a @EnableConfigurationPropreties for these..

With 2.2.0 it will be easier to customize this as you don't need to instantiate the factory, just a thin wrapper class wrapping the RestTemplate...."
And I downloaded 2.2, fixed all the dependency errors, but cannot see what 2.2 will fix it.
Can you please provide a bit of help?

Thank you

@patpatpat123 With 2.2.x depending on wether you have a traditional or a reactive application you need to instantiate the either the BlockingRegistrationClient or ReactiveRegistrationClient yourself with a custom RestTemplate or `WebClient'.

Hello @joshiste ,

Thank you for your answer.
I join paulvi comment about SBA.
It is a real struggle to enable two way TLS and to rewrite this ApplicationRegistrator.
Even if the custom RestTemplate is written, injecting it to the ApplicationRegistrator is a tedious task.
The examples purely with http are really good though.
Thank you

In prometheus, it's as simple as:

    tls_config:
       insecure_skip_verify: true

I'd think something this common should also be a setting in SBA.

Was this page helpful?
0 / 5 - 0 ratings