Fix to many request filter usage in DDI (#526)
* Fix DOS filter usage in DDI Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Add optional CSP definition. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix for empty case. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * readability and ensure that manual enforcement is also possible in timeforced active but no hit yet. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Class order to bean order. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix exception propagation. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.autoconfigure.security;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
@@ -80,6 +82,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationEn
|
|||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.vaadin.spring.security.VaadinSecurityContext;
|
import org.vaadin.spring.security.VaadinSecurityContext;
|
||||||
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
|
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
|
||||||
import org.vaadin.spring.security.web.VaadinDefaultRedirectStrategy;
|
import org.vaadin.spring.security.web.VaadinDefaultRedirectStrategy;
|
||||||
@@ -99,7 +102,7 @@ public class SecurityManagedConfiguration {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
|
||||||
|
|
||||||
private static final int DOS_FILTER_ORDER = 1;
|
private static final int DOS_FILTER_ORDER = -200;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationConfiguration configuration;
|
private AuthenticationConfiguration configuration;
|
||||||
@@ -136,6 +139,8 @@ public class SecurityManagedConfiguration {
|
|||||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||||
static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
private static final String DDI_ANT_MATCHER = "/{tenant}/controller/**";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ControllerManagement controllerManagement;
|
private ControllerManagement controllerManagement;
|
||||||
|
|
||||||
@@ -165,12 +170,10 @@ public class SecurityManagedConfiguration {
|
|||||||
* of service protection filter in the filter chain
|
* of service protection filter in the filter chain
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
|
||||||
public FilterRegistrationBean dosDDiFilter(final HawkbitSecurityProperties securityProperties) {
|
public FilterRegistrationBean dosDDiFilter(final HawkbitSecurityProperties securityProperties) {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = dosFilter(securityProperties.getDos().getFilter(),
|
final FilterRegistrationBean filterRegBean = dosFilter(Arrays.asList(DDI_ANT_MATCHER),
|
||||||
securityProperties.getClients());
|
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||||
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*");
|
|
||||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||||
filterRegBean.setName("dosDDiFilter");
|
filterRegBean.setName("dosDDiFilter");
|
||||||
|
|
||||||
@@ -224,13 +227,13 @@ public class SecurityManagedConfiguration {
|
|||||||
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
|
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
|
||||||
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
|
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
|
||||||
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||||
httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and()
|
httpSec.requestMatchers().antMatchers(DDI_ANT_MATCHER).and().securityContext().disable().anonymous()
|
||||||
.securityContext().disable().anonymous().authenticationFilter(anoymousFilter);
|
.authenticationFilter(anoymousFilter);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
|
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
|
||||||
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
|
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
|
||||||
.antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest()
|
.antMatcher(DDI_ANT_MATCHER).anonymous().disable().authorizeRequests().anyRequest()
|
||||||
.authenticated().and().exceptionHandling()
|
.authenticated().and().exceptionHandling()
|
||||||
.authenticationEntryPoint((request, response, authException) -> response
|
.authenticationEntryPoint((request, response, authException) -> response
|
||||||
.setStatus(HttpStatus.UNAUTHORIZED.value()))
|
.setStatus(HttpStatus.UNAUTHORIZED.value()))
|
||||||
@@ -259,8 +262,8 @@ public class SecurityManagedConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public FilterRegistrationBean dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
|
public FilterRegistrationBean dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = dosFilter(securityProperties.getDos().getFilter(),
|
final FilterRegistrationBean filterRegBean = dosFilter(Collections.emptyList(),
|
||||||
securityProperties.getClients());
|
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||||
filterRegBean.setUrlPatterns(Arrays.asList("/system/*"));
|
filterRegBean.setUrlPatterns(Arrays.asList("/system/*"));
|
||||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||||
filterRegBean.setName("dosSystemFilter");
|
filterRegBean.setName("dosSystemFilter");
|
||||||
@@ -268,39 +271,54 @@ public class SecurityManagedConfiguration {
|
|||||||
return filterRegBean;
|
return filterRegBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FilterRegistrationBean dosFilter(final HawkbitSecurityProperties.Dos.Filter filterProperties,
|
private static FilterRegistrationBean dosFilter(final Collection<String> includeAntPaths,
|
||||||
|
final HawkbitSecurityProperties.Dos.Filter filterProperties,
|
||||||
final HawkbitSecurityProperties.Clients clientProperties) {
|
final HawkbitSecurityProperties.Clients clientProperties) {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
|
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
|
||||||
|
|
||||||
filterRegBean.setFilter(new DosFilter(filterProperties.getMaxRead(), filterProperties.getMaxWrite(),
|
filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(),
|
||||||
filterProperties.getWhitelist(), clientProperties.getBlacklist(),
|
filterProperties.getMaxWrite(), filterProperties.getWhitelist(), clientProperties.getBlacklist(),
|
||||||
clientProperties.getRemoteIpHeader()));
|
clientProperties.getRemoteIpHeader()));
|
||||||
|
|
||||||
return filterRegBean;
|
return filterRegBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter registration bean for spring etag filter.
|
* A Websecruity config to handle and filter the download ids.
|
||||||
*
|
|
||||||
* @return the spring filter registration bean for registering an etag
|
|
||||||
* filter in the filter chain
|
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Configuration
|
||||||
@Order(100)
|
@EnableWebSecurity
|
||||||
public FilterRegistrationBean eTagFilter() {
|
@Order(320)
|
||||||
|
@ConditionalOnClass(MgmtApiConfiguration.class)
|
||||||
|
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
|
@Autowired
|
||||||
// Exclude the URLs for downloading artifacts, so no eTag is generated
|
private DdiSecurityProperties ddiSecurityConfiguration;
|
||||||
// in the ShallowEtagHeaderFilter, just using the SH1 hash of the
|
|
||||||
// artifact itself as 'ETag', because otherwise the file will be copied
|
|
||||||
// in memory!
|
|
||||||
filterRegBean.setFilter(
|
|
||||||
new ExcludePathAwareShallowETagFilter("/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
|
|
||||||
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
|
|
||||||
"/api/v1/downloadserver/**"));
|
|
||||||
|
|
||||||
return filterRegBean;
|
@Autowired
|
||||||
|
private DownloadIdCache downloadIdCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(final HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
|
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
|
||||||
|
downloadIdCache);
|
||||||
|
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
|
||||||
|
|
||||||
|
http.csrf().disable();
|
||||||
|
http.anonymous().disable();
|
||||||
|
|
||||||
|
http.regexMatcher(HttpDownloadAuthenticationFilter.REQUEST_ID_REGEX_PATTERN)
|
||||||
|
.addFilterBefore(downloadIdAuthenticationFilter, FilterSecurityInterceptor.class);
|
||||||
|
http.authorizeRequests().anyRequest().authenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
|
||||||
|
ddiSecurityConfiguration.getRp().getTrustedIPs()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,7 +354,7 @@ public class SecurityManagedConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public FilterRegistrationBean dosMgmtFilter(final HawkbitSecurityProperties securityProperties) {
|
public FilterRegistrationBean dosMgmtFilter(final HawkbitSecurityProperties securityProperties) {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = dosFilter(securityProperties.getDos().getFilter(),
|
final FilterRegistrationBean filterRegBean = dosFilter(null, securityProperties.getDos().getFilter(),
|
||||||
securityProperties.getClients());
|
securityProperties.getClients());
|
||||||
filterRegBean.setUrlPatterns(Arrays.asList("/rest/*", "/api/*"));
|
filterRegBean.setUrlPatterns(Arrays.asList("/rest/*", "/api/*"));
|
||||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||||
@@ -384,6 +402,29 @@ public class SecurityManagedConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter registration bean for spring etag filter.
|
||||||
|
*
|
||||||
|
* @return the spring filter registration bean for registering an etag
|
||||||
|
* filter in the filter chain
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Order(380)
|
||||||
|
public FilterRegistrationBean eTagFilter() {
|
||||||
|
|
||||||
|
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
|
||||||
|
// Exclude the URLs for downloading artifacts, so no eTag is generated
|
||||||
|
// in the ShallowEtagHeaderFilter, just using the SH1 hash of the
|
||||||
|
// artifact itself as 'ETag', because otherwise the file will be copied
|
||||||
|
// in memory!
|
||||||
|
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter("/UI/**",
|
||||||
|
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
|
||||||
|
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
|
||||||
|
"/api/v1/downloadserver/**"));
|
||||||
|
|
||||||
|
return filterRegBean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link WebSecurityConfigurer} for external (management) access.
|
* {@link WebSecurityConfigurer} for external (management) access.
|
||||||
*/
|
*/
|
||||||
@@ -399,6 +440,9 @@ public class SecurityManagedConfiguration {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SecurityProperties springSecurityProperties;
|
private SecurityProperties springSecurityProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HawkbitSecurityProperties hawkbitSecurityProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter to protect the hawkBit management UI against to many requests.
|
* Filter to protect the hawkBit management UI against to many requests.
|
||||||
*
|
*
|
||||||
@@ -411,7 +455,7 @@ public class SecurityManagedConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public FilterRegistrationBean dosMgmtUiFilter(final HawkbitSecurityProperties securityProperties) {
|
public FilterRegistrationBean dosMgmtUiFilter(final HawkbitSecurityProperties securityProperties) {
|
||||||
|
|
||||||
final FilterRegistrationBean filterRegBean = dosFilter(securityProperties.getDos().getUiFilter(),
|
final FilterRegistrationBean filterRegBean = dosFilter(null, securityProperties.getDos().getUiFilter(),
|
||||||
securityProperties.getClients());
|
securityProperties.getClients());
|
||||||
// All URLs that can be called anonymous
|
// All URLs that can be called anonymous
|
||||||
filterRegBean.setUrlPatterns(Arrays.asList("/UI/login", "/UI/login/*", "/UI/logout", "/UI/logout/*"));
|
filterRegBean.setUrlPatterns(Arrays.asList("/UI/login", "/UI/login/*", "/UI/logout", "/UI/logout/*"));
|
||||||
@@ -490,6 +534,10 @@ public class SecurityManagedConfiguration {
|
|||||||
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **\\n******************\"");
|
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **\\n******************\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(hawkbitSecurityProperties.getContentSecurityPolicy())) {
|
||||||
|
httpSec.headers().contentSecurityPolicy(hawkbitSecurityProperties.getContentSecurityPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
httpSec
|
httpSec
|
||||||
// UI
|
// UI
|
||||||
.authorizeRequests().antMatchers("/UI/login/**").permitAll().antMatchers("/UI/UIDL/**").permitAll()
|
.authorizeRequests().antMatchers("/UI/login/**").permitAll().antMatchers("/UI/UIDL/**").permitAll()
|
||||||
@@ -501,46 +549,11 @@ public class SecurityManagedConfiguration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(final WebSecurity webSecurity) throws Exception {
|
public void configure(final WebSecurity webSecurity) throws Exception {
|
||||||
|
// Not security for static content
|
||||||
webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
|
webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A Websecruity config to handle and filter the download ids.
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
@Order(200)
|
|
||||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
|
||||||
public static class IdRestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DdiSecurityProperties ddiSecurityConfiguration;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DownloadIdCache downloadIdCache;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure(final HttpSecurity http) throws Exception {
|
|
||||||
|
|
||||||
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
|
|
||||||
downloadIdCache);
|
|
||||||
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
|
|
||||||
|
|
||||||
http.csrf().disable();
|
|
||||||
http.anonymous().disable();
|
|
||||||
|
|
||||||
http.regexMatcher(HttpDownloadAuthenticationFilter.REQUEST_ID_REGEX_PATTERN)
|
|
||||||
.addFilterBefore(downloadIdAuthenticationFilter, FilterSecurityInterceptor.class);
|
|
||||||
http.authorizeRequests().anyRequest().authenticated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
|
|
||||||
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
|
|
||||||
ddiSecurityConfiguration.getRp().getTrustedIPs()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ security.basic.realm=HawkBit
|
|||||||
security.user.name=admin
|
security.user.name=admin
|
||||||
security.user.password=admin
|
security.user.password=admin
|
||||||
|
|
||||||
# Ensure that DosFilter runs before Spring Security
|
|
||||||
security.filter-order=5
|
|
||||||
|
|
||||||
# Spring cloud bus and stream
|
# Spring cloud bus and stream
|
||||||
spring.cloud.bus.enabled=false
|
spring.cloud.bus.enabled=false
|
||||||
# Disable Cloud Bus default events
|
# Disable Cloud Bus default events
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
final JpaAction action = actionRepository.findById(actionId)
|
final JpaAction action = actionRepository.findById(actionId)
|
||||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||||
|
|
||||||
if (!action.isForced()) {
|
if (!action.isForce()) {
|
||||||
action.setActionType(ActionType.FORCED);
|
action.setActionType(ActionType.FORCED);
|
||||||
return actionRepository.save(action);
|
return actionRepository.save(action);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
|
|||||||
|
|
||||||
protected DefaultMockMvcBuilder createMvcWebAppContext() {
|
protected DefaultMockMvcBuilder createMvcWebAppContext() {
|
||||||
return MockMvcBuilders.webAppContextSetup(context)
|
return MockMvcBuilders.webAppContextSetup(context)
|
||||||
.addFilter(new DosFilter(100, 10, "127\\.0\\.0\\.1|\\[0:0:0:0:0:0:0:1\\]", "(^192\\.168\\.)",
|
.addFilter(new DosFilter(null, 100, 10, "127\\.0\\.0\\.1|\\[0:0:0:0:0:0:0:1\\]", "(^192\\.168\\.)",
|
||||||
"X-Forwarded-For"))
|
"X-Forwarded-For"))
|
||||||
.addFilter(new ExcludePathAwareShallowETagFilter(
|
.addFilter(new ExcludePathAwareShallowETagFilter(
|
||||||
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
|
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -23,6 +24,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
@@ -40,6 +42,9 @@ public class DosFilter extends OncePerRequestFilter {
|
|||||||
private static final Logger LOG_BLACKLIST = LoggerFactory
|
private static final Logger LOG_BLACKLIST = LoggerFactory
|
||||||
.getLogger(SecurityConstants.SECURITY_LOG_PREFIX + ".blacklist");
|
.getLogger(SecurityConstants.SECURITY_LOG_PREFIX + ".blacklist");
|
||||||
|
|
||||||
|
private final AntPathMatcher antMatcher = new AntPathMatcher();
|
||||||
|
private final Collection<String> includeAntPaths;
|
||||||
|
|
||||||
private final Pattern ipAdressBlacklist;
|
private final Pattern ipAdressBlacklist;
|
||||||
|
|
||||||
private final Cache<String, AtomicInteger> readCountCache = Caffeine.newBuilder()
|
private final Cache<String, AtomicInteger> readCountCache = Caffeine.newBuilder()
|
||||||
@@ -57,6 +62,9 @@ public class DosFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter constructor including configuration.
|
* Filter constructor including configuration.
|
||||||
|
*
|
||||||
|
* @param includeAntPaths
|
||||||
|
* paths where filter should hit
|
||||||
*
|
*
|
||||||
* @param maxRead
|
* @param maxRead
|
||||||
* Maximum number of allowed REST read/GET requests per second
|
* Maximum number of allowed REST read/GET requests per second
|
||||||
@@ -73,9 +81,10 @@ public class DosFilter extends OncePerRequestFilter {
|
|||||||
* the header containing the forwarded IP address e.g.
|
* the header containing the forwarded IP address e.g.
|
||||||
* {@code x-forwarded-for}
|
* {@code x-forwarded-for}
|
||||||
*/
|
*/
|
||||||
public DosFilter(final int maxRead, final int maxWrite, final String ipDosWhiteListPattern,
|
public DosFilter(final Collection<String> includeAntPaths, final int maxRead, final int maxWrite,
|
||||||
final String ipBlackListPattern, final String forwardHeader) {
|
final String ipDosWhiteListPattern, final String ipBlackListPattern, final String forwardHeader) {
|
||||||
|
|
||||||
|
this.includeAntPaths = includeAntPaths;
|
||||||
this.maxRead = maxRead;
|
this.maxRead = maxRead;
|
||||||
this.maxWrite = maxWrite;
|
this.maxWrite = maxWrite;
|
||||||
this.forwardHeader = forwardHeader;
|
this.forwardHeader = forwardHeader;
|
||||||
@@ -93,10 +102,24 @@ public class DosFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldInclude(final HttpServletRequest request) {
|
||||||
|
if (includeAntPaths == null || includeAntPaths.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return includeAntPaths.stream()
|
||||||
|
.anyMatch(pattern -> antMatcher.match(request.getContextPath() + pattern, request.getRequestURI()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
|
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
|
||||||
final FilterChain filterChain) throws ServletException, IOException {
|
final FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
|
if (!shouldInclude(request)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean processChain;
|
boolean processChain;
|
||||||
|
|
||||||
final String ip = IpUtil.getClientIpFromRequest(request, forwardHeader).getHost();
|
final String ip = IpUtil.getClientIpFromRequest(request, forwardHeader).getHost();
|
||||||
|
|||||||
@@ -27,14 +27,13 @@ import org.springframework.web.filter.ShallowEtagHeaderFilter;
|
|||||||
public class ExcludePathAwareShallowETagFilter extends ShallowEtagHeaderFilter {
|
public class ExcludePathAwareShallowETagFilter extends ShallowEtagHeaderFilter {
|
||||||
|
|
||||||
private final String[] excludeAntPaths;
|
private final String[] excludeAntPaths;
|
||||||
private final AntPathMatcher antMatcher;
|
private final AntPathMatcher antMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param excludeAntPaths
|
* @param excludeAntPaths
|
||||||
*/
|
*/
|
||||||
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
|
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
|
||||||
this.excludeAntPaths = excludeAntPaths;
|
this.excludeAntPaths = excludeAntPaths;
|
||||||
this.antMatcher = new AntPathMatcher();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,6 +20,19 @@ public class HawkbitSecurityProperties {
|
|||||||
private final Clients clients = new Clients();
|
private final Clients clients = new Clients();
|
||||||
private final Dos dos = new Dos();
|
private final Dos dos = new Dos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content Security policy Header for Manager UI.
|
||||||
|
*/
|
||||||
|
private String contentSecurityPolicy;
|
||||||
|
|
||||||
|
public String getContentSecurityPolicy() {
|
||||||
|
return contentSecurityPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentSecurityPolicy(final String contentSecurityPolicy) {
|
||||||
|
this.contentSecurityPolicy = contentSecurityPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
public Dos getDos() {
|
public Dos getDos() {
|
||||||
return dos;
|
return dos;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ public class SystemSecurityContext {
|
|||||||
setSystemContext(SecurityContextHolder.getContext());
|
setSystemContext(SecurityContextHolder.getContext());
|
||||||
return callable.call();
|
return callable.call();
|
||||||
|
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
throw e;
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user