Securing Spring Boot Admin with Spring Security

Securing Spring Boot Admin with Spring Security thumbnail
30K
By Dhiraj 08 July, 2020

In this article, we will be discussing adding spring security to the spring boot admin server console and discuss different ways such as basic authentication and JWT authentication to secure our admin console. We will also look into accessing the secured Actuator endpoint exposed by the spring boot admin client.

This is the 3rd article on a series of tutorials to monitor and manage our microservice-based spring boot apps. First, we discussed about spring boot actuator and then spring boot admin in great detail and now we are discussing securing the spring boot admin console.

Quick Intro to Spring Boot Admin

Spring Boot Admin is a web app developed by codecentric which provides an admin console for the managing and monitoring of spring boot applications by utilizing the actuator REST endpoints exposed by spring boot actuator.

From the spring boot admin perspective, there are 2 modules - admin server and admin client. The admin server provides an admin console to visualize all the metrics and status of a client app. The client app here is any spring boot app that has a spring boot actuator integrated.

With this admin console, we can perform multiple admin-level operations on the client app hence securing the admin console is the most important task. Even if the admin console is hosted on a private network it is recommended to have a basic authentication added.

For this article, we assume that you already have the local setup of admin server and admin client that we discussed in our last article. In this article, we will add security on top of that.

Also, we added Netflix discovery server to enable discovery of our client app by the admin server app.

Adding Basic Authentication to Admin Console

Adding a basic authentication to an admin console only requires some entries in our properties file and it is very similar to adding a spring security basic authentication in a spring boot which we have already discussed in detail in my previous article here.

Basic authentication is a standard HTTP header with the user and password encoded in base64 in the format username:password and sent in HTTP header as Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==.

To add it in our admin console, we need to include below maven dependencies in our existing admin server app pom file. The spring-boot-admin-server-ui artifact provides a login page and a logout button.

<dependency>
	<groupId>de.codecentric</groupId>
	<artifactId>spring-boot-admin-server-ui</artifactId>
	<version>2.2.3</version>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

With this much configuration, we have basic authentication enabled in our admin console. Now, let us add our username and password required for login. For that, we can make below entries in the admin server properties file.

spring.security.user.name=devglan
spring.security.user.password=password

Now, if we try to access our admin server console you should ideally see a login page like this.

spring-boot-admin-login

No extra configuration is required to add a basic authentication in our admin server app to enable basic authentication. As we are doing the client app discovery via Netflix Eureka no extra configuration required in our client app too and since our actuator endpoints are not secured yet the admin server can directly access those actuator endpoints and show the metrics in the console.

But in case you do not wish use any discovery server for client app discovery then make below entries in admin client app to register itself to admin server app as our admin server app is now secured with basic authentication.

spring.boot.admin.client.username=devglan
spring.boot.admin.client.password=password

This is a very simple setup and good for a poc. Now for a production system a Spring Security configuration of our server could look like this as per the official doc of spring boot admin.

@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

  private final AdminServerProperties adminServer;

  public SecuritySecureConfig(AdminServerProperties adminServer) {
    this.adminServer = adminServer;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    successHandler.setTargetUrlParameter("redirectTo");
    successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

    http.authorizeRequests(
        (authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**")).permitAll() 
            .antMatchers(this.adminServer.path("/login")).permitAll().anyRequest().authenticated() 
    ).formLogin(
        (formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and() 
    ).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout"))).httpBasic(Customizer.withDefaults()) 
        .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) 
            .ignoringRequestMatchers(
                new AntPathRequestMatcher(this.adminServer.path("/instances"),
                    HttpMethod.POST.toString()), 
                new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
                    HttpMethod.DELETE.toString()), 
                new AntPathRequestMatcher(this.adminServer.path("/actuator/**")) 
            ))
        .rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
  }

  // Required to provide UserDetailsService for "remember functionality"
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER");
  }

}

Above configuration is enough to enable basic authentication but we can replace this configuration to use other authentication and authorization mechanism such as JWT token based auth or Oauth2 or session based authentication. You can follow my previous article to integrate JWT token based authentication or integrate it with OAuth2.

Securing Spring Boot Admin Client

Spring Boot admin client is a full fledged spring boot app with spring actuator integrated with it. Hence, there are many ways to secure these apps. Also, we can write our custom security configurations to secure our actuator endpoints.

If you specifically want to know about securing actuator endpoints then you can follow my another article where we have discussed about securing actuator endpoints.

For this article, let us use basic authentication to secure our admin client app. For that let us add below maven dependency in our admin client app and add some properties to enable basic authentication.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
application.properties
spring.security.user.name=client
spring.security.user.password=client

Once basic auth is enabled in the admin client app, the actuator endpoints become secured and hence require authentication for the access of these endpoints. For example, if you try to access the /beans endpoint - http://localhost:8080/actuator/beans you need to produce above-configured username and password - client/client.

And the same authentication is required by the admin server app to access the actuator endpoints now. Hence, below entries are required to add in the admin client properties.

This works in a manner that these credentials are submitted by the admin client app in the metadata to server app when registering the application.

spring.boot.admin.client.instance.metadata.user.name=${spring.security.user.name}
spring.boot.admin.client.instance.metadata.user.password=${spring.security.user.password}

The BasicAuthHttpHeaderProvider then uses this metadata to add the Authorization header to access admin client application actuator endpoints. We can also provide your own HttpHeadersProvider to alter the behaviour or add extra headers.

This HttpHeadersProvider can be used to inject custom JWT tokens in the headers.

    @Bean
    public HttpHeadersProvider injectHeaders(){
        return instance -> {
            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", "Bearer abc");
            return headers;
        };
    }

Above configuration works fine when the admin client app directly registers itself to the admin server app but in our case we have discovery server for service discovery. Hence, below configuration in application.yml file is required to access the actuator endpoints by the admin server app. These entries are made in admin client app.

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true
    registry-fetch-interval-seconds: 5
  instance:
    preferIpAddress: true
    metadata-map:
      user.name: client
      user.password: client

Conclusion

In this article, we added spring security to the spring boot admin server console and discuss different ways such as basic authentication and JWT authentication to secure our admin console.

Share

If You Appreciate This, You Can Consider:

We are thankful for your never ending support.

About The Author

author-image
A technology savvy professional with an exceptional capacity to analyze, solve problems and multi-task. Technical expertise in highly scalable distributed systems, self-healing systems, and service-oriented architecture. Technical Skills: Java/J2EE, Spring, Hibernate, Reactive Programming, Microservices, Hystrix, Rest APIs, Java 8, Kafka, Kibana, Elasticsearch, etc.

Further Reading on Spring Security