diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-09-11 11:29:51 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-09-11 11:29:51 +0200 |
commit | bf95a07154e11446ff4f2985e5b9951567a751a9 (patch) | |
tree | eb11cbfb414ab737a7b30b1db714f5d360fece73 | |
parent | 9be8547f8720e71d4e80582a9ce5f720d2c22d20 (diff) |
Remove NToken support from AthenzPrincipalFilter
3 files changed, 14 insertions, 131 deletions
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java index e51fb1a5c9a..cb153ecb1fc 100644 --- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java +++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java @@ -6,20 +6,15 @@ import com.yahoo.jdisc.Response; import com.yahoo.jdisc.http.filter.DiscFilterRequest; import com.yahoo.jdisc.http.filter.security.base.JsonSecurityRequestFilterBase; import com.yahoo.vespa.athenz.api.AthenzPrincipal; -import com.yahoo.vespa.athenz.api.NToken; import com.yahoo.vespa.athenz.utils.AthenzIdentities; -import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator; -import java.nio.file.Paths; import java.security.cert.X509Certificate; import java.util.List; import java.util.Optional; /** - * Authenticates Athenz principal, either through: - * 1. TLS client authentication (based on Athenz x509 identity certficiate). - * 2. The principal token (NToken) header. + * Authenticates Athenz principal through TLS client authentication (Athenz identity certificate). * The TLS authentication is based on the following assumptions: * - The underlying connector is configured with 'clientAuth' set to either WANT_AUTH or NEED_AUTH. * - The trust store is configured with the Athenz CA certificates only. @@ -33,22 +28,14 @@ public class AthenzPrincipalFilter extends JsonSecurityRequestFilterBase { public static final String RESULT_ERROR_MESSAGE_ATTRIBUTE = RESULT_ATTRIBUTE_PREFIX + ".error.message"; public static final String RESULT_PRINCIPAL = RESULT_ATTRIBUTE_PREFIX + ".principal"; - private final NTokenValidator validator; - private final String principalTokenHeader; private final boolean passthroughMode; @Inject public AthenzPrincipalFilter(AthenzPrincipalFilterConfig athenzPrincipalFilterConfig) { - this(new NTokenValidator(Paths.get(athenzPrincipalFilterConfig.athenzConfFile())), - athenzPrincipalFilterConfig.principalHeaderName(), - athenzPrincipalFilterConfig.passthroughMode()); + this(athenzPrincipalFilterConfig.passthroughMode()); } - AthenzPrincipalFilter(NTokenValidator validator, - String principalTokenHeader, - boolean passthroughMode) { - this.validator = validator; - this.principalTokenHeader = principalTokenHeader; + AthenzPrincipalFilter(boolean passthroughMode) { this.passthroughMode = passthroughMode; } @@ -58,23 +45,12 @@ public class AthenzPrincipalFilter extends JsonSecurityRequestFilterBase { Optional<AthenzPrincipal> certificatePrincipal = getClientCertificate(request) .map(AthenzIdentities::from) .map(AthenzPrincipal::new); - Optional<AthenzPrincipal> nTokenPrincipal = getPrincipalToken(request, principalTokenHeader) - .map(validator::validate); - - if (certificatePrincipal.isEmpty() && nTokenPrincipal.isEmpty()) { + if (certificatePrincipal.isEmpty()) { String errorMessage = "Unable to authenticate Athenz identity. " + "Either client certificate or principal token is required."; return createResponse(request, Response.Status.UNAUTHORIZED, errorMessage); } - if (certificatePrincipal.isPresent() && nTokenPrincipal.isPresent() - && !certificatePrincipal.get().getIdentity().equals(nTokenPrincipal.get().getIdentity())) { - String errorMessage = String.format( - "Identity in principal token does not match x509 CN: token-identity=%s, cert-identity=%s", - nTokenPrincipal.get().getIdentity().getFullName(), - certificatePrincipal.get().getIdentity().getFullName()); - return createResponse(request, Response.Status.UNAUTHORIZED, errorMessage); - } - AthenzPrincipal principal = nTokenPrincipal.orElseGet(certificatePrincipal::get); + AthenzPrincipal principal = certificatePrincipal.get(); request.setUserPrincipal(principal); request.setRemoteUser(principal.getName()); request.setAttribute(RESULT_PRINCIPAL, principal); @@ -90,12 +66,6 @@ public class AthenzPrincipalFilter extends JsonSecurityRequestFilterBase { return Optional.of(chain.get(0)); } - private static Optional<NToken> getPrincipalToken(DiscFilterRequest request, String principalTokenHeaderName) { - return Optional.ofNullable(request.getHeader(principalTokenHeaderName)) - .filter(token -> !token.isEmpty()) - .map(NToken::new); - } - private Optional<ErrorResponse> createResponse(DiscFilterRequest request, int statusCode, String message) { request.setAttribute(RESULT_ERROR_CODE_ATTRIBUTE, statusCode); request.setAttribute(RESULT_ERROR_MESSAGE_ATTRIBUTE, message); diff --git a/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def index 96f79f6d664..8fd95ee1dde 100644 --- a/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def +++ b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def @@ -1,11 +1,5 @@ # Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. namespace=jdisc.http.filter.security.athenz -# Principal header name -principalHeaderName string default="Athenz-Principal-Auth" - -# Path to athenz.conf file -athenzConfFile string - # Pass-through mode passthroughMode bool default=false diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java index 366dbb79f74..a4e85b9f63c 100644 --- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java +++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java @@ -6,16 +6,12 @@ import com.yahoo.jdisc.handler.ContentChannel; import com.yahoo.jdisc.handler.ReadableContentChannel; import com.yahoo.jdisc.handler.ResponseHandler; import com.yahoo.jdisc.http.filter.DiscFilterRequest; -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.athenz.api.AthenzPrincipal; -import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.athenz.api.NToken; import com.yahoo.security.KeyAlgorithm; import com.yahoo.security.KeyUtils; import com.yahoo.security.X509CertificateBuilder; -import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator; -import org.jetbrains.annotations.NotNull; -import org.junit.Before; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzPrincipal; +import com.yahoo.vespa.athenz.api.AthenzUser; import org.junit.Test; import javax.security.auth.x500.X500Principal; @@ -29,13 +25,10 @@ import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; import java.util.Objects; -import java.util.Set; import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED; import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA; -import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_RSA; import static java.util.Collections.emptyList; -import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.joining; import static org.hamcrest.CoreMatchers.containsString; @@ -52,40 +45,12 @@ import static org.mockito.Mockito.when; */ public class AthenzPrincipalFilterTest { - private static final NToken NTOKEN = new NToken("dummy"); - private static final String ATHENZ_PRINCIPAL_HEADER = "Athenz-Principal-Auth"; private static final AthenzIdentity IDENTITY = AthenzUser.fromUserId("bob"); private static final X509Certificate CERTIFICATE = createSelfSignedCertificate(IDENTITY); - private NTokenValidator validator; - - @Before - public void before() { - validator = mock(NTokenValidator.class); - } - - @Test - public void valid_ntoken_is_accepted() { - DiscFilterRequest request = createRequestMock(); - AthenzPrincipal principal = new AthenzPrincipal(IDENTITY, NTOKEN); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken()); - when(request.getClientCertificateChain()).thenReturn(emptyList()); - when(validator.validate(NTOKEN)).thenReturn(principal); - - AthenzPrincipalFilter filter = createFilter(false); - filter.filter(request, new ResponseHandlerMock()); - - assertAuthenticated(request, principal); - } - - private DiscFilterRequest createRequestMock() { - return mock(DiscFilterRequest.class); - } - @Test - public void missing_token_and_certificate_is_unauthorized() { + public void missing_certificate_is_unauthorized() { DiscFilterRequest request = createRequestMock(); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null); when(request.getClientCertificateChain()).thenReturn(emptyList()); ResponseHandlerMock responseHandler = new ResponseHandlerMock(); @@ -97,25 +62,8 @@ public class AthenzPrincipalFilterTest { } @Test - public void invalid_token_is_unauthorized() { - DiscFilterRequest request = createRequestMock(); - String errorMessage = "Invalid token"; - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken()); - when(request.getClientCertificateChain()).thenReturn(emptyList()); - when(validator.validate(NTOKEN)).thenThrow(new NTokenValidator.InvalidTokenException(errorMessage)); - - ResponseHandlerMock responseHandler = new ResponseHandlerMock(); - - AthenzPrincipalFilter filter = createFilter(false); - filter.filter(request, responseHandler); - - assertUnauthorized(request, responseHandler, errorMessage); - } - - @Test public void certificate_is_accepted() { DiscFilterRequest request = createRequestMock(); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null); when(request.getClientCertificateChain()).thenReturn(singletonList(CERTIFICATE)); ResponseHandlerMock responseHandler = new ResponseHandlerMock(); @@ -132,43 +80,10 @@ public class AthenzPrincipalFilterTest { verify(request).setAttribute(AthenzPrincipalFilter.RESULT_PRINCIPAL, expectedPrincipal); } - @Test - public void both_ntoken_and_certificate_is_accepted() { - DiscFilterRequest request = createRequestMock(); - AthenzPrincipal principalWithToken = new AthenzPrincipal(IDENTITY, NTOKEN); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken()); - when(request.getClientCertificateChain()).thenReturn(singletonList(CERTIFICATE)); - when(validator.validate(NTOKEN)).thenReturn(principalWithToken); - - ResponseHandlerMock responseHandler = new ResponseHandlerMock(); - - AthenzPrincipalFilter filter = createFilter(false); - filter.filter(request, responseHandler); - - assertAuthenticated(request, principalWithToken); - } - - @Test - public void conflicting_ntoken_and_certificate_is_unauthorized() { - DiscFilterRequest request = createRequestMock(); - AthenzUser conflictingIdentity = AthenzUser.fromUserId("mallory"); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(NTOKEN.getRawToken()); - when(request.getClientCertificateChain()) - .thenReturn(singletonList(createSelfSignedCertificate(conflictingIdentity))); - when(validator.validate(NTOKEN)).thenReturn(new AthenzPrincipal(IDENTITY)); - - ResponseHandlerMock responseHandler = new ResponseHandlerMock(); - - AthenzPrincipalFilter filter = createFilter(false); - filter.filter(request, responseHandler); - - assertUnauthorized(request, responseHandler, "Identity in principal token does not match x509 CN"); - } @Test public void no_response_produced_when_passthrough_mode_is_enabled() { DiscFilterRequest request = createRequestMock(); - when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null); when(request.getClientCertificateChain()).thenReturn(emptyList()); ResponseHandlerMock responseHandler = new ResponseHandlerMock(); @@ -179,8 +94,12 @@ public class AthenzPrincipalFilterTest { assertThat(responseHandler.response, nullValue()); } + private DiscFilterRequest createRequestMock() { + return mock(DiscFilterRequest.class); + } + private AthenzPrincipalFilter createFilter(boolean passthroughModeEnabled) { - return new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, passthroughModeEnabled); + return new AthenzPrincipalFilter(passthroughModeEnabled); } private static void assertUnauthorized(DiscFilterRequest request, ResponseHandlerMock responseHandler, String expectedMessageSubstring) { |