diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 015967f7..3c9deefa 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -108,6 +108,16 @@ information will have a secure health check URL. Because of the way Eureka works internally, it will still publish a non-secure URL for status and home page unless you also override those explicitly. +NOTE: If your app is running behind a proxy, and the SSL termination +is in the proxy (e.g. if you run in Cloud Foundry or other platforms +as a service) then you will need to ensure that the proxy "forwarded" +headers are intercepted and handled by the application. An embedded +Tomcat container in a Spring Boot app does this automatically for most +proxies (since 1.3.0), but other containers might need explicit +configuration for the 'X-Forwarded-\*` headers. A sign that you got +this wrong will be that the links rendered by your app to itslef will be +wrong (the wrong host, port or protocol). + === Eureka's Health Checks By default, Eureka uses the client heartbeat to determine if a client is up. diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java index 9a0e173b..63ce04ab 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.netflix.ribbon; +import java.net.URI; + import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy; import org.springframework.beans.factory.annotation.Value; @@ -24,7 +26,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.util.ClassUtils; +import org.springframework.web.util.UriComponentsBuilder; +import com.netflix.appinfo.InstanceInfo.PortType; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ConfigurationBasedServerList; @@ -39,6 +44,7 @@ import com.netflix.loadbalancer.ServerListFilter; import com.netflix.loadbalancer.ZoneAvoidanceRule; import com.netflix.loadbalancer.ZoneAwareLoadBalancer; import com.netflix.niws.client.http.RestClient; +import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; import com.netflix.servo.monitor.Monitors; import com.sun.jersey.api.client.Client; import com.sun.jersey.client.apache4.ApacheHttpClient4; @@ -90,9 +96,9 @@ public class RibbonClientConfiguration { /** * Create a Netflix {@link RestClient} integrated with Ribbon if none already exists in the - * application context. It is not required for Ribbon to work properly and is therefore + * application context. It is not required for Ribbon to work properly and is therefore * created lazily if ever another component requires it. - * + * * @param config the configuration to use by the underlying Ribbon instance * @param loadBalancer the load balancer to use by the underlying Ribbon instance * @return a {@link RestClient} instances backed by Ribbon @@ -141,6 +147,27 @@ public class RibbonClientConfiguration { initWithNiwsConfig(ncc); } + @Override + public URI reconstructURIWithServer(Server server, URI original) { + String scheme = original.getScheme(); + if (!"https".equals(scheme) && isSecure(server)) { + original = UriComponentsBuilder.fromUri(original).scheme("https").build().toUri(); + } + return super.reconstructURIWithServer(server, original); + } + + private boolean isSecure(Server server) { + if (ClassUtils.isPresent("com.netflix.niws.loadbalancer.DiscoveryEnabledServer", + null)) { + if (server instanceof DiscoveryEnabledServer) { + DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server; + return enabled.getInstanceInfo().isPortEnabled(PortType.SECURE); + } + } + // Can we do better? + return (""+server.getPort()).endsWith("443"); + } + @Override protected Client apacheHttpClientSpecificInitialization() { ApacheHttpClient4 apache = (ApacheHttpClient4) super diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java index 1bedb94e..edc92025 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java @@ -24,14 +24,17 @@ import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.util.UriComponentsBuilder; +import com.netflix.appinfo.InstanceInfo.PortType; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerStats; +import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; import com.netflix.servo.monitor.Stopwatch; /** @@ -53,9 +56,9 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { RibbonLoadBalancerContext context = this.clientFactory .getLoadBalancerContext(serviceId); Server server = new Server(instance.getHost(), instance.getPort()); - boolean secure = isSecure(this.clientFactory, serviceId); + boolean secure = isSecure(this.clientFactory, server, serviceId); URI uri = original; - if(secure) { + if (secure) { uri = UriComponentsBuilder.fromUri(uri).scheme("https").build().toUri(); } return context.reconstructURIWithServer(server, uri); @@ -67,7 +70,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { if (server == null) { return null; } - return new RibbonServer(serviceId, server, isSecure(this.clientFactory, serviceId)); + return new RibbonServer(serviceId, server, + isSecure(this.clientFactory, server, serviceId)); } @Override @@ -76,7 +80,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { RibbonLoadBalancerContext context = this.clientFactory .getLoadBalancerContext(serviceId); Server server = getServer(loadBalancer); - RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(clientFactory, serviceId)); + RibbonServer ribbonServer = new RibbonServer(serviceId, server, + isSecure(this.clientFactory, server, serviceId)); ServerStats serverStats = context.getServerStats(server); context.noteOpenConnection(serverStats); @@ -93,20 +98,30 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { } return null; } - - private boolean isSecure(SpringClientFactory clientFactory, String serviceId) { + + private boolean isSecure(SpringClientFactory clientFactory, Server server, + String serviceId) { IClientConfig config = clientFactory.getClientConfig(serviceId); - if(config != null) { + if (config != null) { return config.get(CommonClientConfigKey.IsSecure, false); } - return false; + if (ClassUtils.isPresent("com.netflix.niws.loadbalancer.DiscoveryEnabledServer", + null)) { + if (server instanceof DiscoveryEnabledServer) { + DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server; + return enabled.getInstanceInfo().isPortEnabled(PortType.SECURE); + } + } + // Can we do better? + return ("" + server.getPort()).endsWith("443"); } private void recordStats(RibbonLoadBalancerContext context, Stopwatch tracer, ServerStats serverStats, Object entity, Throwable exception) { tracer.stop(); long duration = tracer.getDuration(TimeUnit.MILLISECONDS); - context.noteRequestCompletion(serverStats, entity, exception, duration, null/* errorHandler */); + context.noteRequestCompletion(serverStats, entity, exception, duration, + null/* errorHandler */); } protected Server getServer(String serviceId) { @@ -117,7 +132,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { if (loadBalancer == null) { return null; } - return loadBalancer.chooseServer("default"); //TODO: better handling of key + return loadBalancer.chooseServer("default"); // TODO: better handling of key } protected ILoadBalancer getLoadBalancer(String serviceId) { @@ -128,7 +143,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { private String serviceId; private Server server; private boolean secure; - + protected RibbonServer(String serviceId, Server server) { this(serviceId, server, false); }