Spring-cloud-netflix: CustomSSLSocketFactoryClassName is not working for HttpClient

Created on 14 Mar 2017  路  11Comments  路  Source: spring-cloud/spring-cloud-netflix

I am trying to reconfigure ribbon HttpClient to trust sefl signed certificates
for that reason I set property in my application.yml file CustomSSLSocketFactoryClassName for custom SocketFactory class but it is not working. The validation of the cert is not successful.

When I debug I can see that RibbonLoadBalancingHttpClient is creating new HttpClient delegate but I want to put TrustSelfSignedStrategy configuration.

Most helpful comment

Everything went fine:
At the application class we configured:

@SpringBootApplication(exclude = GatewayRibbonConfig.class)
@ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = GatewayRibbonConfig.class))
@RibbonClients(defaultConfiguration = GatewayRibbonConfig.class)

At GatewayRibbonConfig:

@Configuration
public class GatewayRibbonConfig {

   private static final String NAME = "gateway";

   @Bean
   public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(IClientConfig config,
         ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer, RetryHandler retryHandler) {
      GatewayRibbonLoadBalancingHttpClient client =
            new GatewayRibbonLoadBalancingHttpClient(config, serverIntrospector);
      client.setLoadBalancer(loadBalancer);
      client.setRetryHandler(retryHandler);
      Monitors.registerObject("Client_" + NAME, client);
      return client;
   }
}

and at GatewayRibbonLoadBalancingHttpClient we overrode the createDelegate method with our own HttpClient:

public class GatewayRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient {
   private static final Logger log = LoggerFactory.getLogger(GatewayRibbonLoadBalancingHttpClient.class);
   /**
    * @param config
    * @param serverIntrospector
    */
   public GatewayRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) {
      super(config, serverIntrospector);
   }


   @Override
   protected HttpClient createDelegate(IClientConfig config) {
      return httpClient();
   }

   public static HttpClient httpClient() {
      TrustSelfSignedStrategy trustStrategy = new TrustSelfSignedStrategy();

      SSLContext sslContext = null;
      try {
         sslContext = new SSLContextBuilder().loadTrustMaterial(trustStrategy).build();
      } catch (GeneralSecurityException e) {
         log.error("Error initializing ssl context.", e);
         throw new SSLContextInitError(e);

      }
      SSLConnectionSocketFactory socketFactory =
            new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
      return HttpClientBuilder.create().setSSLSocketFactory(socketFactory).build();
   }

}

Hopefully this code snippets might be useful to someone who hits similar issue.

All 11 comments

Perhaps if the RibbonClientConfiguration$HttpClientRibbonConfiguration bean in the nested Spring context created by SpringClientFactory can be overridden somehow? @spencergibb can you please advice?

Have you looked at #1684? I am not sure what you mean by you set a property in your application.yml? What property did you set?

In the class CommonClientConfigKey there is fields such as
CustomSSLSocketFactoryClassName
IsHostnameValidationRequired
but they are not working as expected.

Hi Ryan, looking at your comment in #1684:

If you create a bean that extends RibbonLoadBalancingHttpClient (or OkHttpLoadBalancingClient depending on what Http Client you want to use) and then override the createDelegate method to customize the HttpClient you should be able to tell the client to not validate the certificate.

How do we register this bean that extends RibbonLoadBalancingHttpClient? We (Yordan and me) are confused how to properly do this given the nested Spring contexts operated by SpringClientFactory.

@ryanjbaxter Could you please give us a code example how we can override the createDelegate method.

@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
@SpringBootApplication
/* ... */

class DefaultRibbonConfig {
    @Bean
    public MyClient myClient() {
        return /* ... */;
    }
}

Thank you so much for the example. We somehow missed it in the documentation - http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_ribbon_client.
We will update tomorrow once we verify that everything is working.

Everything went fine:
At the application class we configured:

@SpringBootApplication(exclude = GatewayRibbonConfig.class)
@ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = GatewayRibbonConfig.class))
@RibbonClients(defaultConfiguration = GatewayRibbonConfig.class)

At GatewayRibbonConfig:

@Configuration
public class GatewayRibbonConfig {

   private static final String NAME = "gateway";

   @Bean
   public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(IClientConfig config,
         ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer, RetryHandler retryHandler) {
      GatewayRibbonLoadBalancingHttpClient client =
            new GatewayRibbonLoadBalancingHttpClient(config, serverIntrospector);
      client.setLoadBalancer(loadBalancer);
      client.setRetryHandler(retryHandler);
      Monitors.registerObject("Client_" + NAME, client);
      return client;
   }
}

and at GatewayRibbonLoadBalancingHttpClient we overrode the createDelegate method with our own HttpClient:

public class GatewayRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient {
   private static final Logger log = LoggerFactory.getLogger(GatewayRibbonLoadBalancingHttpClient.class);
   /**
    * @param config
    * @param serverIntrospector
    */
   public GatewayRibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) {
      super(config, serverIntrospector);
   }


   @Override
   protected HttpClient createDelegate(IClientConfig config) {
      return httpClient();
   }

   public static HttpClient httpClient() {
      TrustSelfSignedStrategy trustStrategy = new TrustSelfSignedStrategy();

      SSLContext sslContext = null;
      try {
         sslContext = new SSLContextBuilder().loadTrustMaterial(trustStrategy).build();
      } catch (GeneralSecurityException e) {
         log.error("Error initializing ssl context.", e);
         throw new SSLContextInitError(e);

      }
      SSLConnectionSocketFactory socketFactory =
            new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
      return HttpClientBuilder.create().setSSLSocketFactory(socketFactory).build();
   }

}

Hopefully this code snippets might be useful to someone who hits similar issue.

@mvalkanov you wrote above code in eureka or in ZUUL because when i write same code in eureka then i got error

The above code we use with Zuul.
In other projects Feign automatically used a HttpClient bean from the root Spring context.

I don't have experience with Eureka. We use a custom solution for service discovery based on Consul and have integrated it with Ribbon.

Thanks a lot for posting this example here, i was struggeling for hours to get a new custom RibbonLoadBalancingHttpClient bean to work. Without this issue, i would still be lost. I am thinking about submitting a PR to improve the documentation on this topic. http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_ribbon_client only describes the @RibbonClient Annotation, not @RibbonClients with defaultConfiguration property.

Was this page helpful?
0 / 5 - 0 ratings