how to IP white list /oauth/check_token endpoint in spring security - spring-boot

I have two applications (war), one acting as a Resource Server and other is my Auth Server running on two different servers. I am using client_credentials grant_type. I need to white list the IP of my resource server so that nobody else can access the "/oauth/check_token" end point directly using post-man or browser or any other user-agent. Here is the code snippet from auth server :
#EnableAuthorizationServer
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isFullyAuthenticated()")
.allowFormAuthenticationForClients().realm(REALM + "/client");
}
...
some other configuration code related to ClientDetails and tokenStore.
...
}
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/oauth/check_token").access("isFullyAuthenticated() and hasIpAddress('0.0.0.0/0')").accessDecisionManager(accessDecisionManager)
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
I found a way to whitelist IP in spring :
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.anyRequest().access("hasIpAddress('0.0.0.0/0')");
}
But this is not helping in my situation because it is only checking for fullyAuthenticated(). I want all the resource server should be authenticated and authorized before accessing "/oauth/check_token"
Edited
I have referred to a similar kind of problem : How to find users' IPs in Spring Security? but it wasn't helping me because in that question they were trying to authorize a resource server Api, but in my case i want to authorize spring-security default endpoint i.e; /oauth/check_token
On further debuggin I found that if I remove .antMatchers(HttpMethod.GET,"/oauth/check_token").access("hasIpAddress('0.0.0.0/0')").accessDecisionManager(accessDecisionManager)
i am still able to use /oauth/check_token endpoint.
Then I tried to change fullyAuthenticated to permitAll() in AuthorizationServerSecurityConfigurer.checkTokenAccess("permittAll()"); it started allowing everybody to access check_token endpoint. Which implies it isn't even reading the configuration from .antMatchers(HttpMethod.GET,"/oauth/check_token")
Now I am confused how to configure hasIpAddress() and do we need to explicitly mention antMatcher("/oauth/check_token") or it is provided by default.

You can model it like this, which keeps both requirements - IP whitelisting and all requests are authenticated. You can remove the localhost access rule if not needed:
http
.authorizeRequests()
.antMatchers(GET, "/oauth/check_token")
.access("isFullyAuthenticated() and
(hasIpAddress('IP_OR_NETWORK_OF_RESOURCE_SERVER')
or hasIpAddress('127.0.0.1/32'))")
.antMatchers("/**").fullyAuthenticated();

Related

Spring security - Specific session creation policy per matchers

I'm trying to implement SessionCreationPolicy.ALWAYS for the /testMVCController/** endpoint and SessionCreationPolicy.STATELESS for rest of endpoints (/**).
Expected scenario:
When accessing to /testMVCController/displayUsers the user logs in once and the log I have implemented in UserDetailsService logs the authorities associated to that user.
After that, all the requests to /testMVCController/displayUsers or other URL under /testMVCController/** will not log the authorities again because the session creation policy is always and the user is already logged in.
This works when I don't specify the 2nd security configuration (X509ClientSessionCreationPolicyStateless) but when I add it, all the requests become session stateless.
It is not working with the current security configuration because after I log in with my client certificate, at any request executed under /testMVCController/** endpoint (e.g. /testMVCController/displayUsers), the authenticationUserDetailsService is consulted and the list of authorities is logged for each file request the browser makes (.js file, .css files, ...), even after the initial login.
So, if there are 3 requests (/testMVCController/displayUsers, displayUsers.js, displayUsers.css) the list of authorities log present in authenticationUserDetailsService is logged 3 times.
I configured SecurityConfiguration as shown below but it is not working:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class X509ClientSessionCreationPolicyAlways extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService x509CUDService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/testMVCController/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(x509CUDService)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
}
}
#Configuration
#Order(2)
public static class X509ClientSessionCreationPolicyStateless extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService X509CUDService ;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(X509CUDService);
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}
I've searched this issue and I found various links (e.g. Spring session creation policy per-request?, Spring Session: How to create separate session management policies for different URLs and Multiple HttpSecurity) but none of them worked.
Thanks in advance!
I was missing some details on my configuration. I was catching all the requests to /testMVCController/** and that was working, but in addition to catch the requests to any endpoint of the type /testMVCController/** (e.g.: /testMVCController/usersList), I also have to catch the requests that these pages make to get their scripts (.js files, .css files, .png files).
What was happening was: the request to /testMVCController/usersList), was configured with SessionCreationPolicy.ALWAYS, but the subsequent requests such as usersList.js, usersList.css, etc were configured with SessionCreationPolicy.STATELESS, and in these cases the X509CustomUserDetailsService was always consulted.
Example:
GET request to /testMVCController/usersList works, but there also requests in this usersList page to usersList.js, usersList.css, etc.
So, once I included these resource paths in the antMatchers all worked perfectly.

How do I use IPwhitelisting and OAuth2 in spring boot? [duplicate]

This question already has answers here:
Single role multiple IP addresses in Spring Security configuration
(2 answers)
Closed 1 year ago.
I am using OAuth2 in my spring boot application and I want to use IP whitelisting for range of IP addresses. i.e I want to give whitelisted user to access particular resource without providing token.
I have large number of whitelisted IPs and I am using Oauth2 token validation so that I have one resource server. I want to use IP whitelisting at first place and if it fails user should have valid token to have access to resource. Can you please give me any idea how can I do that.
In Spring Security you can configure your particular end point and whitelist using the method hasIpAddress in security config class.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").hasIpAddress("11.11.11.11")
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
If you have multiple IP addresses then you can use in this way
http
.authorizeRequests()
.antMatchers("/api/**").access(
"hasIpAddress('10.0.0.0/16') or hasIpAddress('127.0.0.1/32')")
For Resource Server you can do it in the #EnableResourceServer class & the same configure method you can setup Ipwhitelisting as shared below
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OAuthRequestedMatcher())
.anonymous().disable().authorizeRequests();
http
.authorizeRequests()
.antMatchers("/api/**").access("hasIpAddress('10.0.0.0/16') or hasIpAddress('127.0.0.1/32')");
}
}
Now since you have mentioned that you have many IP addresses you can make a list in the property file (application.properties) & at the application startup, you can loop through those to build the argument string that has to be passed in the access method.

While protecting the app with OAuth2, I'd like to expose some URLs accessible to anyone

After I created a small Spring Boot 2.2.6 application and I configured AWS Cognito as authentication provider, everything work well. When accessing any of application's URLs, I am redirected to Cognito and, after login, the application worked well.
I try to add some public pages (/api/**), which do not require any authentication.
First I tried this:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").permitAll(); // This should be permitted for anyone
but, now, everything is open. No security at all. Ooops.
I change it to:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").permitAll() // This should be permitted for anyone
.anyRequest().authenticated(); // Everything else should be protected
Now, the whitelisted URL (/api/**) work well, no password. But all other URL (eg. /private), instead of redirecting me to the login page, produce a 403 error:
There was an unexpected error (type=Forbidden, status=403). Access Denied
Does anybody have any idea how to keep the original behaviour (password) but with few URLs accessible anonymously?
The WebSecurityConfigurerAdapter has another method that can be used to ignore certain urls:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**");
}

Securing same endpoint with multiple configurations

We have a microservice architecture with securities for front to back with JWT, and back-to-back security with HTTP Basic.
Here is our configuration class for JWT :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers(endpointsProperties.getJwtWithWildcard())
.and()
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
}
jwtFilter is a simple filter that reads the Authorization header, and set the SecurityContextHolder.
And the HTTP Basic :
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
for (Map<String, String> userData : properties.getUsers()) {
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser(userData.get("login")).password(userData.get("password")).authorities(BASIC_AUTH_AUTHORITY);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers(endpoints.getBasicWithWildcard() )
.and().csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().httpBasic();
}
Those configuration class are used in differnets services, with distinct JWT and HTTP Auth endpoints. They are used either at the same time or independently. EndpointsProperties are loaded from application.yml.
But now, we have some referential microservices that we want to be reached either by other services or direclty by a (web) frontend application. We want to know if it is possible to secure a same URL ('/api/referential', for example) with the two different methods. Combining those configuration class with the same endpoints does not work, and it seems one configuration eraze the other.
Is it possible to secure a same URL path with different methods like that ? If yes, what do we need to change to enable it ?
Thanks for your support.
I think you can just add the two filters to the filter chain in this order
BasicAuthenticationFilter
JwtFilter
and make sure the ignoreFailure property of the BasicAuthenticationFilter is set to true.
This will make The basicAuthFilter authenticate requests with basicAuth and just continue down the filter chain if no basicAuth is sent - thus delegating to the JwtFilter.
then remove .httpBasic() from the WebsecurityConfig - as it will try to add another BasicSecurityFilter.
Just an off the cuff idea

Adding spring security to Zuul service in spring boot micro service

I am new to micro service architecture.
We have few services like zuul service (api gateway), security service (connect to db check access) and xyz service.
I would like to know, how we can add spring security to the routes defined in the zuul service. ?
On defining authorizeRequests like below on zuul service, it should call security service internally and authenticate the requests.
Example:
.authorizeRequests()
.antMatchers("/user/count").permitAll()
.antMatchers("/user/**").authenticated()
Note: /user End point is defined in the security service.
Please help me with this.
Our Zuul proxy is supporting OAuth2 security. An example of the security config is this:
#Configuration
#EnableResourceServer
public class JwtSecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.antMatchers("/**").hasAuthority("ROLE_API_ACCESS")
.and()
.csrf().disable();
}
}
I would assume if you are doing basic auth you could do something similar using the appropriate class. Maybe something like this.
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
BasicAuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint();
authenticationEntryPoint
.setRealmName("EnterpriseDeviceAuthentication");
http
.authorizeRequests()
.antMatchers("/health").permitAll() // allow access to health page
.antMatchers("/somepath").permitAll()
.antMatchers("/somepath2").permitAll()
.antMatchers("/bootstrap.min.css").permitAll()
.anyRequest().hasAnyAuthority(SecurityRoles.ALL_SECURITY_ROLES)
.and().httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf().disable();
}
}

Resources