How to redirect to https with Spring cloud gateway & ribbon - spring

I have 2 microservices working with https, I added spring cloud gateway api to centralize the routes but I faced a problem where it says: This combination of host and port requires TLS I'm pretty sure that's because of the routes configuration on my gateway, I dont have much experience on this side but if someone could help me.
this is my spring gateway routes configuration :
#Bean
public RouteLocator gatewayRouter(RouteLocatorBuilder builder){
return builder.routes()
.route(p -> p.path("/api/v1/**")
.uri("lb://statement"))
.route( p -> p.path("/api/v3/**")
.uri("lb://activiti-workflow"))
.build();
}
Please I just want to know if this configuration will redirects to https or not, because while sending http requests directly to the microservices it works but with gateway is doesn't.

If your backend services in eureka provide https, your can set route uri like this:
uri: lb:https://your-service-name
Or
.uri("lb:https://your-service-name")
It just like websocket configuration. The key class is org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter
More info see at: https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-routetorequesturl-filter
If your backend services use insecure HTTPS cert, your may need config this:
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
See at: https://cloud.spring.io/spring-cloud-gateway/reference/html/#tls-and-ssl

Related

Expose public and private endpoint through Spring Cloud Gateway

I'm using Spring Cloud Gateway like entrypoint for my infrastructure. The gateway is configured with keycloak to validate Authentication header with following configuration
spring:
security:
oauth2:
resource-server:
jwt:
jwk-set-uri: https://httpd.keycloak.local:443/keycloak/realms/myRealm/protocol/openid-connect/certs
An example Route is the following
spring:
cloud:
gateway:
routes:
- id: my-route
uri: http://service.local:8020
predicates:
- Path=/myPath/api/myRoute/test
filters:
- name: StripPrefix
args:
parts: 2
How can I define, into yml file, this route public and another one authenticated through jwk-uri directed to keycloak?
I see nothing about security rules in spring-cloud-gateway configuration doc.
I believe you'll have to either:
let all traffic go through gateway and handle security on each service
write a web-security configuration class and define a SecurityWebFilterChain bean.
use spring-addons-webflux-jwt-resource-server to define permitAll() routes from properties (other routes requirering user to be authenticated)
Only last solution would work with yaml file configuration only:
in pom, replace spring-boot-starter-oauth2-resource-server with
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
<version>${com.c4-soft.springaddons.version}</version>
</dependency>
in yaml, replace spring.security.oauth2.resource-server with
com.c4-soft.springaddons.security:
jwt-issuers:
- location: https://httpd.keycloak.local:443/keycloak/realms/myRealm
permit-all:
- /myPath/api/myRoute/test
- /public/**
Note that trying to access "non-public" routes without valid authorization would result in 401 (unauthorized) and not in 302 (redirect to login). In my opinion, client should unsure requests to protected routes are issued with Authorization header or handle unauthorized with a redirection to authorization-server and a retry.
Also note that spring-security-oauth2-webflux-addons will auto-configure more than just permit-all routes (CORS, CSRF and authorities mapping for instance).
Last, I haven't tried it yet with spring-cloud-gateway. please let me know how it goes ;-)

Access secured actuators from Spring Boot Admin with Kubernetes Service Discovery

I've got a Spring Boot Admin application which uses a Kubernetes Service Discovery to get the Spring Boot client applications.
spring:
cloud:
kubernetes:
discovery:
all-namespaces: true
service-labels:
springbootadmin: true
reload:
enabled: true
period: 60s
strategy: refresh
Without secured actuator endpoints this works fine.
But as soon as the client actuator endpoints are protected by basic auth this does not work any more. The Spring Boot Admin Documentation describes how to add the authentication data to the Spring Boot Admin Server bit it does not describe how to provide this when the services are discovered via Kubernetes.
I've tried these configurations. But they don't work:
Spring Boot Admin Docs: spring.boot.admin.instance-auth.default-user-name + password
Spring Boot Admin Tutorial spring.boot.admin.client.instance.metadata.user.name + password
I also found an answer which describes how to configure the credentials in the Kubernetes annotations. This works but I would prefer to configure the credentials in the Spring Boot Admin configuration (where I can use Secrets) and not separately for each service in the Kubernetes configuration as an unsecure label.
I think I have to inject the credentials in the Service Discovery metadata. But how?
EDIT
I've examined the service discovery and found no auth configuration options which could be provided:
class KubernetesDiscoveryProperties.Metadata
class de.codecentric.boot.admin.server.cloud.discovery.DefaultServiceInstanceConverter
It might be an option to add a custom header to the requests that are sent by SBA to the clients:
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return (instance) -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization", "Basic bXlTcGVjaWFsVXNlcm5hbWU6bXlTcGVjaWFsUGFzc3dvcmQ=");
return httpHeaders;
};
}
The authentication can be set by these settings:
spring:
boot:
admin:
instance-auth:
default-user-name: user
default-password: pw
These settings are read by the Configuration Class AdminServerInstanceWebClientConfiguration which instantiates a bean basicAuthHttpHeadersProvider.

Handling of requests locally by Zuul Gateway server

How do I make Spring Boot Zuul Proxy Server handle a specific request locally, instead of proxying it to some other Server?
Let's say I want to do a custom health check of Zuul Proxy Server itself through an API which should return a local response, instead of returning a proxied response from some other remote server.
What kind of route configuration is required to forward the request?
Following is what I figured out and it worked for me:
1) Create a local request handler:
Add a regular Controller/RestController with a RequestMapping in a file in Zuul proxy Server code
#RestController
public class LocalRequestHandler {
#GetMapping(path = "/status", produces = MediaType.TEXT_PLAIN_VALUE);
private static ResponseEntity<String> getServiceStatus() {
String status = "I am alive";
return ResponseEntity.ok().body(status);
}
}
2) Create a local forwarding route in the configuration file (application.properties or as the case may be) where other routes are defined. In my case, Spring Boot Zuul Proxy server is running on Jetty container with GatewaySvc as the application context.
zuul.routes.gatewaysvc.path=/GatewaySvc/status
zuul.routes.gatewaysvc.url=forward:/GatewaySvc/status
For a standalone Spring Boot, which I have not tested, you may have to remove the context path from the above configuration.
zuul.routes.gatewaysvc.path=/status
zuul.routes.gatewaysvc.url=forward:/status
Reference:
Strangulation Patterns and Local Forwards

Spring Cloud: default redirecting from Gateway to UI

I'm new to microservices and Spring Boot. I have a few Spring Cloud microservices with a Zuul gateway running on port 8080.
browser
|
|
gateway (:8080)
/ \
/ \
/ \
resource UI (:8090)
There is a UI microservice on port 8090, which has a controller with a method inside, returning index.html.
I configured Zuul routing like this for UI (I'm using Eureka too):
zuul:
routes:
ui:
path: /ui/**
serviceId: myproject.service.ui
stripPrefix: false
sensitiveHeaders:
If I call http://localhost:8080/ui/ everything works fine and I see rendering of my index.html.
Is it possible to configure Spring Cloud in some way to make the following flow work: calling http://localhost:8080/ redirects us to controller of UI microservice, which returns index.html?
So the idea is to open UI from the root of my site.
Thanks in advance!
Finally, I've made my code work! Thanks to #pan for mentioning Zuul Routing on Root Path question and #RzvRazvan for revealing about how Zuul routing works.
I've just added controller to Zuul routed Gateway microservice with one endpoint to redirect from root http://localhost:8080/ to http://localhost:8080/ui/:
#Controller
public class GateController {
#GetMapping(value = "/")
public String redirect() {
return "forward:/ui/";
}
}
Zuul properties for redirecting from Gateway microservice on port 8080 as http://localhost:8080/ui/ to UI microservice, which implemented as a separate Spring Boot application on port 8090 as http://localhost:8090/ui/:
zuul:
routes:
ui:
path: /ui/**
serviceId: myproject.service.ui
stripPrefix: false
sensitiveHeaders:
UI microservice's properties:
server:
port: 8090
servlet:
contextPath: /ui
Eventually, this call http://localhost:8080/ redirects us to controller of UI microservice, which returns view index.html:
#Controller
public class UiController {
#GetMapping(value = "/")
public String index() {
return "index.html";
}
}
Actually, I had another problem with rendering static content in such architecture, but it was connected with configuration of my front-end, which I develop using Vue.js framework. I will describe it here in a few sentences, in case it might be helpful for someone.
I have the following folders structure of UI microservice:
myproject.service.ui
└───src/main/resources
└───public
|───static
| ├───css
| └───js
└───index.html
All content of public folder is generated by npm run build task from webpack and vue.js. First time, I called my http://localhost:8080/ I got 200 OK for index.html and 404 for all other static resources, because they was called like this:
http:\\localhost:8080\static\js\some.js
So it was configured wrong public path for static assets in webpack. I changed it in config\index.js:
module.exports = {
...
build: {
...
assetsPublicPath: '/ui/',
...
}
...
}
And static assets became to be called properly. e.g.:
http:\\localhost:8080\ui\static\js\some.js
If you would like to have on Zuul the UI(front-end) you can add the static content in resources/static folder(html, css and js files). On this way your proxy is able to render the index.html (of course you must have an index.html in static folder). O this way on http://localhost:8080 the proxy will render index.html; also you can have another paths but all these path are managed by index.html)
About routing, the Zuul only redirect the http request. http://localhost:8080/ui/. On 8080 is running the proxy (Zuul) BUT /ui is the context path of resource server. Se when you make a call on this path http://localhost:8080/ui/ the proxy will redirect to resource server and will actually make a request to http://localhost:8090/ui/
It is a difference between browser path and http request path. The browser path is managed by index.html and the http request is managed by Zuul. I don't know if I was explicit enough.
One more thing... You can have the same path (/ui) on http request and index.html and when your browser will access the http://localhost:8080/ui/ a .js file with http request method will make an http request to http://localhost:8080/ui/ and then will be redirected to http://localhost:8090/ui/ and the response from the resource server will be rendered on the page from http://localhost:8080/ui/.

Spring Security Oauth2 SSO with Zuul Proxy

I'm modifying the oauth2-vanilla sample from Springs excellent security tutorials. The oauth2-vanilla combines the Zuul Proxy and the UI into a single application. I would like to seperate the Zuul Proxy and the UI. (The Zuul Proxy should act as an API gateway and as a reverse proxy for several UIs).
When accessing the UI via the zuul proxy, it should be able to do SSO based on Oauth2 between the UI and the resource backend.
The oauth2-vanilla looks like this
Where I want to move to something like this :
I've removed the UI part from the gateway, and added a zuul route for the ui
zuul:
routes:
resource:
url: http://localhost:9000
user:
url: http://localhost:9999/uaa/user
ui:
url: http://localhost:8080
I created a new UI webapp containing the UI (Angular stuff) with an #EnableOAuth2Sso annotation.
So I'm accessing the UI via http://localhost:8888 (through the zuul proxy).
After authenticating and doing through the UI flow, I can access the /user endpoint that returns me the user. (During debugging, I see that when I access the /user endpoint that I have an HTTP Session with an OAuth2Authentication.
When I access the /resource endpoint however, the HttpSessionSecurityContextRepository cannot find a session and is unable to build a context with the OAuth2Authentication.
I've created a git repository with the modified sample.
I'm guessing there is something wrong with the gateway configuration.
I've tried changing cookie paths, changing HttpSecurity rules in the proxy but I cannot get it to work.
What I don't understand is why the UI, when accessed through the proxy is able to resolve the /user endpoint fine (with an HTTP session and a OAuth2Authentication), but it is unable to access the /resource endpoint.
Also, as the UI is now running in the /ui context, it seems that I need to have the following code in the gateway for it to load up the angular css / js files.
.antMatchers("/ui/index.html", "/ui/home.html", "ui/css/**", "/ui/js/**").permitAll().anyRequest().authenticated();
It also doesn't seem right that I need to prefix it with the zuul ui route.
Any help would be appreciated.
I was never able to get the #EnableOauthSso to work. Instead, I annotated as an #EnableResourceServer and created a security config for Zuul.
#Configuration
#EnableResourceServer
public class JwtSecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.antMatchers("/**").hasAuthority("ROLE_API")
.and()
.csrf().disable();
}
}

Resources