You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2017/09/28 23:28:34 UTC
knox git commit: KNOX-1072 - Add Client Cert Required Capability to
KnoxToken
Repository: knox
Updated Branches:
refs/heads/master 145ed5dd6 -> 7b4755d57
KNOX-1072 - Add Client Cert Required Capability to KnoxToken
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/7b4755d5
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/7b4755d5
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/7b4755d5
Branch: refs/heads/master
Commit: 7b4755d57c8998d1aed62c100124b8a94a3427db
Parents: 145ed5d
Author: Larry McCay <lm...@hortonworks.com>
Authored: Thu Sep 28 19:27:38 2017 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Thu Sep 28 19:27:54 2017 -0400
----------------------------------------------------------------------
.../service/knoxtoken/TokenResource.java | 35 +++++
.../knoxtoken/TokenServiceResourceTest.java | 144 +++++++++++++++++++
2 files changed, 179 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/7b4755d5/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
index 43dd526..9d8bae3 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
@@ -19,6 +19,7 @@ package org.apache.hadoop.gateway.service.knoxtoken;
import java.io.IOException;
import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
@@ -54,12 +55,16 @@ public class TokenResource {
private static final String TOKEN_AUDIENCES_PARAM = "knox.token.audiences";
private static final String TOKEN_TARGET_URL = "knox.token.target.url";
private static final String TOKEN_CLIENT_DATA = "knox.token.client.data";
+ private static final String TOKEN_CLIENT_CERT_REQUIRED = "knox.token.client.cert.required";
+ private static final String TOKEN_ALLOWED_PRINCIPALS = "knox.token.allowed.principals";
static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
private long tokenTTL = 30000l;
private List<String> targetAudiences = new ArrayList<>();
private String tokenTargetUrl = null;
private Map<String,Object> tokenClientDataMap = null;
+ private ArrayList<String> allowedDNs = new ArrayList<>();
+ private boolean clientCertRequired = false;
@Context
HttpServletRequest request;
@@ -81,6 +86,17 @@ public class TokenResource {
}
}
+ String clientCert = context.getInitParameter(TOKEN_CLIENT_CERT_REQUIRED);
+ clientCertRequired = "true".equals(clientCert);
+
+ String principals = context.getInitParameter(TOKEN_ALLOWED_PRINCIPALS);
+ if (principals != null) {
+ String[] dns = principals.split(";");
+ for (int i = 0; i < dns.length; i++) {
+ allowedDNs.add(dns[i]);
+ }
+ }
+
String ttl = context.getInitParameter(TOKEN_TTL_PARAM);
if (ttl != null) {
try {
@@ -113,7 +129,26 @@ public class TokenResource {
return getAuthenticationToken();
}
+ private X509Certificate extractCertificate(HttpServletRequest req) {
+ X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
+ if (null != certs && certs.length > 0) {
+ return certs[0];
+ }
+ return null;
+ }
+
private Response getAuthenticationToken() {
+ if (clientCertRequired) {
+ X509Certificate cert = extractCertificate(request);
+ if (cert != null) {
+ if (!allowedDNs.contains(cert.getSubjectDN().getName())) {
+ return Response.status(403).entity("{ \"Unable to get token - untrusted client cert.\" }").build();
+ }
+ }
+ else {
+ return Response.status(403).entity("{ \"Unable to get token - client cert required.\" }").build();
+ }
+ }
GatewayServices services = (GatewayServices) request.getServletContext()
.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
http://git-wip-us.apache.org/repos/asf/knox/blob/7b4755d5/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
index bddd13d..b4e51e6 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.gateway.service.knoxtoken;
+import org.apache.hadoop.gateway.security.PrimaryPrincipal;
import org.apache.hadoop.gateway.service.knoxtoken.TokenResource;
import org.apache.hadoop.gateway.services.GatewayServices;
import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
@@ -49,6 +50,7 @@ import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
@@ -203,6 +205,148 @@ public class TokenServiceResourceTest {
assertTrue(audiences.contains("recipient2"));
}
+ @Test
+ public void testValidClientCert() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
+ EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
+ ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
+ certArrayList.add(trustedCertMock);
+ X509Certificate[] certs = {};
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response, trustedCertMock);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
+ JWTToken parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+ }
+
+ @Test
+ public void testValidClientCertWrongUser() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
+ EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
+ ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
+ certArrayList.add(trustedCertMock);
+ X509Certificate[] certs = {};
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response, trustedCertMock);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(403, retResponse.getStatus());
+ }
+
+ @Test
+ public void testMissingClientCert() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(null).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(403, retResponse.getStatus());
+ }
+
private String getTagValue(String token, String tagName) {
String searchString = tagName + "\":";
String value = token.substring(token.indexOf(searchString) + searchString.length());