KNOX-1341 - Constrain cookies added to the HadoopAuthCookieStore
authorPhil Zampino <pzampino@apache.org>
Fri, 1 Jun 2018 01:26:34 +0000 (21:26 -0400)
committerPhil Zampino <pzampino@apache.org>
Wed, 6 Jun 2018 02:12:52 +0000 (22:12 -0400)
gateway-spi/src/main/java/org/apache/knox/gateway/SpiGatewayMessages.java
gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/HadoopAuthCookieStore.java
gateway-spi/src/test/java/org/apache/knox/gateway/dispatch/HadoopAuthCookieStoreTest.java

index 8718b1d..38e81be 100644 (file)
@@ -78,4 +78,7 @@ public interface SpiGatewayMessages {
   @Message( level = MessageLevel.DEBUG, text = "Accepting service cookie: {0}" )
   void acceptingServiceCookie( Cookie cookie );
 
+  @Message( level = MessageLevel.ERROR, text = "Error reading Kerberos login configuration {0} : {1}" )
+  void errorReadingKerberosLoginConfig(String fileName, @StackTrace(level=MessageLevel.ERROR) Exception e);
+
 }
index dcb7465..80a045b 100644 (file)
@@ -114,7 +114,7 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
           .build();
 
       builder = builder.setDefaultAuthSchemeRegistry(authSchemeRegistry)
-          .setDefaultCookieStore(new HadoopAuthCookieStore())
+          .setDefaultCookieStore(new HadoopAuthCookieStore(gatewayConfig))
           .setDefaultCredentialsProvider(credentialsProvider);
     } else {
       builder = builder.setDefaultCookieStore(new NoCookieStore());
index 1bab384..ea72abb 100644 (file)
  */
 package org.apache.knox.gateway.dispatch;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.Date;
+import java.util.Properties;
 
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.http.cookie.Cookie;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.cookie.BasicClientCookie;
 import org.apache.knox.gateway.SpiGatewayMessages;
+import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 
 public class HadoopAuthCookieStore extends BasicCookieStore {
 
   private static SpiGatewayMessages LOG = MessagesFactory.get( SpiGatewayMessages.class );
 
+  private GatewayConfig gatewayConfig = null;
+
+
+  HadoopAuthCookieStore(GatewayConfig config) {
+    this.gatewayConfig = config;
+  }
+
   @Override
   public void addCookie(Cookie cookie) {
     if (cookie.getName().equals("hadoop.auth") || cookie.getName().equals("hive.server2.auth")) {
-      Wrapper wrapper = new Wrapper( cookie );
-      LOG.acceptingServiceCookie( wrapper );
-      super.addCookie( wrapper );
+      // Only add the cookie if it's Knox's cookie
+      if (isKnoxCookie(gatewayConfig, cookie)) {
+        Wrapper wrapper = new Wrapper(cookie);
+        LOG.acceptingServiceCookie(wrapper);
+        super.addCookie(wrapper);
+      }
+    }
+  }
+
+
+  private static boolean isKnoxCookie(GatewayConfig config, Cookie cookie) {
+    boolean result = false;
+
+    if (cookie != null) {
+      String value = cookie.getValue();
+      if (value != null && !value.isEmpty()) {
+        String principal = null;
+
+        String[] cookieParts = value.split("&");
+        if (cookieParts.length > 1) {
+          String[] elementParts = cookieParts[1].split("=");
+          if (elementParts.length == 2) {
+            principal = elementParts[1];
+          }
+
+          if (principal != null) {
+            String krb5Config = config.getKerberosLoginConfig();
+            if (krb5Config != null && !krb5Config.isEmpty()) {
+              Properties p = new Properties();
+              try {
+                p.load(new FileInputStream(krb5Config));
+                String configuredPrincipal = p.getProperty("principal");
+                // Strip off enclosing quotes, if present
+                if (configuredPrincipal.startsWith("\"")) {
+                  configuredPrincipal = configuredPrincipal.substring(1, configuredPrincipal.length() - 1);
+                }
+                // Check if they're the same principal
+                result = principal.equals(configuredPrincipal);
+              } catch (IOException e) {
+                LOG.errorReadingKerberosLoginConfig(krb5Config, e);
+              }
+            }
+          }
+        }
+      }
     }
+
+    return result;
   }
 
   private static class Wrapper extends BasicClientCookie {
index 93070d5..b1685f3 100644 (file)
  */
 package org.apache.knox.gateway.dispatch;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.util.List;
 
 import org.apache.http.cookie.Cookie;
 import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.easymock.EasyMock;
 import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.*;
 
 public class HadoopAuthCookieStoreTest {
@@ -45,29 +48,146 @@ public class HadoopAuthCookieStoreTest {
     List<Cookie> cookies;
     Cookie cookie;
 
-    store = new HadoopAuthCookieStore();
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+    File krb5LoginConf = createTestKrb5LoginConfigFile("krb5JAASLogin",
+                                                       getTestKrb5LoginConf("knox/host.example.com.com@EXAMPLE.COM"));
+    assertNotNull(krb5LoginConf);
+    EasyMock.expect(gatewayConfig.getKerberosLoginConfig()).andReturn(krb5LoginConf.getAbsolutePath()).anyTimes();
+    EasyMock.replay(gatewayConfig);
+
+    store = new HadoopAuthCookieStore(gatewayConfig);
     store.addCookie( new BasicClientCookie( "hadoop.auth", rawValue ) );
     cookies = store.getCookies();
     cookie = cookies.get( 0 );
     assertThat( cookie.getValue(), is(quotedValue) );
 
-    store = new HadoopAuthCookieStore();
+    store = new HadoopAuthCookieStore(gatewayConfig);
     store.addCookie( new BasicClientCookie( "hadoop.auth", quotedValue ) );
     cookies = store.getCookies();
     cookie = cookies.get( 0 );
     assertThat( cookie.getValue(), is(quotedValue) );
 
-    store = new HadoopAuthCookieStore();
+    store = new HadoopAuthCookieStore(gatewayConfig);
     store.addCookie( new BasicClientCookie( "hadoop.auth", null ) );
     cookies = store.getCookies();
-    cookie = cookies.get( 0 );
-    assertThat( cookie.getValue(), is(nullValue()) );
+    assertNotNull(cookies);
+    assertTrue(cookies.isEmpty());
 
-    store = new HadoopAuthCookieStore();
+    store = new HadoopAuthCookieStore(gatewayConfig);
     store.addCookie( new BasicClientCookie( "hadoop.auth", "" ) );
     cookies = store.getCookies();
-    cookie = cookies.get( 0 );
-    assertThat( cookie.getValue(), is("") );
+    assertNotNull(cookies);
+    assertTrue(cookies.isEmpty());
+  }
+
+  @Test
+  public void testKnoxCookieInclusionDefaultUserAndPrincipal() {
+    doTestKnoxCookieInclusion("u=knox&p=knox/myhost.example.com@EXAMPLE.COM&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieInclusionDefaultUser() {
+    doTestKnoxCookieExclusion("u=knox&p=anotherUser/myhost.example.com@EXAMPLE.COM&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieInclusionDefaultPrincipal() {
+    doTestKnoxCookieInclusion("u=anotherUser&p=knox/myhost.example.com@EXAMPLE.COM&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieExclusionWrongUserAndPrincipal() {
+    doTestKnoxCookieExclusion("u=test&p=dummy/host@EXAMPLE.COM&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieExclusionWrongUserNoPrincipal() {
+    doTestKnoxCookieExclusion("u=test&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieInclusionDefaultUserAndCustomPrincipal() {
+    final String principal = "myTestPrincipal/test@EXAMPLE.COM";
+
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+    File krb5LoginConf = createTestKrb5LoginConfigFile("krb5Login", getTestKrb5LoginConf(principal));
+    assertNotNull(krb5LoginConf);
+    EasyMock.expect(gatewayConfig.getKerberosLoginConfig()).andReturn(krb5LoginConf.getAbsolutePath()).anyTimes();
+    EasyMock.replay(gatewayConfig);
+
+    doTestKnoxCookieInclusion(gatewayConfig,
+                              "u=knox&p=" + principal + "&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  @Test
+  public void testKnoxCookieInclusionDefaultUserAndMissingPrincipal() {
+    doTestKnoxCookieExclusion("u=knox&t=kerberos&e=1517900515610&s=HpSXUOhoXR/2wXrsgPz5lSbNuf8=");
+  }
+
+  private void doTestKnoxCookieInclusion(final String cookieValue) {
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+    File krb5LoginConf = createTestKrb5LoginConfigFile();
+    assertNotNull(krb5LoginConf);
+    EasyMock.expect(gatewayConfig.getKerberosLoginConfig()).andReturn(krb5LoginConf.getAbsolutePath()).anyTimes();
+    EasyMock.replay(gatewayConfig);
+
+    doTestKnoxCookieInclusion(gatewayConfig, cookieValue);
+  }
+
+  private void doTestKnoxCookieInclusion(final GatewayConfig gatewayConfig, final String cookieValue) {
+    HadoopAuthCookieStore store = new HadoopAuthCookieStore(gatewayConfig);
+    store.addCookie(new BasicClientCookie("hadoop.auth", cookieValue));
+    List<Cookie> cookies = store.getCookies();
+    assertNotNull(cookies);
+    assertFalse(cookies.isEmpty());
+    assertThat(cookies.get(0).getValue(), is("\"" + cookieValue + "\""));
+  }
+
+  private void doTestKnoxCookieExclusion(final String cookieValue) {
+    GatewayConfig gConf = EasyMock.createNiceMock(GatewayConfig.class);
+    EasyMock.replay(gConf);
+
+    HadoopAuthCookieStore store = new HadoopAuthCookieStore(gConf);
+    store.addCookie(new BasicClientCookie("hadoop.auth", cookieValue));
+    List<Cookie> cookies = store.getCookies();
+    assertNotNull(cookies);
+    assertTrue(cookies.isEmpty());
+  }
+
+  private static File createTestKrb5LoginConfigFile() {
+    return createTestKrb5LoginConfigFile("krb5JAASLogin", getTestKrb5LoginConf());
+  }
+
+  private static File createTestKrb5LoginConfigFile(String filename, String contents) {
+    File result = null;
+    try {
+      File f = File.createTempFile(filename, ".conf");
+      FileOutputStream out = new FileOutputStream(f);
+      out.write(contents.getBytes());
+      out.flush();
+      out.close();
+      result = f;
+    } catch (Exception e) {
+      //
+    }
+
+    return result;
+  }
+
+  private static String getTestKrb5LoginConf() {
+    return getTestKrb5LoginConf("knox/myhost.example.com@EXAMPLE.COM");
+  }
+
+  private static String getTestKrb5LoginConf(String principal) {
+    return "com.sun.security.jgss.initiate {\n" +
+           "com.sun.security.auth.module.Krb5LoginModule required\n" +
+           "renewTGT=false\n" +
+           "doNotPrompt=true\n" +
+           "useKeyTab=true\n" +
+           "keyTab=\"/etc/security/keytabs/knox.service.keytab\"\n" +
+          (principal != null ? "principal=\"" + principal + "\"\n" : "") +
+           "storeKey=true\n" +
+           "useTicketCache=false;";
   }
 
 }
\ No newline at end of file