Split SecurityManagedConfiguration to mgmt and ddi starters (#2014)
* SecurityManagedConfiguration is moved to hawkbit-rest-core with commons for mgmt and ddi only * Configurations for DDI and Management API are moved to respective starters * hawkbit-http-security is removed - DosFilter (as common) is moved in hawkbit-rest-security, rest to the ddi starter as used only there * some classes are moved into different packages - it is a bad practice to have same packet into multiple artifacts _release_notes_ Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -33,7 +33,7 @@ dependencies on hawkBit users. We in fact are looking into reducing them in futu
|
||||
So we kindly ask contributors:
|
||||
|
||||
* not introduce extra utility library dependencies
|
||||
* keep them out of the core modules (e.g. hawkbit-core, hawkbit-rest-core, hawkbit-http-security) to avoid that all
|
||||
* keep them out of the core modules (e.g. hawkbit-core, hawkbit-rest-core) to avoid that all
|
||||
modules have them as transitive dependency
|
||||
* use utility functions in general based in the following priority:
|
||||
* use utility functions from JDK if feasible
|
||||
|
||||
@@ -33,12 +33,6 @@
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-security-core</artifactId>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation to enable the managed security configuration.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(SecurityManagedConfiguration.class)
|
||||
public @interface EnableHawkbitManagedSecurityConfiguration {
|
||||
|
||||
}
|
||||
@@ -1,526 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSource;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateAnonymousDownloadFilter;
|
||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
|
||||
import org.eclipse.hawkbit.security.MdcHandler;
|
||||
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.firewall.FirewalledRequest;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* All configurations related to HawkBit's authentication and authorization layer.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@PropertySource("classpath:hawkbit-security-defaults.properties")
|
||||
public class SecurityManagedConfiguration {
|
||||
|
||||
public static final String ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES = """
|
||||
******************
|
||||
** Anonymous controller security enabled, should only be used for development purposes **
|
||||
******************""";
|
||||
private static final int DOS_FILTER_ORDER = -200;
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server system management interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Collections.emptyList(),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(List.of("/system/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosSystemFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* HttpFirewall which enables to define a list of allowed host names.
|
||||
*
|
||||
* @return the http firewall.
|
||||
*/
|
||||
@Bean
|
||||
public HttpFirewall httpFirewall(final HawkbitSecurityProperties hawkbitSecurityProperties) {
|
||||
final List<String> allowedHostNames = hawkbitSecurityProperties.getAllowedHostNames();
|
||||
final IgnorePathsStrictHttpFirewall firewall = new IgnorePathsStrictHttpFirewall(
|
||||
hawkbitSecurityProperties.getHttpFirewallIgnoredPaths());
|
||||
|
||||
if (!CollectionUtils.isEmpty(allowedHostNames)) {
|
||||
firewall.setAllowedHostnames(hostName -> {
|
||||
log.debug("Firewall check host: {}, allowed: {}", hostName, allowedHostNames.contains(hostName));
|
||||
return allowedHostNames.contains(hostName);
|
||||
});
|
||||
}
|
||||
return firewall;
|
||||
}
|
||||
|
||||
private static FilterRegistrationBean<DosFilter> dosFilter(final Collection<String> includeAntPaths,
|
||||
final HawkbitSecurityProperties.Dos.Filter filterProperties,
|
||||
final HawkbitSecurityProperties.Clients clientProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>();
|
||||
|
||||
filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(),
|
||||
filterProperties.getMaxWrite(), filterProperties.getWhitelist(), clientProperties.getBlacklist(),
|
||||
clientProperties.getRemoteIpHeader()));
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
private static AuthenticationManager setAuthenticationManager(final HttpSecurity http, final DdiSecurityProperties ddiSecurityConfiguration)
|
||||
throws Exception {
|
||||
// configure authentication manager
|
||||
final AuthenticationManager authenticationManager =
|
||||
http
|
||||
.getSharedObject(AuthenticationManagerBuilder.class)
|
||||
.authenticationProvider(
|
||||
new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()))
|
||||
.build();
|
||||
http.authenticationManager(authenticationManager);
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security configuration for the hawkBit server DDI interface.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||
static class ControllerSecurityConfigurationAdapter {
|
||||
|
||||
private static final String[] DDI_ANT_MATCHERS = {
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/confirmationBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final TenantAware tenantAware;
|
||||
private final DdiSecurityProperties ddiSecurityConfiguration;
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Autowired
|
||||
ControllerSecurityConfigurationAdapter(final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecurityConfiguration,
|
||||
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
|
||||
this.securityProperties = securityProperties;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
protected FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean =
|
||||
dosFilter(List.of(DDI_ANT_MATCHERS),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(300)
|
||||
protected SecurityFilterChain filterChainDDI(final HttpSecurity http) throws Exception {
|
||||
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
|
||||
|
||||
http
|
||||
.securityMatcher(DDI_ANT_MATCHERS)
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.warn(ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
List.of(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
|
||||
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
http
|
||||
.securityContext(AbstractHttpConfigurer::disable)
|
||||
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
|
||||
} else {
|
||||
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry.anyRequest().authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.addFilter(securityHeaderFilter)
|
||||
.addFilter(securityTokenFilter)
|
||||
.addFilter(gatewaySecurityTokenFilter)
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
|
||||
(request, response, authException) ->
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value())))
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Security configuration for the hawkBit server DDI download interface.
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||
static class ControllerDownloadSecurityConfigurationAdapter {
|
||||
|
||||
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING
|
||||
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final TenantAware tenantAware;
|
||||
private final DdiSecurityProperties ddiSecurityConfiguration;
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Autowired
|
||||
ControllerDownloadSecurityConfigurationAdapter(final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecurityConfiguration,
|
||||
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
|
||||
this.securityProperties = securityProperties;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI download interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterDDIDL(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(List.of(DDI_DL_ANT_MATCHER),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiDlFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(301)
|
||||
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
|
||||
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
|
||||
|
||||
http
|
||||
.securityMatcher(DDI_DL_ANT_MATCHER)
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.warn(ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
List.of(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
|
||||
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
http
|
||||
.securityContext(AbstractHttpConfigurer::disable)
|
||||
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
|
||||
} else {
|
||||
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager);
|
||||
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.addFilter(securityHeaderFilter)
|
||||
.addFilter(securityTokenFilter)
|
||||
.addFilter(gatewaySecurityTokenFilter)
|
||||
.addFilter(controllerAnonymousDownloadFilter)
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
|
||||
(request, response, authException) -> response.setStatus(HttpStatus.UNAUTHORIZED.value())))
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Security configuration for the REST management API.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@ConditionalOnClass(MgmtApiConfiguration.class)
|
||||
public static class RestSecurityConfigurationAdapter {
|
||||
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
|
||||
public RestSecurityConfigurationAdapter(final HawkbitSecurityProperties securityProperties) {
|
||||
this.securityProperties = securityProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server Management interface against to
|
||||
* many requests.
|
||||
*
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterREST() {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(null,
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(List.of(
|
||||
MgmtRestConstants.BASE_REST_MAPPING + "/*",
|
||||
MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosMgmtFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(350)
|
||||
SecurityFilterChain filterChainREST(
|
||||
final HttpSecurity http,
|
||||
@Autowired(required = false)
|
||||
@Qualifier("hawkbitOAuth2ResourceServerCustomizer") final Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer,
|
||||
// called just before build of the SecurityFilterChain.
|
||||
// could be used for instance to set authentication provider
|
||||
// Note: implementation of the customizer shall always take in account what is the already set by the
|
||||
// hawkBit
|
||||
@Autowired(required = false)
|
||||
@Qualifier("hawkbitHttpSecurityCustomizer") final Customizer<HttpSecurity> httpSecurityCustomizer,
|
||||
final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext) throws Exception {
|
||||
http
|
||||
.securityMatcher(MgmtRestConstants.BASE_REST_MAPPING + "/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry
|
||||
.requestMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.requestCache(AbstractHttpConfigurer::disable)
|
||||
.exceptionHandling(Customizer.withDefaults())
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.addFilterAfter(
|
||||
// Servlet filter to create metadata after successful authentication over RESTful.
|
||||
(request, response, chain) -> {
|
||||
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
},
|
||||
SessionManagementFilter.class);
|
||||
|
||||
if (securityProperties.getCors().isEnabled()) {
|
||||
http.cors(configurer -> configurer.configurationSource(corsConfigurationSource()));
|
||||
}
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
if (oauth2ResourceServerCustomizer != null) {
|
||||
http.oauth2ResourceServer(oauth2ResourceServerCustomizer);
|
||||
}
|
||||
if (oauth2ResourceServerCustomizer == null || securityProperties.isAllowHttpBasicOnOAuthEnabled()) {
|
||||
http.httpBasic(configurer -> {
|
||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
|
||||
configurer.authenticationEntryPoint(basicAuthEntryPoint);
|
||||
});
|
||||
}
|
||||
|
||||
if (httpSecurityCustomizer != null) {
|
||||
httpSecurityCustomizer.customize(http);
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins());
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.setAllowedHeaders(securityProperties.getCors().getAllowedHeaders());
|
||||
corsConfiguration.setAllowedMethods(securityProperties.getCors().getAllowedMethods());
|
||||
corsConfiguration.setExposedHeaders(securityProperties.getCors().getExposedHeaders());
|
||||
return request -> corsConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
private static class IgnorePathsStrictHttpFirewall extends StrictHttpFirewall {
|
||||
|
||||
private final Collection<String> pathsToIgnore;
|
||||
|
||||
public IgnorePathsStrictHttpFirewall(final Collection<String> pathsToIgnore) {
|
||||
super();
|
||||
this.pathsToIgnore = pathsToIgnore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) {
|
||||
if (pathsToIgnore != null && pathsToIgnore.contains(request.getRequestURI())) {
|
||||
return new FirewalledRequest(request) {
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// nothing to do
|
||||
}
|
||||
};
|
||||
}
|
||||
return super.getFirewalledRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,6 @@
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.DosFilter;
|
||||
import org.eclipse.hawkbit.rest.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.app.ddi;
|
||||
|
||||
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -24,7 +23,6 @@ import org.springframework.web.servlet.view.RedirectView;
|
||||
* The minimal configuration for the stand alone hawkBit DDI server.
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit")
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
public class DDIStart {
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,11 +56,6 @@
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.ControllerTenantAwareAuthenticationDetailsSource;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateAnonymousDownloadFilter;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.rest.SecurityManagedConfiguration;
|
||||
import org.eclipse.hawkbit.rest.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.MdcHandler;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Security configuration for the hawkBit server DDI download interface.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
class ControllerDownloadSecurityConfiguration {
|
||||
|
||||
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING +
|
||||
"/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final TenantAware tenantAware;
|
||||
private final DdiSecurityProperties ddiSecurityConfiguration;
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Autowired
|
||||
ControllerDownloadSecurityConfiguration(final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecurityConfiguration,
|
||||
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
|
||||
this.securityProperties = securityProperties;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI download interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterDDIDL(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = SecurityManagedConfiguration.dosFilter(List.of(DDI_DL_ANT_MATCHER),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(SecurityManagedConfiguration.DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiDlFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(301)
|
||||
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
|
||||
final AuthenticationManager authenticationManager = ControllerSecurityConfiguration.setAuthenticationManager(
|
||||
http, ddiSecurityConfiguration);
|
||||
|
||||
http
|
||||
.securityMatcher(DDI_DL_ANT_MATCHER)
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.warn(
|
||||
SecurityManagedConfiguration.ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
List.of(new SimpleGrantedAuthority(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
|
||||
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
http
|
||||
.securityContext(AbstractHttpConfigurer::disable)
|
||||
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
|
||||
} else {
|
||||
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager);
|
||||
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
|
||||
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.addFilter(securityHeaderFilter)
|
||||
.addFilter(securityTokenFilter)
|
||||
.addFilter(gatewaySecurityTokenFilter)
|
||||
.addFilter(controllerAnonymousDownloadFilter)
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
|
||||
(request, response, authException) -> response.setStatus(HttpStatus.UNAUTHORIZED.value())))
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.ControllerTenantAwareAuthenticationDetailsSource;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.autoconfigure.ddi.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.rest.SecurityManagedConfiguration;
|
||||
import org.eclipse.hawkbit.rest.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.MdcHandler;
|
||||
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Security configuration for the hawkBit server DDI interface.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
class ControllerSecurityConfiguration {
|
||||
|
||||
private static final String[] DDI_ANT_MATCHERS = {
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/confirmationBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/installedBase/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
|
||||
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final TenantAware tenantAware;
|
||||
private final DdiSecurityProperties ddiSecurityConfiguration;
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Autowired
|
||||
ControllerSecurityConfiguration(final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecurityConfiguration,
|
||||
final HawkbitSecurityProperties securityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
|
||||
this.securityProperties = securityProperties;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server DDI interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
protected FilterRegistrationBean<DosFilter> dosFilterDDI(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean =
|
||||
SecurityManagedConfiguration.dosFilter(List.of(DDI_ANT_MATCHERS),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setOrder(SecurityManagedConfiguration.DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosDDiFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(300)
|
||||
protected SecurityFilterChain filterChainDDI(final HttpSecurity http) throws Exception {
|
||||
final AuthenticationManager authenticationManager = setAuthenticationManager(http, ddiSecurityConfiguration);
|
||||
|
||||
http
|
||||
.securityMatcher(DDI_ANT_MATCHERS)
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
|
||||
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
|
||||
log.warn(SecurityManagedConfiguration.ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES);
|
||||
|
||||
final AnonymousAuthenticationFilter anonymousFilter = new AnonymousAuthenticationFilter(
|
||||
"controllerAnonymousFilter", "anonymous",
|
||||
List.of(new SimpleGrantedAuthority(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
|
||||
anonymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
http
|
||||
.securityContext(AbstractHttpConfigurer::disable)
|
||||
.anonymous(configurer -> configurer.authenticationFilter(anonymousFilter));
|
||||
} else {
|
||||
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
|
||||
tenantAware, systemSecurityContext);
|
||||
securityHeaderFilter.setAuthenticationManager(authenticationManager);
|
||||
securityHeaderFilter.setCheckForPrincipalChanges(true);
|
||||
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
|
||||
securityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
securityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager);
|
||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
|
||||
http
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry.anyRequest().authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.addFilter(securityHeaderFilter)
|
||||
.addFilter(securityTokenFilter)
|
||||
.addFilter(gatewaySecurityTokenFilter)
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(
|
||||
(request, response, authException) ->
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value())))
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
static AuthenticationManager setAuthenticationManager(final HttpSecurity http, final DdiSecurityProperties ddiSecurityConfiguration)
|
||||
throws Exception {
|
||||
// configure authentication manager
|
||||
final AuthenticationManager authenticationManager =
|
||||
http
|
||||
.getSharedObject(AuthenticationManagerBuilder.class)
|
||||
.authenticationProvider(
|
||||
new PreAuthTokenSourceTrustAuthenticationProvider(ddiSecurityConfiguration.getRp().getTrustedIPs()))
|
||||
.build();
|
||||
http.authenticationManager(authenticationManager);
|
||||
return authenticationManager;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,6 @@ import org.springframework.context.annotation.Import;
|
||||
@Configuration
|
||||
@ConditionalOnClass(DdiApiConfiguration.class)
|
||||
@Import(DdiApiConfiguration.class)
|
||||
public class DDiApiAutoConfiguration {
|
||||
public class DdiApiAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -23,6 +23,9 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
|
||||
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.util.UrlUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -7,13 +7,14 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.security.TenantAwareWebAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.util.UrlUtils;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
@@ -7,11 +7,14 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
|
||||
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -39,8 +42,7 @@ public class HttpControllerPreAuthenticateAnonymousDownloadFilter extends Abstra
|
||||
|
||||
@Override
|
||||
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
|
||||
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware,
|
||||
systemSecurityContext);
|
||||
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -7,11 +7,14 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
|
||||
import org.eclipse.hawkbit.security.PreAuthenticationFilter;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -25,7 +28,7 @@ public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends Abstract
|
||||
private final String caAuthorityNameHeader;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ControllerPreAuthenticatedSecurityHeaderFilter}, in
|
||||
* Creates a new {@link org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter}, in
|
||||
* case the HTTP request matches the given pattern the principal is parsed
|
||||
* from the HTTP request with the given URI pattern, in case the URI pattern
|
||||
* does not match the current request then only the existence of the
|
||||
@@ -51,7 +54,8 @@ public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends Abstract
|
||||
|
||||
@Override
|
||||
protected PreAuthenticationFilter createControllerAuthenticationFilter() {
|
||||
return new ControllerPreAuthenticatedSecurityHeaderFilter(caCommonNameHeader, caAuthorityNameHeader,
|
||||
return new ControllerPreAuthenticatedSecurityHeaderFilter(
|
||||
caCommonNameHeader, caAuthorityNameHeader,
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
}
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration
|
||||
org.eclipse.hawkbit.autoconfigure.ddi.DdiApiAutoConfiguration
|
||||
org.eclipse.hawkbit.rest.SecurityManagedConfiguration
|
||||
org.eclipse.hawkbit.autoconfigure.ddi.ControllerSecurityConfiguration
|
||||
org.eclipse.hawkbit.autoconfigure.ddi.ControllerDownloadSecurityConfiguration
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.autoconfigure.ddi.security;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -18,6 +18,8 @@ import java.util.Collections;
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
|
||||
import org.eclipse.hawkbit.security.TenantAwareWebAuthenticationDetails;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -34,9 +36,10 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
|
||||
|
||||
private static final String REQUEST_SOURCE_IP = "127.0.0.1";
|
||||
|
||||
private final PreAuthTokenSourceTrustAuthenticationProvider underTestWithoutSourceIpCheck = new PreAuthTokenSourceTrustAuthenticationProvider();
|
||||
private final PreAuthTokenSourceTrustAuthenticationProvider underTestWithSourceIpCheck = new PreAuthTokenSourceTrustAuthenticationProvider(
|
||||
REQUEST_SOURCE_IP);
|
||||
private final PreAuthTokenSourceTrustAuthenticationProvider underTestWithoutSourceIpCheck =
|
||||
new PreAuthTokenSourceTrustAuthenticationProvider();
|
||||
private final PreAuthTokenSourceTrustAuthenticationProvider underTestWithSourceIpCheck =
|
||||
new PreAuthTokenSourceTrustAuthenticationProvider(REQUEST_SOURCE_IP);
|
||||
|
||||
@Mock
|
||||
private TenantAwareWebAuthenticationDetails webAuthenticationDetailsMock;
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.app.dmf;
|
||||
|
||||
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -18,7 +17,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
* The minimal configuration for the stand alone hawkBit DMF server.
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit")
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
public class DMFStart {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<!--
|
||||
|
||||
Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
|
||||
This program and the accompanying materials are made
|
||||
available under the terms of the Eclipse Public License 2.0
|
||||
which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>hawkbit-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
</parent>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<name>hawkBit :: HTTP Security</name>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-security-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -83,12 +83,6 @@
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.app.mgmt;
|
||||
|
||||
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -24,7 +23,6 @@ import org.springframework.web.servlet.view.RedirectView;
|
||||
* The minimal configuration for the stand alone hawkBit server.
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit")
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
public class MgmtServerStart {
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,11 +33,6 @@
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.autoconfigure.mgmt;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.rest.SecurityManagedConfiguration;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.rest.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.MdcHandler;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* Security configuration for the REST management API.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class MgmtSecurityConfiguration {
|
||||
|
||||
private final HawkbitSecurityProperties securityProperties;
|
||||
|
||||
public MgmtSecurityConfiguration(final HawkbitSecurityProperties securityProperties) {
|
||||
this.securityProperties = securityProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server Management interface against to many requests.
|
||||
*
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosFilterREST() {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = SecurityManagedConfiguration.dosFilter(null,
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(List.of(
|
||||
MgmtRestConstants.BASE_REST_MAPPING + "/*",
|
||||
MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/*"));
|
||||
filterRegBean.setOrder(SecurityManagedConfiguration.DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosMgmtFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(350)
|
||||
SecurityFilterChain filterChainREST(
|
||||
final HttpSecurity http,
|
||||
@Autowired(required = false)
|
||||
@Qualifier("hawkbitOAuth2ResourceServerCustomizer") final Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer,
|
||||
// called just before build of the SecurityFilterChain.
|
||||
// could be used for instance to set authentication provider
|
||||
// Note: implementation of the customizer shall always take in account what is the already set by the
|
||||
// hawkBit
|
||||
@Autowired(required = false)
|
||||
@Qualifier("hawkbitHttpSecurityCustomizer") final Customizer<HttpSecurity> httpSecurityCustomizer,
|
||||
final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext) throws Exception {
|
||||
http
|
||||
.securityMatcher(MgmtRestConstants.BASE_REST_MAPPING + "/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.authorizeHttpRequests(amrmRegistry ->
|
||||
amrmRegistry
|
||||
.requestMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.requestCache(AbstractHttpConfigurer::disable)
|
||||
.exceptionHandling(Customizer.withDefaults())
|
||||
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.addFilterAfter(
|
||||
// Servlet filter to create metadata after successful authentication over RESTful.
|
||||
(request, response, chain) -> {
|
||||
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
},
|
||||
SessionManagementFilter.class);
|
||||
|
||||
if (securityProperties.getCors().isEnabled()) {
|
||||
http.cors(configurer -> configurer.configurationSource(corsConfigurationSource()));
|
||||
}
|
||||
|
||||
if (securityProperties.isRequireSsl()) {
|
||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
||||
}
|
||||
|
||||
if (oauth2ResourceServerCustomizer != null) {
|
||||
http.oauth2ResourceServer(oauth2ResourceServerCustomizer);
|
||||
}
|
||||
if (oauth2ResourceServerCustomizer == null || securityProperties.isAllowHttpBasicOnOAuthEnabled()) {
|
||||
http.httpBasic(configurer -> {
|
||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(securityProperties.getBasicRealm());
|
||||
configurer.authenticationEntryPoint(basicAuthEntryPoint);
|
||||
});
|
||||
}
|
||||
|
||||
if (httpSecurityCustomizer != null) {
|
||||
httpSecurityCustomizer.customize(http);
|
||||
}
|
||||
|
||||
MdcHandler.Filter.addMdcFilter(http);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
corsConfiguration.setAllowedOrigins(securityProperties.getCors().getAllowedOrigins());
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.setAllowedHeaders(securityProperties.getCors().getAllowedHeaders());
|
||||
corsConfiguration.setAllowedMethods(securityProperties.getCors().getAllowedMethods());
|
||||
corsConfiguration.setExposedHeaders(securityProperties.getCors().getExposedHeaders());
|
||||
return request -> corsConfiguration;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
org.eclipse.hawkbit.autoconfigure.mgmt.MgmtApiAutoConfiguration
|
||||
org.eclipse.hawkbit.rest.SecurityManagedConfiguration
|
||||
org.eclipse.hawkbit.autoconfigure.mgmt.MgmtSecurityConfiguration
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.app;
|
||||
|
||||
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -24,7 +23,6 @@ import org.springframework.web.servlet.view.RedirectView;
|
||||
* The minimal configuration for the stand alone hawkBit server.
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit")
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
// Exception squid:S1118 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S1118" })
|
||||
public class Start {
|
||||
|
||||
@@ -29,18 +29,15 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-artifact-api</artifactId>
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-artifact-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -61,12 +58,27 @@
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.rest;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.rest.security.DosFilter;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.firewall.FirewalledRequest;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* All configurations related to HawkBit's authentication and authorization layer.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true)
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@PropertySource("classpath:hawkbit-security-defaults.properties")
|
||||
public class SecurityManagedConfiguration {
|
||||
|
||||
public static final String ANONYMOUS_CONTROLLER_SECURITY_ENABLED_SHOULD_ONLY_BE_USED_FOR_DEVELOPMENT_PURPOSES = """
|
||||
******************
|
||||
** Anonymous controller security enabled, should only be used for development purposes **
|
||||
******************""";
|
||||
public static final int DOS_FILTER_ORDER = -200;
|
||||
|
||||
/**
|
||||
* Filter to protect the hawkBit server system management interface against too many requests.
|
||||
*
|
||||
* @param securityProperties for filter configuration
|
||||
* @return the spring filter registration bean for registering a denial of service protection filter in the filter chain
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean<DosFilter> dosSystemFilter(final HawkbitSecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = dosFilter(Collections.emptyList(),
|
||||
securityProperties.getDos().getFilter(), securityProperties.getClients());
|
||||
filterRegBean.setUrlPatterns(List.of("/system/*"));
|
||||
filterRegBean.setOrder(DOS_FILTER_ORDER);
|
||||
filterRegBean.setName("dosSystemFilter");
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* HttpFirewall which enables to define a list of allowed host names.
|
||||
*
|
||||
* @return the http firewall.
|
||||
*/
|
||||
@Bean
|
||||
public HttpFirewall httpFirewall(final HawkbitSecurityProperties hawkbitSecurityProperties) {
|
||||
final List<String> allowedHostNames = hawkbitSecurityProperties.getAllowedHostNames();
|
||||
final IgnorePathsStrictHttpFirewall firewall = new IgnorePathsStrictHttpFirewall(
|
||||
hawkbitSecurityProperties.getHttpFirewallIgnoredPaths());
|
||||
|
||||
if (!CollectionUtils.isEmpty(allowedHostNames)) {
|
||||
firewall.setAllowedHostnames(hostName -> {
|
||||
log.debug("Firewall check host: {}, allowed: {}", hostName, allowedHostNames.contains(hostName));
|
||||
return allowedHostNames.contains(hostName);
|
||||
});
|
||||
}
|
||||
return firewall;
|
||||
}
|
||||
|
||||
public static FilterRegistrationBean<DosFilter> dosFilter(final Collection<String> includeAntPaths,
|
||||
final HawkbitSecurityProperties.Dos.Filter filterProperties,
|
||||
final HawkbitSecurityProperties.Clients clientProperties) {
|
||||
final FilterRegistrationBean<DosFilter> filterRegBean = new FilterRegistrationBean<>();
|
||||
|
||||
filterRegBean.setFilter(new DosFilter(includeAntPaths, filterProperties.getMaxRead(),
|
||||
filterProperties.getMaxWrite(), filterProperties.getWhitelist(), clientProperties.getBlacklist(),
|
||||
clientProperties.getRemoteIpHeader()));
|
||||
|
||||
return filterRegBean;
|
||||
}
|
||||
|
||||
private static class IgnorePathsStrictHttpFirewall extends StrictHttpFirewall {
|
||||
|
||||
private final Collection<String> pathsToIgnore;
|
||||
|
||||
public IgnorePathsStrictHttpFirewall(final Collection<String> pathsToIgnore) {
|
||||
super();
|
||||
this.pathsToIgnore = pathsToIgnore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) {
|
||||
if (pathsToIgnore != null && pathsToIgnore.contains(request.getRequestURI())) {
|
||||
return new FirewalledRequest(request) {
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// nothing to do
|
||||
}
|
||||
};
|
||||
}
|
||||
return super.getFirewalledRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.security;
|
||||
package org.eclipse.hawkbit.rest.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.security.SecurityConstants;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,11 +36,6 @@
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-http-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
|
||||
1
pom.xml
1
pom.xml
@@ -135,7 +135,6 @@
|
||||
<module>hawkbit-core</module>
|
||||
<module>hawkbit-security-core</module>
|
||||
<module>hawkbit-security-integration</module>
|
||||
<module>hawkbit-http-security</module>
|
||||
<module>hawkbit-artifact</module>
|
||||
<module>hawkbit-repository</module>
|
||||
<module>hawkbit-autoconfigure</module>
|
||||
|
||||
@@ -46,7 +46,7 @@ This has to be kept in mind e.g. if the scheduler executes critical code which h
|
||||
### Denial-of-Service (DoS) filter
|
||||
|
||||
hawkBit owns the feature of guarding itself from DoS attacks,
|
||||
a [DoS filter](https://github.com/eclipse-hawkbit/hawkbit/blob/master/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/DosFilter.java).
|
||||
a [DoS filter](https://github.com/eclipse-hawkbit/hawkbit/blob/master/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/security/DosFilter.java).
|
||||
It reduces the maximum number of requests per seconds which can be configured for read and write requests.
|
||||
This mechanism is only working for every node separately, i.e. in a cluster environment the worst-case behaviour would
|
||||
be that the maximum number of requests per seconds will be increased to its product if every request is handled by a
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.doc;
|
||||
|
||||
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -18,7 +17,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
* The minimal configuration for the stand alone hawkBit server.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
// Exception squid:S1118 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S1118" })
|
||||
public class Start {
|
||||
|
||||
Reference in New Issue
Block a user