Allow only acces by Zuul - spring-boot

I have several microservices, that I only want to access them by zuul resources server and avoid direct access.
Zuul server implements jwt authentication.
I have these redirections in application.yaml file
zuul:
routes:
servicio-carga-electores:
path: /cargaElectores/**
service-id: servicio-carga-electores
url: ${server-uri}/cargaElectoresWS
servicio-oauth-server:
service-id: servicio-oauth-server
path: /oauth/**
url: ${server-uri}/celec-oauth-server
sensitive-headers:
- Cookie
- Set-Cookie
ribbon:
eureka:
enabled: false
Trying with postman, after getting jwt token, all redirections causes 403 forbidden.
http://localhost:8080/celec-zuul-server/cargaElectores/recupera_ficheros
{
"timestamp": "2022-08-24T08:00:12.806+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/cargaElectoresWS/recupera_ficheros"
}
I've seen in other places using hasIpAdress, but isn't working.
#Configuration
#EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/estado.jsp").permitAll()
.and()
.authorizeRequests()
.antMatchers("/**").hasIpAddress("::1");
}
}
If I don't use oauth2, the redirection goes right, but I can access directly to the microservices.
Thanks for advance.
José Pascual

Related

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()...
}
}

How to have unauthenticated endpoints with Spring Boot Resource Server

I have a spring boot application annotated with #ResourceServer. I am trying to have unauthenticated endpoints as well as authenticated endpoints in my application, however all the endpoints return the same 401 response.
My SecurityConfig looks like this:
#Configuration
#EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/search").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
}
}
But when I send a POST request to /api/search I get this response:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I also tried having only anyRequest().permitAll() in my SecurityConfig, but it still returns the same response.
It was a pretty noobie mistake. I was actually configuring a WebSecurityConfigurerAdapter instead of a ResourceServerConfigurerAdapter.

How to config Spring #EnableOAuth2Sso App with self-signed SSL OAuth2 Server?

My app works well with normal non-SSL OAuth2 Servers. But I dont know how to config client to work with the SSL equivalents. I always get 401 when oauth2 server redirects back to http://localhost:8051/app1/login after success authentication and granting permissions. My App code likes below:
application.yml:
server:
port: 8051
servlet:
contextPath: /app1
security:
oauth2:
client:
clientId: sampleclient
clientSecret: samplepw
accessTokenUri: https://localhost:8668/sso-server/oauth/token
userAuthorizationUri: https://localhost:8668/sso-server/oauth/authorize
tokenName: oauth_token
resource:
userInfoUri: https://localhost:8688/api/me
Application.java
#SpringBootApplication
#EnableOAuth2Sso
public class App1 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login", "/error**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and().logout()
.logoutUrl("/logout")
.logoutSuccessUrl("https://localhost:8668/sso-server/logout")
.invalidateHttpSession(true)
.deleteCookies("client-session", "JSESSIONID")
.permitAll()
.and().csrf().disable()
;
}
public static void main(String[] args) {
SpringApplication.run(App1.class, args);
}
}
OAuth2 Servers work as expected and I can call all oauth APIs that clients need with https schema on Postman, so I think we have nothing to do with them and don't post the code here.

Spring Cloud Zuul API gateway doesn't forward JWT token for stateless sessions

I am trying to implement Microservices architecture backend using Spring Boot 1.5.6.RELEASE and Spring Cloud Dalston.SR3 that would be consumed by mobile/web endpoints.
API Gateway application
#SpringBootApplicatio
#EnableEurekaClient
#EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
API security
#Configuration
#EnableWebSecurity
#Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER)
#EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/sign-up", "/login")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.ignoringAntMatchers("/sign-up", "/login")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// #formatter:on
}
}
Gradle security related dependencies
// Spring OAuth2 security
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.security.oauth:spring-security-oauth2")
compile("org.springframework.cloud:spring-cloud-starter-oauth2")
compile("org.springframework.security:spring-security-jwt")
Zuul routes
zuul:
ignoredServices: '*'
routes:
user-service:
path: /user-service/**
stripPrefix: false
serviceId: user-webservice
sensitiveHeaders:
task-service:
path: /task-service/**
stripPrefix: false
serviceId: task-webservice
sensitiveHeaders:
user:
path: /userauth/**
stripPrefix: false
serviceId: auth-server
sensitiveHeaders:
I am able to get the access token from the authorization server(stateless sessions - no JSESSIONID cookie)
curl -D - --request POST -u acme:acmesecret
"http://localhost:8899/userauth/oauth/token?grant_type=password&username=<...>&password=<...>"
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDQ3ODg4NzgsInVzZXJfbmFtZSI6IjcyMTk2MTk2NDEiLCJhdXRob3JpdGllcyI6WyJST0xFX1BBVElFTlQiXSwianRpIjoiZThhMzBjNmQtZjA2MS00MWEzLWEyZGItYTZiN2ZjYTI5ODk1IiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIm9wZW5pZCJdfQ.AhF_kqfsRYM1t1HVT........
I can use the access token to request data from the authorization server or another resource
curl -D - --request GET -H "Authorization: Bearer
eyJhbGciOiJSUzI1...." http://localhost:8899/userauth/me
{"authorities":[{"authority":"ROLE_P.........}
curl -D - --request GET -H "Authorization: Bearer
eyJhbGciOiJSUzI1NiIsInR5......." http://localhost:8081/user-service/
[{"firstName":"Anil".....}]
However for the same requests routed through the API gateway, it fails at the Gateway itself and is filtered as AnonymousAuthenticationToken.
curl -D - --request GET -H "Authorization: Bearer
eyJhbGciOiJSUzI1...." http://localhost:8765/user-service/
HTTP/1.1 302 Set-Cookie:
XSRF-TOKEN=b5a1c34e-e83c-47ea-86a6-13a237c027d4; Path=/ Location:
http://localhost:8765/login
I was assuming that with #EnableZuulProxy and #EnableOAuth2Sso, Zuul would take care to forward the bearer token to the downstream services but that is not happening. I already have a working sample that uses HTTP session and browser redirection to get the API gateway to pass tokens - https://github.com/anilallewar/microservices-basics-spring-boot
But I am struggling to get it to work with Stateless sessions, any pointers what might be missing on the Zuul API gateway side?
Zuul considers Authorization header as a sensitive header by default and does not pass it to downstream requests. To override this, you can modify sensitiveHeaders in Zuul configuration either globally (for all routes):
zuul:
# exclude Authorization from sensitive headers
sensitiveHeaders: Cookie,Set-Cookie
ignoredServices: '*'
Or for a specific route:
zuul:
ignoredServices: '*'
routes:
user-service:
path: /user-service/**
stripPrefix: false
serviceId: user-webservice
# exclude Authorization from sensitive headers
sensitiveHeaders: Cookie,Set-Cookie
To find more about the problem, check this question:
Authorization header not passed by ZuulProxy starting with Brixton.RC1
I was assuming that with #EnableZuulProxy and #EnableOAuth2Sso, Zuul would take care to forward the bearer token to the downstream services but that is not happening.
I assumed the same thing, but in my (painful) experience, #EnableOAuth2Sso secures all endpoints with SSO and blocks even the requests with a Bearer token from getting to downstream services. I had to change my gateway to disable authentication on the routes that lead to my resources, so that the request with a Bearer token could get through.
Try adding /user-service/** and /task-service/** to your permitAll() matcher:
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/sign-up", "/login", "/task-service/**", "/user-service/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.ignoringAntMatchers("/sign-up", "/login")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// #formatter:on
}

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.

Resources