Spring Security Oauth2 SSO with Zuul Proxy - spring

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

Related

Spring Boot Security and Keycloak - receive 403 forbidden /sso/login after successfull login when using HTTPS

I have a Spring Boot App using Spring Security and Keycloak as IDP.
Keycloak is available under https://auth.example.com, having a realm backo with a client backo-core and a testuser configured with the required roles.
If I configure my local app, using
keycloak.enabled=true
keycloak.auth-server-url=https://auth.example.com/auth
keycloak.realm=backo
keycloak.public-client=true
keycloak.resource=backo-core
keycloak.ssl-required=external
keycloak.confidential-port=443
Everything works fine.
If I deploy it to my DEV environment under https://backo.example.com/backo with https://backo.example.com/sso/login, when accessing the app I get redirect to the Keycloak login. After successful login I get redirected to the redirect URL with the state, session-state and code parameters but receive 403.
https://backo.example.com/sso/login?state=...&session_state=...&code=...
with request initiator chain coming from https://auth.example.com/auth/realms/backo/login-action/authenticate?...
If I set locally keycloak.ssl-required=all, the redirect_url parameter when accessing the login page is https://localhost/sso/login, so I can change it manually to http://localhost:8080/sso/login. After a successful login I have the exact same problem with a 403 Forbidden response for
http://localhost:8080/sso/login?state=...&session_state=...&code=...
with request initiator chain coming from https://auth.example.com/auth/realms/backo/login-action/authenticate?...
This is how my security config looks like
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/backo").hasAnyRole(ROLE_ADMIN, ROLE_BACKOFFICE)
.anyRequest().permitAll();
http.csrf().disable();
http.headers().frameOptions().disable(); // otherwise Vaadin doesn't work properly
http
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/");
}
You can ignore this Vaadin workaround, as it fails in /sso/login already.
http.csrf().disable(); is required to prevent other 403 Forbidden errors on post request while authorization.
One thing that could be worth mentioning:
The Spring Boot App has no HTTPS enabled, in our dev environment it sits behind an AWS Application loadbalancer ALB. HTTPS is terminated in the ALB.
We have faced the similar problem three days ago with spring boot thymeleaf application. We have used keycloak adapter (13.0.1) for spring boot.
After successful login it was redirecting to /sso/login page with 403 error.
After lots of effort we finally got the configuration required to fix this issue. you need to use the below application properties :
server.use-forward-headers=true
keycloak.public-client=true
keycloak.ssl-required=all
keycloak.confidential-port=443

JHipster - How to add route to external microservices in application.yml

I'm using Jhipster 5.5.0 to build a zuul gateway capable to route rest request to different microservices.
Some of this microservices are developed in different languages, deployed and running on different server. Every microservices is protected via OIDC using the same keycloak server, under different realms.
Now I need to configure zuul route on application.yml properties file of my gateway app, to access this service by external rest client (customers) and using zuul for filtering request and keycloak as oidc token provider.
Then I modify gateway application.yml adding the following zuul route to a sample external service (this type of configuration work well with another zuul gateway developed for another project without using jhipster):
# zuul routing:
zuul:
ignored-services: "*"
routes:
# external endpoints
myapi-v2-test:
path: /my-api/mypackage/v2/add
sensitiveHeaders: Cookie, Set-Cookie
url: http://192.168.3.148:8080/server/rest/api/mypackage_2.0.0/add
When I try to test the call using a soap-ui client with Auth Bearer token in header, provided by the keycloak server using the jhipster realm (and client_id "web_app"), I always receive the response error code 403 - Forbidden for path
"/my-api/mypackage/v2/add".
What is the right way to configure the application.yml of the gateway app?
Thank in advance for any help.
I'm not using registry service (e.g Spring Cloud Eureka or Jhipster Registry).
I post my solution in case someone have the same question. To solve my problem I added in OAuth2SsoConfiguration.java this line of code in configure(WebSecurity web) method:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.
.antMatchers("/my-api/**")
.
}
and the following in configure(HttpSecurity http):
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.
.
.and()
.
.antMatchers("/my-api/**").permitAll()
.
.
}

Spring boot admin: Full authentication is required to access this resource

we are using netflix oss for reverse proxying and security of microservices, we are following the jhipster pattern mentioned here https://www.jhipster.tech/microservices-architecture/, where request from UI application goes to gateway which is Api Gateway and it proxies the request to our backend microservices , we are using jwt for authentication, we wanted a dashboard to monitor our microservices and api gateway which registers with eureka server , we started a separate spring boot admin server so that it registers with eureka server and poll microservices and gateway for metrics endpoint but we are getting exception
Full authentication is required to access this resource
which is thrown by filters which are filtering for jwts at both api gateway and microservices level,
we also tried disabled
management.security.enabled: false
but still no luck ,can some one please help to guide what changes i need to make to enable spring boot admin to successfully poll the microservices and api gateway?
I tried the following approach
firstly i enabled web.ignoring().antMatchers("/actuator/**"), so that actuator endpoints are ignored by spring security but this approach will risk my api's
Second idea:
if i enable 2 filters in spring security , the first filter would be for spring boot admin with basic authentication for actuator endpoints and second filter will be of my jwt authentication for rest all api's and downstream api's not sure will it be feasible?
i enabled the 2 filters one filter for actuator end points and 1 filter for api's but these filters are working perfectly but not able to connect to SBA
public class SpringSecurityAdminFilter extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
String password = passwordEncoder().encode("xxxx");
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).withUser("sam").password(password).roles("ADMIN");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//We don't need sessions to be created.
}
}
i enabled basic authentication for spring boot admin server added the property in microservices
eureka.instance.metadata-map.user.name:
eureka.instance.metadata-map.user.password:
now actuator endpoints are protected by basic authentication
I have had a similar issue. On my spring boot application we had a cors filter to block Http Head requests. So head requests cannot be accepted from spring boot admin.
Check maybe filter is blocking HEAD Http requests .
Setting management.security.enabled=false in the application.properties also necessary.

Enabling Spring Boot Health Actuator with OAuth2 Authentication

We've seen a number of questions related to Spring Boot's Health Actuator endpoint for version 1.5+. Having gone through a number of them, we're still at a loss.
Our goals are:
To utilize Spring Boot/Spring Security's auto configuration of the security filter chaining (i.e. not have to fully implement/configure HttpSecurity)
To enable secured health access (to see a full view into the health information of the application)
To enable unsecured health access (to allow for an endpoint to function as a liveness probe in Kubernetes)
We've tried:
Using a WebSecurityConfigurerAdapter and configuring the WebSecurity object to ignore security for the /health endpoint, and then mapping a separate endpoint to the /health endpoint according to Routing multiple URLs to Spring Boot Actuator's health endpoint to hopefully enable a secured path.
Ensuring that security.oauth2.resource.filter-order=3 as was recommended in https://github.com/spring-projects/spring-boot/issues/5072. This puts the OAuth2AuthenticationProcessingFilter before the Actuator's Mvc endpoints, and allows for requests that contain pre-authenticated Authorization: Bearer ... headers (such as JWT authorizations) to be processed. However, it dictates that all requests contain authorization - otherwise, the FilterSecurityInterceptor triggers Secure object: FilterInvocation: URL: /health; Attributes: [#oauth2.throwOnError(authenticated)] and an AccessDeniedException
Utilizing Basic Authentication for /health and OAuth2 for everything else is a no-go (see Spring boot oauth2 management httpbasic authentication & https://github.com/spring-projects/spring-boot/issues/5072).
The question that we keep coming back to is how do we get:
Anonymous requests to the /health endpoint to function as unsecured
Pre-authenticated requests (i.e. those that contain pre-authenticated Authorization: Bearer ... headers) to the /health endpoint not having the appropriate authorizations or roles to function as unsecured
Pre-authenticated requests (i.e. those that contain pre-authenticated Authorization: Bearer ... headers) to the /health endpoint having the appropriate authorizations or roles to function as secured
We can easily allow any request to access /health by having something like:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers(HttpMethod.GET, "/health", "/info");
}
}
And that works great solely as a readiness/liveness probe. However, when the user is actually authenticated, it doesn't provide the benefit of seeing which backing services may be misbehaving.
Thanks in advance!
I faced something similar and this somewhat works:
Add a hacky access rule for actuators eg: #oauth2.clientHasRole('ROLE_CLIENT') or hasRole('ROLE_ANONYMOUS') that enables the security context to be populated for the actuator endpoints (for both authenticated and non authenticated requests) and tweak the 'sensitive' actuator endpoints configuration.
/health should return basic info for anonymous and full info for authenticated in this case, provided you enable the management security and mark it as non sensitive.
You still need to keep the filter configuration.
Try this, work for me
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers("/actuator/**","/assets/**")
.permitAll()
........
;
}

Use oauth2 scope instead of role to secure spring actuator management endpoints

I've upgraded to Spring Cloud Dalston recently, that means Spring Boot 1.5.1, and I can not secure the management endpoints by checking an oauth2 scope anymore. It worked before in Spring Cloud Camden.
This is the configuration that worked before :
#Configuration
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
#Value("${management.context-path}")
private String managementContextPath;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// some paths I don't want to secure at all
.antMatchers("/path1/**", "/path2/**").permitAll()
// access to health endpoint is open to anyone
.antMatchers(HttpMethod.GET, managementContextPath + "/health").permitAll()
// but app.admin scope is necessary for other management endpoints
.antMatchers(managementContextPath + "/**").access("#oauth2.hasScope('my-super-scope')") //
// And we make sure the user is authenticated for all the other cases
.anyRequest().authenticated();
}
}
And this is the important part of the config :
security:
oauth2:
client:
clientId: a-client
clientSecret: the-client-password
resource:
tokenInfoUri: http://my-spring-oauth2-provider/oauth/check_token
management:
context-path: /my-context
security:
enabled: true
endpoints:
health:
sensitive: false
When I try to POST on /my-context/refresh I get a HTTP 401 "Full authentication is needed" even though I give a valid OAuth2 token
Looking through the log I saw that my request was considered anonymous, and checking the FilterChainProxy log saw that the OAuth2AuthenticationProcessingFilter was not called. After a bit of digging I found that I could change the oauth2 resource filter order, so I tried that and now I have an OAuth2 authentication, yeah, finished right ?
Hum, no, now I have an Access is denied. User must have one of the these roles: ACTUATOR error.
I tried a few other things, including disabling management security (but my rules are not applied and access is open to everyone), playing with (ugh) #Order (no change), and even, lo and behold, reading and applying the documentation which says :
To override the application access rules add a #Bean of type
WebSecurityConfigurerAdapter and use
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) if you don’t want to
override the actuator access rules, or
#Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER) if you do
want to override the actuator access rules.
But this did not change the error : User must have one of these roles: ACTUATOR
Does anybody have a workaround/idea ?
Update : I'm also using Zuul, so I finally created a specific zuul route to the endpoint I needed (cloud bus refresh in my case), unprotected on an other backend service that was not exposed otherwise, and protected that zuul route with oauth2.
I'm leaving this question here nevertheless, if anyone finds a workaround, could be useful.
Probably being captain obvious, but see http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html. You can override the role with management.security.roles and simply add whatever role your Oauth2 credentials have.
I confronted with this issue also. The workaround that I used was to expose the actuator action on a new endpoint which I defined, and just call the actuator bean to handle request.
For example to secure /my-context/refresh with Oauth2 , I just expose a new resource at {whatever-api-prefix}/refreshConfig and I exposed a request handler on the rest controller for this URL; in the rest controller I wire the RefreshEndpoint bean and in the request handler I just call the refreshEndpoint.refresh().

Resources