11
votes

We want to use HTTPS for our microservices communication based on Feign and Ribbon. The services are based on spring boot and tomcat is correctly setup. The instances are registered with the HTTPS URL and securePort enabled on Eureka. However, when we call another microservice via Feign then the underlying Ribbon doesn't recognizes the protocol and falls back to HTTP. I could solve that problem by adding the protocol to the FeignClient annotation like this:

    @FeignClient("https://users")

But it seem that the Zuul proxy and the Hystrix/Turbine which are also using Ribbon internally have the same HTTP fallback problem. Is there any way to configure Ribbon centrally to use HTTPS as default or use the securePort setting of the registred eureka instance?

Eureka instance configuration:

eureka.instance.hostname=localhost
eureka.instance.securePort = ${server.port}
eureka.instance.securePortEnabled = true  
eureka.instance.nonSecurePortEnabled = false 
eureka.instance.metadataMap.hostname = ${eureka.instance.hostname}
eureka.instance.metadataMap.securePort = ${server.port}
eureka.instance.homePageUrl = https://${eureka.instance.hostname}:${server.port}/
eureka.instance.statusPageUrl = https://${eureka.instance.hostname}:${server.port}/admin/info

With these settings it looks in Eureka like the service runs on HTTPS. The Zuul proxy runs fine, but uses the HTTP URL to call the service. You have to enable SSL in Spring Boots embedded Tomcat by providing a server certificate in a keystore:

server.ssl.key-store=server.jks
server.ssl.key-store-password=<pw>
server.ssl.keyStoreType=jks
server.ssl.keyAlias=tomcat
server.ssl.key-password=<pw> 

Tomcat than only runs on HTTPS and the HTTP port is blocked, but than I get: localhost:8081 failed to respond because a HTTP URL is used to call the service. By setting ribbon.IsSecure=true the users service url is correctly generated, but the Ribbon loadbalancer fails to lookup the users service in Eureka: Load balancer does not have available server for client: users. I aslo tried to set users.ribbon.IsSecure=true in the zuul proxy only, but still get the same error.

Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: user
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:468)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180)
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable.unsafeSubscribe(Observable.java:7304)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:112)
at rx.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:81)
at rx.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:59)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:77)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:41)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:30)
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable.subscribe(Observable.java:7393)
at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:441)
at rx.observables.BlockingObservable.single(BlockingObservable.java:340)
at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:102)
at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:81)
at org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand.forward(RibbonCommand.java:129)
at org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand.run(RibbonCommand.java:103)
at org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand.run(RibbonCommand.java:1)
at com.netflix.hystrix.HystrixCommand$1.call(HystrixCommand.java:298)
3
If you use eureka's securePort, that should be the port registered with eureka.spencergibb
I confirm spencergibb's comment: we are not having any problem with ZUUL forwarding to services listening to 443 (make sure you enabled the secure port in your client with 'eureka.instance.securePortEnabled=true') However, I wonder if you shouldn't always use "http" instead of "https" in your @FeignClient annotation whatever the actual port your instances are listening.Bertrand Renuart
The service is registered with eureka correctly I guess. We are using the following properties: eureka.instance.securePort = ${server.port} eureka.instance.securePortEnabled = true eureka.instance.nonSecurePortEnabled = false but still getting com.netflix.zuul.exception.ZuulException: Forwarding error Caused by: com.sun.jersey.api.client.ClientHandlerException: org.apache.http.NoHttpResponseException: <host>:8081 failed to respondDaniel Sass
Sorry, I was busy with other stuff, but today I could debug this a bit further. I think the magic lies in LoadBalancerContext.deriveSchemeAndPortFromPartialUri(). When I set ribbon.IsSecure=true than the URL calculation for the service is correct in reconstructURIWithServer() like https://<host>:8081/users/1. But than the service lookup in Eureka fails in LoadBalancerContext.getServerFromLoadBalancer() because the URL is https://<host>:443 and not https://<host>:8761. I'll continue debugging next week ...Daniel Sass
No luck so far. We configured Eureka listening on 443, but get now Load balancer does not have available server for client: users so it seems that that LoadBalancerContext.getServerFromLoadBalancer() can't lookup an user service instance. We want to run all microservices on HTTPS and several of them on one host differentiated by ports. So it wouldn't be an option for us to use the default port. Any ideas how to set this up?Daniel Sass

3 Answers

7
votes

We solved the zuul proxy problem now by setting

ribbon.IsSecure=true
eureka.instance.secureVirtualHostName=${spring.application.name}

so that all services are also in the secure virtual hosts pool in com.netflix.discovery.shared.Applications. That helps the discovery process to find the instances in eureka.

However, the Hystrix dashboard has still a similar problem

0
votes

I have the same problem trying to configure Zuul proxy to use Ribbon to connect to micro services running on https: RibbonRoutingFilter is looking at requestURI and create RestClient when run method is executed. I configured Eureka to run on http though. It is the underlying https microservice registered on Eureka cannot be accessed by Ribbon.

http works perfectly with simple zuul routes set up.

0
votes

After adding isSecure:true also there might be some possibility for an unsuccessful handshake, faced this once and I resolve this by adding the Truststore like below

-Djavax.net.ssl.trustStore="//location of.JKS file " and -Djavax.net.ssl.keyStorePassword="valid password".

Thes one helped me to fix the HTTPS handshake issues