summaryrefslogtreecommitdiffstats
path: root/jdisc-security-filters
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2019-09-11 11:29:51 +0200
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2019-09-11 11:29:51 +0200
commitbf95a07154e11446ff4f2985e5b9951567a751a9 (patch)
treeeb11cbfb414ab737a7b30b1db714f5d360fece73 /jdisc-security-filters
parent9be8547f8720e71d4e80582a9ce5f720d2c22d20 (diff)
Remove NToken support from AthenzPrincipalFilter
Diffstat (limited to 'jdisc-security-filters')
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java40
-rw-r--r--jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def6
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java99
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) {