Add support for multiple hashes in Issuer hash based authentication
Signed-off-by: Marcel Mager (INST-IOT/ESB) <Marcel.Mager@bosch-si.com>
This commit is contained in:
@@ -50,7 +50,7 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
|
|||||||
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
|
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
||||||
|
|
||||||
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
|
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
@@ -97,7 +101,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
public Object getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
||||||
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||||
sslIssuerNameConfigTenantRunner);
|
sslIssuerNameConfigTenantRunner);
|
||||||
String controllerId = secruityToken.getControllerId();
|
String controllerId = secruityToken.getControllerId();
|
||||||
@@ -108,7 +112,17 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
controllerId = secruityToken.getHeader(caCommonNameHeader);
|
controllerId = secruityToken.getHeader(caCommonNameHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HeaderAuthentication(controllerId, authorityNameConfigurationValue);
|
String[] knownHashes = splitMultiHash(authorityNameConfigurationValue);
|
||||||
|
if (knownHashes.length > 1) {
|
||||||
|
Set<HeaderAuthentication> multiHashes = new HashSet<>();
|
||||||
|
final String cntlId = controllerId;
|
||||||
|
Arrays.asList(knownHashes)
|
||||||
|
.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem)));
|
||||||
|
return multiHashes;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return new HeaderAuthentication(controllerId, authorityNameConfigurationValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,12 +131,13 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
* It's ok if we find the the hash in any the trusted CA chain to accept
|
* It's ok if we find the the hash in any the trusted CA chain to accept
|
||||||
* this request for this tenant.
|
* this request for this tenant.
|
||||||
*/
|
*/
|
||||||
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHash) {
|
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) {
|
||||||
// iterate over the headers until we get a null header.
|
// iterate over the headers until we get a null header.
|
||||||
|
String[] knownHashes = splitMultiHash(knownIssuerHashes);
|
||||||
int iHeader = 1;
|
int iHeader = 1;
|
||||||
String foundHash;
|
String foundHash;
|
||||||
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
|
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
|
||||||
if (foundHash.equals(knownIssuerHash)) {
|
if (Arrays.asList(knownHashes).contains(foundHash)) {
|
||||||
if (LOGGER.isTraceEnabled()) {
|
if (LOGGER.isTraceEnabled()) {
|
||||||
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
|
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
|
||||||
}
|
}
|
||||||
@@ -148,4 +163,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
|
|||||||
TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue());
|
TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String[] splitMultiHash(String knownIssuerHashes) {
|
||||||
|
return knownIssuerHashes.split(";");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
// proxy which extracted the CN from the certificate
|
// proxy which extracted the CN from the certificate
|
||||||
if (principal.equals(credentials)) {
|
if (principal.equals(credentials)) {
|
||||||
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
||||||
|
|
||||||
|
} else if (Collection.class.isAssignableFrom(credentials.getClass())) {
|
||||||
|
final Collection<?> multiValueCredentials = (Collection<?>) credentials;
|
||||||
|
if (multiValueCredentials.contains(principal)) {
|
||||||
|
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successAuthentication) {
|
if (successAuthentication) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public interface PreAuthentificationFilter {
|
|||||||
* the secruityToken
|
* the secruityToken
|
||||||
* @return the extracted tenant and controller id
|
* @return the extracted tenant and controller id
|
||||||
*/
|
*/
|
||||||
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to add additional authorities to the successful authenticated
|
* Allows to add additional authorities to the successful authenticated
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||||
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
|
|
||||||
|
@Features("Unit Tests - Security")
|
||||||
|
@Stories("Issuer hash based authentication")
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ControllerPreAuthenticatedSecurityHeaderFilterTest {
|
||||||
|
|
||||||
|
private ControllerPreAuthenticatedSecurityHeaderFilter underTest;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TenantConfigurationManagement tenantConfigurationManagementMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TenantSecurityToken tenantSecurityTokenMock;
|
||||||
|
|
||||||
|
private SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
|
||||||
|
|
||||||
|
private static final String CA_COMMON_NAME = "ca-cn";
|
||||||
|
|
||||||
|
private static final String X_SSL_ISSUER_HASH_1 = "X-Ssl-Issuer-Hash-1";
|
||||||
|
|
||||||
|
private static final String SINGLE_HASH = "hash1";
|
||||||
|
|
||||||
|
private static final String MULTI_HASH = "hash1;hash2;hash3";
|
||||||
|
|
||||||
|
private static final TenantConfigurationValue<String> CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue
|
||||||
|
.<String>builder().value(SINGLE_HASH).build();
|
||||||
|
|
||||||
|
private static final TenantConfigurationValue<String> CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue
|
||||||
|
.<String>builder().value(MULTI_HASH).build();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
underTest = new ControllerPreAuthenticatedSecurityHeaderFilter(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d",
|
||||||
|
tenantConfigurationManagementMock,
|
||||||
|
tenantAware, new SystemSecurityContext(tenantAware));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the filter for issuer hash based authentication with a single known hash")
|
||||||
|
public void testIssuerHashBasedAuthenticationWithSingleKnownHash() {
|
||||||
|
// prepare security token
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken();
|
||||||
|
securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH);
|
||||||
|
// use single known hash
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_SINGLE_HASH);
|
||||||
|
assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the filter for issuer hash based authentication with multiple known hashes")
|
||||||
|
public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() {
|
||||||
|
// prepare security token
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken();
|
||||||
|
securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, SINGLE_HASH);
|
||||||
|
// use multiple known hashes
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||||
|
assertNotNull(underTest.getPreAuthenticatedPrincipal(securityToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the filter for issuer hash based authentication with unknown hash")
|
||||||
|
public void testIssuerHashBasedAuthenticationWithUnknownHash() {
|
||||||
|
// prepare security token
|
||||||
|
final TenantSecurityToken securityToken = prepareSecurityToken();
|
||||||
|
securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "unknown");
|
||||||
|
// use single known hash
|
||||||
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
|
||||||
|
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||||
|
assertNull(underTest.getPreAuthenticatedPrincipal(securityToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TenantSecurityToken prepareSecurityToken() {
|
||||||
|
final TenantSecurityToken securityToken = new TenantSecurityToken("default", "1234",
|
||||||
|
FileResource.createFileResourceBySha1("12345"));
|
||||||
|
securityToken.getHeaders().put(CA_COMMON_NAME, "any");
|
||||||
|
|
||||||
|
return securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -63,13 +63,17 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
|
|||||||
final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel();
|
final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel();
|
||||||
caRootAuthorityLabel.setDescription(
|
caRootAuthorityLabel.setDescription(
|
||||||
"The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate.");
|
"The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate.");
|
||||||
|
caRootAuthorityLabel.setWidthUndefined();
|
||||||
|
|
||||||
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(128).buildTextComponent();
|
caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(160).buildTextComponent();
|
||||||
caRootAuthorityTextField.setWidth("500px");
|
caRootAuthorityTextField.setWidth("100%");
|
||||||
caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged());
|
caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged());
|
||||||
|
|
||||||
caRootAuthorityLayout.addComponent(caRootAuthorityLabel);
|
caRootAuthorityLayout.addComponent(caRootAuthorityLabel);
|
||||||
|
caRootAuthorityLayout.setExpandRatio(caRootAuthorityLabel, 0);
|
||||||
caRootAuthorityLayout.addComponent(caRootAuthorityTextField);
|
caRootAuthorityLayout.addComponent(caRootAuthorityTextField);
|
||||||
|
caRootAuthorityLayout.setExpandRatio(caRootAuthorityTextField, 1);
|
||||||
|
caRootAuthorityLayout.setWidth("100%");
|
||||||
|
|
||||||
detailLayout.addComponent(caRootAuthorityLayout);
|
detailLayout.addComponent(caRootAuthorityLayout);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user