How to let the zuul proxy handle the cors? - spring-boot

I have several microservices behind a microservice acting as zuul proxy.
When called from outside the domain the microservices return a "403- Invalid Cors".
This is solved by adding the following to each microservice:
#Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**"); //just for the sake of explaination i'm allowing everything
}
}
Anyway this is a bad design: I have a zuul api gateway after all! This should be done by that and the microservices should actually return that error when directly called.
So, I moved that same configuration only to my zuul microservice, here it is by the way:
#SpringBootApplication
#EnableSwagger2
#EnableZuulProxy
#ComponentScan(basePackages = "my.base.package")
public class MyApiGateway extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}
....
The props:
zuul:
prefix: /api
routes:
my-api:
path: /myApi/**
url: 'http: ...'
stripPrefix: true
Problem is that the microservice still returns "403 - Invalid Cors"! What am I doing wrong? I guess this happens because the zuul proxy actually does nothing except to forward the request...but how to configure it so to make the destination MS notice that the request comes from zuul, on the same network and so allowing it?

At least in the past Spring Cloud did not handle Zuul proxy via Spring MVC framework, so you cannot configure CORS for Zuul using WebMvcConfigurerAdapter, see: https://github.com/spring-cloud/spring-cloud-netflix/issues/888
You could try adding a CorsFilter bean to your Zuul API gateway, see: https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/web.html#mvc-cors-filter
Or you can add Spring Security to your API gateway which comes with CORS support already integrated, see: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

Related

spring cloud secured gateway ratelimiting not allowing permitall urls

I was trying to apply rate limiting on my spring cloud gateway which is secured through oauth2 and keycloak. the gateway sits in front of 3 microservices. each microservice exposes openapi3 config. in both gateway and microservices made the url to openapi3 config as public. but when i followed instruction to apply rate limiting using redis the public urls are not public anymore and getting 403 forbidden.
gateway security -->
#Configuration
#EnableWebFluxSecurity
public class WebFluxSecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity serverHttpSecurity) {
serverHttpSecurity.authorizeExchange(exchange -> exchange
.pathMatchers("/v3/api-docs/**",
"/employee/v3/api-docs/**",
"/department/v3/api-docs/**",
"/organization/v3/api-docs/**",
"/webjars/swagger-ui/**",
"/swagger-ui/**", "/swagger-ui.html").permitAll()
.anyExchange().authenticated())
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
serverHttpSecurity.csrf().disable();
serverHttpSecurity.formLogin().disable();
serverHttpSecurity.httpBasic().disable();
return serverHttpSecurity.build();
}
}
Rate limiting config -->
#Configuration
public class RateLimitingConfig {
/*
* NOTE: this stops all unauthenticated access :(
* need a way to allow public permitted urls from this. but how!
*/
#Bean
KeyResolver userKeyResolver() {
return exchange -> ReactiveSecurityContextHolder.getContext()
.map(ctx -> ctx.getAuthentication().getPrincipal().toString());
}
}
microservice security config -->
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//NOTE: this is to configure authorization
http.authorizeRequests(authorize -> authorize
.antMatchers("/v3/api-docs/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
http.csrf().disable();
http.formLogin().disable();
http.httpBasic().disable();
}
}
the rate limiting is working properly for authenticated user but my permit all methods are not permitted by the gateway anymore.
how can i enforce rate limiting for private urls and also expose public urls to unauthenticated users?
here is the full code base -
https://github.com/tareqmy/springcloudexamples

Spring Cloud Gateway + Keycloak CORS not working

I developed backend microservice application using Spring Boot and put API Gateway in front of microservices. To authenticate users I am using Keycloak.
Right now I am developing frontend application using Svelte, I configured my application.yml in gateway application like this:
spring:
cloud:
gateway:
default-filters:
- TokenRelay
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
add-to-simple-url-handler-mapping: true
However, when I am trying to send AJAX request I get the CORS error.
Also I Have spring security (through org.springframework.boot:spring-boot-starter-oauth2-client and org.springframework.boot:spring-boot-starter-oauth2-resource-server dependencies). I defined SecurityWebFilterChain as:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.cors().and()
.authorizeExchange()
.pathMatchers("/actuator/**")
.permitAll()
.and()
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.oauth2Login(); // to redirect to oauth2 login page.
return http.build();
}
When putting build of frontend in static folder there is no CORS error, but for development I need developer node.js server on localhost on different port.
So, how to fix this cors issue?
You can create a class to define the Cors mapping like this:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
The example above enables CORS requests from any origin to any endpoint in the application.
To lock this down a bit more, the registry.addMapping method returns a CorsRegistration object, which we can use for additional configuration. There’s also an allowedOrigins method that lets us specify an array of allowed origins. This can be useful if we need to load this array from an external source at runtime.
Additionally, there are also allowedMethods, allowedHeaders, exposedHeaders, maxAge and allowCredentials that we can use to set the response headers and customization options.
CORS With Spring Security:
If you use Spring Security in youproject, you must take an extra step to make sure it plays well with CORS. That's because CORS needs to be processed first. Otherwise, Spring Security will reject the request before it reaches Spring MVC.
Luckily, Spring Security provides an out-of-the-box solution:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}

Spring security login except whitelisted IP

I have a Spring boot application and added Spring security. I would like that some ip ranges by pass the security while all other requests need to log in before. I have extended the WebSecurityConfigurerAdapteras follows however it does not work as I would expect, The whitelisted IP can use the application without login while other requests get an HTTP 403 instead of getting the log in form. What I am doing wrong here?
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure (HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().access("hasIpAddress('192.168.1.0/24') or isAuthenticated()");
}
}

Spring Zuul Eureka Security Authentication get user info from Zuul

I'm using #EnableOAuth2Sso to authenticate a user with a third party authentication server on the Zuul server. I need to pass user info from Zuul to the routed servers. I've set up the request endpoint /userinfo to return a jsonified representation of a flattened version of the userinfo from the third party. How do I get this userinfo to one of the resource servers?
What I've tried so far:
I've tried making a request using the #LoadBalanced #Bean RestTemplate been. However, I get redirected to the third party for authorization. The sensitive-headers is set to none. I've checked which headers were going through:
["upgrade-insecure-requests","user-agent","accept","accept-language","cookie",
"authorization","x-forwarded-host","x-forwarded-proto",
"x-forwarded-prefix","x-forwarded-port","x-forwarded-for","accept-encoding",
"content-length", "host","connection"]
So, then I tried using #LoadBalanced #Bean OAuth2RestTemplate. I had to set the config security.basic.enabled=false to prevent the Authentication User Login Prompt from appearing. This produces UserRedirectRequiredException
Resource Server
#RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseEntity<String> test3() {
return restTemplate.getForEntity("http://zuul-server/userinfo", String.class);
}
Zuul Server
#RequestMapping(value = "/userinfo", produces = MediaType.APPLICATION_JSON_VALUE)
public User getInfo(OAuth2Authentication auth) {
return service.getUser(auth); // Returns User Object
}
Additional Notes
The Resource Server has not been annotated with #EnableResourceServer. If it was, a forwarded request will result in Invalid access token error message.
This is what I have working on our system for passing Oauth2 JWT tokens.
#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();
}
}
And the config portion you might need.
services:
path: /services/**
serviceId: services
stripPrefix: false
sensitiveHeaders: true
auth:
path: /oauth/**
serviceId: saapi-auth-server
stripPrefix: false
sensitiveHeaders: true
There was very little documentation on this. So it was really just hacking away at it until I could get it to pass tokens on.

Enable CORS for "/health" endpoint in Spring Boot Actuator

We want to enable CORS for all GET requests to the /health endpoint provided by Spring Boot Actuator.
We tried adding the following bean without success:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/health").allowedMethods("GET");
}
};
}
There are a few properties that can be used to enable CORS for the actuator's endpoints. For example, to allow GET requests from example.com in Spring Boot 1.x:
endpoints.cors.allowed-origins=http://example.com
endpoints.cors.allowed-methods=GET
and for Spring Boot 2:
management.endpoints.web.cors.allowed-origins=http://example.com
management.endpoints.web.cors.allowed-methods=GET
Spring boot 2:
You need to add the allow OPTIONS and allow headers as follows
management.endpoints.web.cors.allowed-origins=*
management.endpoints.web.cors.allowed-methods=OPTIONS, GET, POST
management.endpoints.web.cors.allowed-headers=*

Resources