Spring Security - Custom Authentication Provider and HTTP Basic for Actuator Endpoints - spring-boot

I´ve got a running Spring Boot Application (Spring Boot v2.4.1) and I would like to monitor it using Spring Boot Admin.
I have already setup the server and I can monitor the instance of my application with the /actuator/ endpoint not secured. I have a permitAll() on it.
Now I´d like to secure it, but I do not know how to do it without messing with my current Security Configuration.
I have Spring Security configured to match username and password from a DB and with a CustomAuthenticationProvider. If possible I would like to add a Actuator Endpoints with a HTTP Basic authentication.
This is my current security config:
http.
authorizeRequests()
.antMatchers("/admin/**").hasAuthority(AUTHORITY_ADMIN)
.antMatchers("/user/**").hasAnyAuthority(AUTHORITY_ADMIN, AUTHORITY_USER)
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.successHandler(new CustomUrlAuthenticationSuccessHandler(translator))
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.headers().frameOptions().sameOrigin();
I would like to keep that configuration and also tell spring that whenever a user hits /actuator/ endpoint, it will requiere HTTP Basic Security credentials.
I was thinking on having two #Configuration classes, extending WebSecurityConfigurerAdapter. One would be the one I´ve already got and the other one for the actuator endpoints. But I had no luck with it.
Thank you
Thank you very much

You can create two SecurityFilterChain beans, one for your /actuator/** endpoint with higher priority, and other to every other endpoint with lower priority, like so:
#Bean
#Order(1)
public SecurityFilterChain actuatorWebSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((matchers) -> matchers
.antMatchers("/actuator/**"));
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.httpBasic();
http.userDetailsService(myUserDetailsService);
...
return http.build();
}
#Bean
#Order(2)
public SecurityFilterChain defaultWebSecurity(HttpSecurity http) throws Exception {
// your current configuration
}
In this configuration, the #Order annotation tells the order that the SecurityFilterChains are gonna be matched against the requests.

This is how I solve it: I create a new #Configuraiton class extending WebSecurityConfigurerAdapter,
I was unable to stop using WebSecurityConfigurerAdapter (as suggested by #Marcus-Hert-da-Coregio in the comments) because if I do not extend it I was not able to define my custom AuthenticationProvider.
This class has #Order(1) so it would take precedence over my other initial configuration (which I set to #Order(2)). And this is it's content:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/actuator/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
Then my custom AuthenticationProvider will verify if the given credentials for accessing the actuator endpoints are valid.
Addittional information
The reason why this fails the first time I test it was because I was not setting the initial
.antMatcher("/actuator/**")
by adding it I was telling SpringSecurity that this configuration should only be applied to those endpoints. I get that notion from this article
I hope this helps someone in the future

Related

How does one use different session creation policies for UI and REST endpoints with Spring Security?

I have an application that contains both a UI and some REST endpoints. The UI uses SAML login (the old Spring Security SAML extension) and the REST endpoints using a custom authentication. The REST endpoints are only called by external applications.
For the REST endpoints ("/api/**") I have stated a stateless session creation policy and for the rest of the endpoint no session creation policy at all (I also tried with ALWAYS as in the below example).
Prior to some Spring Boot version, not sure which, this worked. Currently I'm using Spring Boot v.2.6.1. The UI endpoint got the authentication object from the Http session.
But now it doesn't work. The security context object cannot be found in the Http session using the default HttpSessionSecurityContextRepository implementation. It is saved but it can't be restored.
So is it possible to use two session creation policy, one for the REST and the other for the UI part, or should this be handled in a different way?
Now it seems that the stateless session creation policy is also used by the UI, which is not intended.
I'm using two WebSecurityConfigurerAdapter classes; one for the API and the other for the UI.
After a successful SAML login the redirect URL now contains the ";jsessionid=6051854D94A0771BB9B99FE573AA4DFD" parameter. Probably because of the stateless policy...?
protected void configure(HttpSecurity http) throws Exception {
List<AbstractAuthenticationProcessingFilter> authFilters = new ArrayList<>();
authFilters.add(new OAuthMacAuthenticationProcessingFilter(authenticationManager(), this.properties));
ApiAuthenticationProcessingFilter apiAuthenticationProcessingFilter = new ApiAuthenticationProcessingFilter(authenticationManager(),authFilters);
http
.csrf()
.disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterBefore(apiAuthenticationProcessingFilter, BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
and for the UI part
protected void configure(HttpSecurity http) throws Exception {
http.securityContext().securityContextRepository(customSessionSecurityContextRepository);
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);
var auth = http
.authorizeRequests()
.antMatchers("/saml/**").permitAll()
.antMatchers("/loggedout/**").permitAll()
.antMatchers("/error").permitAll();
auth
.anyRequest()
.authenticated();
http.csrf().disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.headers().frameOptions().sameOrigin();
http.exceptionHandling().accessDeniedHandler(this.accessDeniedHandler());
http
.logout()
.disable(); // The logout procedure is already handled by SAML filters.
}
I'll answer this myself. The above code does actually work. The problem was on the remote end, the IDP I was using had some problems that day that resulted in that it didn't work as expected. The day after, it worked.

Spring security protect logout url

I am using spring security in my project. I configure secured endpoints, using #PreAuthorize annotation. I want spring security to protect the logout URL, which I specified in the config file.
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(LOGOUT_URL).authenticated()
.anyRequest().permitAll()
.and()
.logout()
.logoutUrl(LOGOUT_URL)
.invalidateHttpSession(true)
.logoutSuccessHandler(logoutSuccessHandler);
}
Here I permit any request, except logout. I guess that this configuration is only for #RestController endpoints. But how to configure it to forbid not authenticated users to use logout.

Spring Security custom authentication provider's authenticate() method is not working

I followed the article Using Custom Authentication Provider Spring Security adding a custom authentication provider in Spring Security.
I found that if I POST to /login, it is redirected to /login.
My custom Authentication Provider's authenticate() is not called.
Even if I use Spring Security's TestingAuthenticationProvider, POST to /login still get redirected to /login.
Is there some thing wrong with my WebSecurityConfigure? This is my configure.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
I use Spring Boot. Do I need to modify the application.properties file? Is there any working sample project on using custom authentication provider?
Have you tried using
http.authorizeRequests()
.antMatchers("/", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
in your WebSecurityConfigure?
You will get a generated login-window that is using your CustomAuthenticationProvider as soon as you are trying to call a page you have not included in
.antMatchers("/", "/webjars/**").permitAll().

How to create a spring boot app with ssl.enable=true and a non-secure "/health" end point

Is it possible to configure spring boot application ( Jetty ) to have at least one non-secure (non https) endpoint for a load balancer to perform health checks but have all other requests be forced to be secure?
When setting the property:
server.ssl.enabled=true
requests for all ports (both regular port and management/actuator port) are forced to be https.
Secure requests URLS must have the server name in the URL match the certificate configured. A load balancer or container manager like kubernetes would have to access each node in a pool of servers with some kind of host name to server mapping.
Initially I thought that the setting management.ssl.enable=false would do the trick but it doesn't appear to be the case. What I wound up doing that worked for me was to add an ssl exclusion rule for just the /health endpoint.
Here is an abridged version of my SecurityConfiguration which is a #Configuration annotated class that extends/implements WebSecurityConfigurerAdapter/WebSecurityConfigurer.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/secure-path").hasAuthority("SOME_ROLE")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.permitAll()
.and()
.exceptionHandling();
if (securityProperties.isRequireSsl()) {
//allow health checks to be over http
http.requiresChannel().antMatchers("/health").requiresInsecure();
http.requiresChannel().anyRequest().requiresSecure();
}
}
making use of the requiresInsecure() for the /health endpoint was the key. Note, the order is important, generally in Spring Security more specific rules should come first.
The Spring Boot 2 property for disabling the management server TLS is:
management.server.ssl.enabled=false

spring security and VAADIN

I am developing my spring boot app which is protected by spring security. Here is part of secured config:
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// .csrf().ignoringAntMatchers("/dashboard")
// .and()
.httpBasic()
.and()
.headers().frameOptions().disable()
.and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/VAADIN/**", "/PUSH/**", "/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority(Authority.Type.ROLE_ADMIN.getName())
.antMatchers("/", "/login**", "/index.html", "/home.html").permitAll()
.and()
.logout().logoutSuccessUrl("/").permitAll()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// #formatter:on
}
I am going to implement some admin dashboard to manage my app using VAADIN.
I have read that "Disable CSRF-protection in either Spring or Vaadin. If you have both turned on, your application will not work.".
In my case I need to disable CSRF-protection in Vaadin, but I could not find how can I do it using Java config.
For this moment I am getting: https://127.0.0.1:8443/vaadinServlet/UIDL/?v-wsver=7.5.5&v-uiId=0 "Communication error: UIDL could not be read from server. Check servlets mappings. Error code: 403", during navigation from the main view to other views. (e.g: /dashboard#!myview). This because AccessDeniedHandlerImpl handle method is invoked. I have try to fix this using following statements but it doesn't help:
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
So, please help me to solve this two issues:
Disable CSRF in VAADIN using java config.
Solve problem with view navigation.
Thanks
To fix the above issues, I have decided to divide my project into two modules. First is API app, which has own implemented security configuration. Second is Dashboard, which has both Spring Security integrated with Vaadin4Spring based on this sample.

Resources