Consul health check pass by Spring security filter - spring

I created Spring cloud application using consul as services discovery/registry.
I have configured my spring security as follow:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(myEntryPoint());
http
.authorizeRequests()
.antMatchers("/images/**").permitAll()
.antMatchers("/modules/**").permitAll()
.antMatchers("/vendor/**").permitAll()
.antMatchers("/views/**").permitAll()
.antMatchers("/index.html").permitAll()
.regexMatchers("/health").permitAll()// consul check health
.antMatchers("/api/**").authenticated()
.antMatchers("/**").permitAll();
http // login configuration
.addFilterAfter(springSecurityFilter(), BasicAuthenticationFilter.class);
http //logout configuration
.logout()
.logoutSuccessHandler(logoutHandler());
http.csrf().disable();
}
Normally, using this filter spring consul health check doesn't pass by this filter (public access).
But consul health check consul pass by filter.
If I use the following url I'm redirected to the authentication page:
https://localhost:8181/health

By default consul from spring-cloud-starter-consul-discovery use /actuator/health to check health.
When spring-security is used, should provide & permit that path.
Steps:
Permit /actuator/health in spring-security, then consul could perform the health check.
e.g
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/hello", "/authenticate", "/actuator/health")
.permitAll().
Add actuator dependency, if not yet.
Expose endpoints
e.g
management:
endpoints:
web:
exposure:
include: "*"

Related

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

securing jolokia actuator endpoint not working when accessing through hawt.io

I've got hawtio 2.1.0 installed connecting to the jolokia endpoint exposed by a spring boot 2.0.5 app.
My app yaml contains
management:
endpoints:
enabled-by-default: true
web:
exposure:
include: "jolokia"
jmx:
exposure:
exclude: "*"
endpoint:
jolokia:
enabled: true
config:
debug: true
In addition I have a filter
#Configuration
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))
.hasRole("ADMIN")
.requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))
.permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint())
.fullyAuthenticated()
.and().httpBasic();
}
When I access the jolokia actuator endpoint in my browser it correctly asks me for my credentials.
So using this method the endpoints are secured.
When I connect to the jolokia endpoint through the hawt.io web app I don't need to provide any credentials. It makes no differnence if hawt.io is running on a remote or on the local maschine the spring boot app is running on. Hawt.io is able to get all the MBean infos via jolokia.
How can that be?
Hawt.io somehow circumvents the securing of the jolokia actuator endpoint.
Any ideas why this is or how I can secure the jolokia actuator endpoint so that even hawt.io prompts for the credentials?
Thanks a lot in advance!
Cheers
Oliver

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 returns 401 for unsecured URL

Using Spring Security 4.0.3 from a Spring Boot 1.3.3 application.
The application has two types of HTTP contents : "API" a REST API and "UI" a web based used interface (Thymeleaf + Spring Web MVC).
Most endpoints of the REST API of the application are secured, using Basic, but some are not and should always be available.
The simplified configuration looks like:
// In one method, the security config for the "API"
httpSecurity
.antMatcher("/api/**")
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.authorizeRequests()
.antMatchers("/api/ping").permitAll()
.antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic();
// In another method, the security config for the "UI"
httpSecurity
.authorizeRequests()
.antMatchers("/ui/", "/ui/index.html", "/ui/css/*", "/ui/js/*", "/ui/img/*").permitAll()
.antMatchers("/ui/user/**").hasRole("USER")
.antMatchers("/ui/**").denyAll()
.and()
.formLogin().loginPage("/ui/login.html").permitAll().failureUrl("/ui/login.html").defaultSuccessUrl("/ui/user/main.html")
.and()
.logout().logoutUrl("/ui/logout").permitAll().logoutSuccessUrl("/ui/login.html")
.and()
.httpBasic();
Accesses to secured endpoints work as expected.
But accesses to public endpoints such as ".../api/ping" fail with a 401 when the user provided an invalid Basic authentication. Of course such endpoints works fine when no or valid Basic authentication is provided.
This 401 from Spring Security is surprising. How can we implement a Spring Security configuration that never returns any 401 or 403 for selected endpoints?
Thank you for your time.
Update 1 : added info about the "UI" existence and security config
Order is important. Most specific rule (path) first:
httpSecurity
.antMatchers("/api/ping").permitAll()
// and then the rest
This because if there is a match like on antMatcher("/api/**"), Spring Security will not evaluate later rules.

Spring boot actuator secure services does not work fine

I an Trying to secure spring actuator services /manage context path when calling for example:
http://localhost:9091/manage/metrics
with this config in my yalm.properties
management:
port: 9091
address: 127.0.0.1
context-path: /manage
security:
enabled: true
role: ADMIN.
Git branch with security actuator service layer
but access to every service is still free.
Spring security config:
'#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/pizzas","/info","/addPizza").hasAnyRole("USER","ADMIN").and().authorizeRequests().antMatchers("/users","/addUser").hasRole("ADMIN").and().authorizeRequests().antMatchers("/static/**","/logout","/login").permitAll();
http.formLogin().loginPage("/login").failureUrl("/login?error").permitAll();
http.logout().logoutSuccessUrl("/?logout").deleteCookies("remember-me").permitAll();
http.sessionManagement().maximumSessions(1).
expiredUrl("/?expired").maxSessionsPreventsLogin(true).and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
/**
* Configure global security with Bccyptenoncder and custom userDetailService with Spring Security
* #param auth
* #throws Exception
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder());
}
/**
* Bcrypt password encoding configuration, more info at http://www.baeldung.com/spring-security-registration-password-encoding-bcrypt
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
'
Spring boot team has resolved me this issue. I share the solution here:
Same Origin Policy
You cannot use the login page from your main Spring Application within actuator security. The reason is that the cookie is going to be associated with the domain + port + context path of the application. This is part of the Same Origin Policy
This means if you sent the user to localhost:9090/pizza/login and authenticated, when you visited localhost:9091/manage/ the JSESSIONID cookie would not be submitted to the management application which means you would not be seen as authenticated.
In order to authenticate across domains (i.e. different ports in this case) you would need some single sign on (OpenID, CAS, SAML, etc) mechanism.
Mapping a Login Page in the Management Application
In order to use this configuration you would need to setup a login page within the management application. To do this you would just need to return an HTML form when /login is requested. However, I'm not really certain how you would do that within the Boot management application. Perhaps #philwebb or #dsyer can elaborate on how one would do that.
Distinct Security Configuration for the Management Application
Alternatively you could create separate security configuration for the management application that allows authenticating with Basic Authentication. To do this you would create another Security Configuration that looks something like this:
#Order(0)
#Configuration
public class ManagementSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.requestMatchers(request -> "/manage".equals(request.getContextPath()))
.and()
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
This would make sure that if the context root is "/manage" that this security configuration is used. A few points of interest:
#Order(0) makes sure the configuration occurs before your other security configuration since by default any subclass of WebSecurityConfigurerAdapter will be ordered at 100. This is important because only the first WebSecurityConfigurerAdapter is used (similar to the authorizeRequests() matchers).
The request matcher is using a lambda for matching on the contextPath. I had thought there was a better way to distinguish Spring Boot application from the main application, but it does not appear that is the case. Perhaps #dsyer knows how this should be done.
NOTE
You can rewrite your configuration much more concisely as:
http
.authorizeRequests()
.antMatchers("/pizzas","/info","/addPizza").hasAnyRole("USER","ADMIN")
.antMatchers("/users","/addUser").hasRole("ADMIN")
.antMatchers("/static/**","/logout","/login").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/?logout")
.deleteCookies("remember-me")
.permitAll();
You might consider reading Spring Security Java Config Preview: Readability for details on how to format the configuration to better read it too.

Resources