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

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

Related

Configuring spring-boot-starter-oauth2-client to authenticate with Azure AD

I want to add Azure AD as an OAuth2 provider in Spring Boot 2.4. I followed Spring Boot's OAuth2 docs and came up with the following configuration:
spring.security.oauth2.client.provider.azuread.issuer-uri=https://login.microsoftonline.com/<tenant uuid>/v2.0
spring.security.oauth2.client.registration.azuread.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.azuread.client-id=<client uuid>
spring.security.oauth2.client.registration.azuread.client-name=Azure AD
spring.security.oauth2.client.registration.azuread.client-secret=<client secret>
spring.security.oauth2.client.registration.azuread.provider=azuread
spring.security.oauth2.client.registration.azuread.scope=openid
Just for completeness, this is my web security configuration:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests(a -> a
.antMatchers("/", "/login", "/error", "/webjars/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login();
// #formatter:on
}
}
When coming back from entering the credentials on https://login.microsoftonline.com, I get the following error:
[invalid_id_token] An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found.
The problem originates in DefaultJWTProcessor.java from Nimus-JOSE-JWT.
Looking through the requests in Firefox's network inspector, Spring Boot picks up the right URLs from the Issuer URI. I'm at a loss what's going wrong and appreciate any pointers.
Azure AD has some pretty unintuitive (in my opinion) default behaviour - I think this is what you are experiencing:
YOUR PROBLEM CAUSE (I THINK)
You are using standard OpenID Connect scopes
This causes Azure AD to issue an access token intended for the Graph API
This token fails standards based validation in Custom APIs since it is only designed for Graph APIs to use - it is recognisable by the nonce field in the JWT header
WHAT YOU NEED TO DO
Expose an API scope such as 'default'
Use the full value of this scope in your web client, with a value such as 'api://cb398b43-96e8-48e6-8e8e-b168d5816c0e/default', where the long identifier is that of the API
You will then get a normal OAuth token that Spring can validate - with no nonce field in the JWT header
FURTHER INFO
See steps 3 to 8 of my blog post from a couple of years ago
See the OpenID Connect settings of my web code sample
I had a similar problem and if I remember correctly there was an issue with scope. Don't know whether this is also your issue but in any case following configuration is working for me (note I'm using client credentials not auth code):
spring:
security:
oauth2:
client:
provider:
azure:
token-uri: https://login.microsoftonline.com/${custom.azure.account.tenant-id}/oauth2/token
registration:
azure:
client-id: ${custom.azure.service-principal.client-id}
client-secret: ${custom.azure.service-principal.client-secret}
authorization-grant-type: client_credentials
scope:
- https://graph.microsoft.com/.default
According to your description, it seems that you wanna use azure ad as the an OAuth2 provider in Spring Boot, and I found a doc said that 'Spring Starter for Azure Active Directory (AD) is now integrated with Spring Security 5.0'. It also offered a sample of springboot web app. I also tried it and it did work.
What I think need to note is, the tutorial said the redirect url setted in azure ad is 'http://localhost:8080/login/oauth2/code/', but when my tested, it proved to be 'http://localhost:8080/login/oauth2/code/azure'.
Upon your error, I'm not sure but maybe you can change your configuration of 'scope' to 'openid,profile,offline_access'.

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

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

Spring Oauth for Web Application

I have a query I am struggling to come to a decent answer with and hoping by vocalizing it someone might be able to guide me or advise me in the right direction.
All our current web applications are build using Spring, which includes the security which is handled by Spring Security module of course.
We are exploring the opportunities of integrating some of our new android projects into these web applications.
After some research and guidance, all flags point towards OAuth2 implementation in Android App and using that to obtain access to relevant parts of the web application server via Restfull calls.
What I am trying to understand now is can we and should we replace our existing Spring Security implementation in our web application and replace it with Spring Oauth2 equivalent.
The overall goal would to be able to have a single security solution that we would use for both website login, app login, and any API implementations that would be exposed to other web applications.
If anyone can also provide a link to a Spring Oauth2 Java Config (not-XML) setup where a user logs in and accesses a page based on their role in a unrestful manner, would also be extremely helpful. Most examples we have found were either extremely complex, focused solely on restfull calls, or only XML configurations.
Seems you did not hear about spring-cloud-security project which extremely simplifies working with oauth2 providers. Take a look at:
http://cloud.spring.io/spring-cloud-security/
There are samples available on github ( right side of above page ). It shows how to set up in case you want to use just one oauth2 provider - check project which shows how to do that for github https://github.com/spring-cloud-samples/sso . You do most of this stuff through configuration in application.yml file + add #EnableOAuth2Sso annotation so all machinery starts.
For 'normal' interaction with your web app its pretty straighforward. If you need your app to work as an api for others then you have to maintain tokens, add them to request etc. If client of your api is also your code which you can change then you may use OAuth2RestTemplate class which take care about tokens, so you do queries to your api as it was usual/not secured endpoint.
If you need to use for example two different providers for different paths then its more complicated, you have to configure them all like:
#Bean
public OAuth2RestTemplate facebookOAuth2RestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(facebookOAuth2ResourceDetails(), clientContext);
}
#Bean
public OAuth2ProtectedResourceDetails facebookOAuth2ResourceDetails() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(tokenUri);
resource.setId("id");
resource.setUserAuthorizationUri(authorizationUri);
resource.setUseCurrentUri(false);
resource.setPreEstablishedRedirectUri(redirectUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setTokenName("tokenname");
resource.setAuthenticationScheme(AuthenticationScheme.query);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
return resource;
}
and decide which instance of OAuth2RestTemplate to use in which case.
//update
If you want to exchange spring security core with authorizing users by some oauth2 provider you can extends OAuth2SsoConfigurerAdapter class:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/admin");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.and()
.logout().logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
}
So /admin will be now protected, and user redirected to any authorization server you specify. It requires oauth configuration in application.yml.
spring:
oauth2:
client: # Sauron
clientId: app_clientid
clientSecret: app_secret
accessTokenUri: http://authorizationserver/oauth/token
userAuthorizationUri: http://authorizationserver/oauth/authorize
clientAuthenticationScheme: header
resource:
tokenInfoUri: http://authorizationserver/oauth/check_token
preferTokenInfo: false
Thats why I wrote before that its easy to use just one auhorization server, but in case you need more then its more complicated.

Resources