knox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pzamp...@apache.org
Subject [knox] branch master updated: KNOX-1832 - KnoxSession handling of JAAS config for kerberos auth is not deterministic
Date Wed, 20 Mar 2019 16:41:37 GMT
This is an automated email from the ASF dual-hosted git repository.

pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a504c7  KNOX-1832 - KnoxSession handling of JAAS config for kerberos auth is not
deterministic
8a504c7 is described below

commit 8a504c7c7f8e6a07ded65c671b64ccd702f47668
Author: pzampino <pzampino@apache.org>
AuthorDate: Wed Mar 20 12:03:39 2019 -0400

    KNOX-1832 - KnoxSession handling of JAAS config for kerberos auth is not deterministic
---
 .../apache/knox/gateway/shell/ClientContext.java   |   2 +-
 .../org/apache/knox/gateway/shell/KnoxSession.java | 140 ++++++++++++++++++---
 .../knox/gateway/shell/KnoxShellMessages.java      |  30 ++++-
 gateway-shell/src/main/resources/jaas.conf         |   7 +-
 .../apache/knox/gateway/shell/KnoxSessionTest.java |  68 ++++++++++
 5 files changed, 225 insertions(+), 22 deletions(-)

diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/ClientContext.java
b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/ClientContext.java
index 3bb1339..dde0ac7 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/ClientContext.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/ClientContext.java
@@ -240,7 +240,7 @@ public class ClientContext {
     }
 
     public boolean debug() {
-      return configuration.getBoolean("debug");
+      return configuration.getBoolean("debug", false);
     }
 
   }
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSession.java b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSession.java
index 0033e02..3179642 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSession.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxSession.java
@@ -57,6 +57,8 @@ import org.apache.knox.gateway.shell.util.ClientTrustStoreHelper;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLContext;
 import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
@@ -66,10 +68,12 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Constructor;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.nio.file.Files;
+import java.security.AccessController;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -100,6 +104,8 @@ public class KnoxSession implements Closeable {
   private static final KnoxShellMessages LOG = MessagesFactory.get(KnoxShellMessages.class);
   private boolean isKerberos;
 
+  private URL jaasConfigURL;
+
   String base;
   HttpHost host;
   CloseableHttpClient client;
@@ -118,7 +124,7 @@ public class KnoxSession implements Closeable {
   protected KnoxSession() throws KnoxShellException, URISyntaxException {
   }
 
-  public KnoxSession( final ClientContext clientContext) throws KnoxShellException, URISyntaxException
{
+  public KnoxSession(final ClientContext clientContext) throws KnoxShellException, URISyntaxException
{
     this.executor = Executors.newCachedThreadPool();
     this.base = clientContext.url();
 
@@ -199,7 +205,22 @@ public class KnoxSession implements Closeable {
    */
   public static KnoxSession kerberosLogin(final String url)
       throws URISyntaxException {
-    return kerberosLogin(url, "", "", false);
+    return kerberosLogin(url, false);
+  }
+
+  /**
+   * Support kerberos authentication.
+   * This method assumed kinit has already been called
+   * and the token is persisted on disk.
+   * @param url Gateway url
+   * @param debug enable debug messages
+   * @return KnoxSession
+   * @throws URISyntaxException exception in case of malformed url
+   * @since 1.3.0
+   */
+  public static KnoxSession kerberosLogin(final String url, boolean debug)
+      throws URISyntaxException {
+    return kerberosLogin(url, "", "", debug);
   }
 
   public static KnoxSession loginInsecure(String url, String username, String password) throws
URISyntaxException {
@@ -264,12 +285,24 @@ public class KnoxSession implements Closeable {
       }
 
       if (!StringUtils.isBlank(clientContext.kerberos().jaasConf())) {
-        System.setProperty("java.security.auth.login.config",
-            clientContext.kerberos().jaasConf());
-      } else {
-        final URL url = getClass().getResource(DEFAULT_JAAS_FILE);
-        System.setProperty("java.security.auth.login.config",
-            url.toExternalForm());
+        File f = new File(clientContext.kerberos().jaasConf());
+        if (f.exists()) {
+          try {
+            jaasConfigURL = f.getCanonicalFile().toURI().toURL();
+            LOG.jaasConfigurationLocation(jaasConfigURL.toExternalForm());
+          } catch (IOException e) {
+            LOG.failedToLocateJAASConfiguration(e.getMessage());
+          }
+        } else {
+          LOG.jaasConfigurationDoesNotExist(f.getAbsolutePath());
+        }
+      }
+
+      // Fall back to the default JAAS config
+      if (jaasConfigURL == null) {
+        LOG.usingDefaultJAASConfiguration();
+        jaasConfigURL = getClass().getResource(DEFAULT_JAAS_FILE);
+        LOG.jaasConfigurationLocation(jaasConfigURL.toExternalForm());
       }
 
       if (clientContext.kerberos().debug()) {
@@ -426,7 +459,18 @@ public class KnoxSession implements Closeable {
     if (isKerberos) {
       LoginContext lc;
       try {
-        lc = new LoginContext(JGSS_LOGIN_MOUDLE, new TextCallbackHandler());
+        Configuration jaasConf;
+        try {
+          jaasConf = new JAASClientConfig(jaasConfigURL);
+        } catch (Exception e) {
+          LOG.failedToLoadJAASConfiguration(jaasConfigURL.toExternalForm());
+          throw new KnoxShellException(e.toString(), e);
+        }
+
+        lc = new LoginContext(JGSS_LOGIN_MOUDLE,
+                              Subject.getSubject(AccessController.getContext()),
+                              new TextCallbackHandler(),
+                              jaasConf);
         lc.login();
         return Subject.doAs(lc.getSubject(),
             (PrivilegedAction<CloseableHttpResponse>) () -> {
@@ -447,9 +491,7 @@ public class KnoxSession implements Closeable {
       } catch (final LoginException e) {
         throw new KnoxShellException(e.toString(), e);
       }
-
     } else {
-
       CloseableHttpResponse response = client.execute(host, request, context);
       if (response.getStatusLine().getStatusCode() < 400) {
         return response;
@@ -458,7 +500,6 @@ public class KnoxSession implements Closeable {
             response);
       }
     }
-
   }
 
   public <T> Future<T> executeLater( Callable<T> callable ) {
@@ -519,8 +560,79 @@ public class KnoxSession implements Closeable {
 
   @Override
   public String toString() {
-    final StringBuilder sb = new StringBuilder(
-        "KnoxSession{base='").append(base).append("\'}");
+    final StringBuilder sb = new StringBuilder("KnoxSession{base='");
+    sb.append(base).append("\'}");
     return sb.toString();
   }
+
+
+  private static final class JAASClientConfig extends Configuration {
+
+    private static final Configuration baseConfig = Configuration.getConfiguration();
+
+    private Configuration configFile;
+
+    JAASClientConfig(URL configFileURL) throws Exception {
+      if (configFileURL != null) {
+        this.configFile = ConfigurationFactory.create(configFileURL.toURI());
+      }
+    }
+
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+      AppConfigurationEntry[] result = null;
+
+      // Try the config file if it exists
+      if (configFile != null) {
+        result = configFile.getAppConfigurationEntry(name);
+      }
+
+      // If the entry isn't there, delegate to the base configuration
+      if (result == null) {
+        result = baseConfig.getAppConfigurationEntry(name);
+      }
+
+      return result;
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidAccessibilityAlteration")
+  private static class ConfigurationFactory {
+
+    private static final Class implClazz;
+    static {
+      // Oracle and OpenJDK use the Sun implementation
+      String implName = System.getProperty("java.vendor").contains("IBM") ?
+                                "com.ibm.security.auth.login.ConfigFile" : "com.sun.security.auth.login.ConfigFile";
+
+      LOG.usingJAASConfigurationFileImplementation(implName);
+      Class clazz = null;
+      try {
+        clazz = Class.forName(implName, false, Thread.currentThread().getContextClassLoader());
+      } catch (ClassNotFoundException e) {
+        LOG.failedToLoadJAASConfigurationFileImplementation(implName, e.getLocalizedMessage());
+      }
+
+      implClazz = clazz;
+    }
+
+    static Configuration create(URI uri) {
+      Configuration config = null;
+
+      if (implClazz != null) {
+        try {
+          Constructor ctor = implClazz.getDeclaredConstructor(URI.class);
+          config = (Configuration) ctor.newInstance(uri);
+        } catch (Exception e) {
+          LOG.failedToInstantiateJAASConfigurationFileImplementation(implClazz.getCanonicalName(),
+                                                                     e.getLocalizedMessage());
+        }
+      } else {
+        LOG.noJAASConfigurationFileImplementation();
+      }
+
+      return config;
+    }
+  }
+
 }
diff --git a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellMessages.java
b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellMessages.java
index f3671b2..07f5fa6 100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellMessages.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/KnoxShellMessages.java
@@ -26,6 +26,34 @@ import org.apache.knox.gateway.i18n.messages.StackTrace;
 
 @Messages(logger="org.apache.knox.gateway.shell")
 public interface KnoxShellMessages {
-  @Message( level = MessageLevel.WARN, text = "Unable to load provided PEM encoded trusted
cert - falling through for other truststores: {0}" )
+  @Message( level = MessageLevel.WARN, text = "Unable to create provided PEM encoded trusted
cert - falling through for other truststores: {0}" )
   void unableToLoadProvidedPEMEncodedTrustedCert(@StackTrace( level = MessageLevel.DEBUG
) IOException e);
+
+  @Message( level = MessageLevel.DEBUG, text = "Using JAAS configuration file implementation:
{0}" )
+  void usingJAASConfigurationFileImplementation(String implName);
+
+  @Message( level = MessageLevel.ERROR, text = "Failed to create JAAS configuration file
implementation {0}: {1}" )
+  void failedToLoadJAASConfigurationFileImplementation(String implName, String error);
+
+  @Message( level = MessageLevel.ERROR, text = "Failed to instantiate JAAS configuration
file implementation {0}: {1}" )
+  void failedToInstantiateJAASConfigurationFileImplementation(String implName, String error);
+
+  @Message( level = MessageLevel.ERROR, text = "No JAAS configuration file implementation
is available" )
+  void noJAASConfigurationFileImplementation();
+
+  @Message( level = MessageLevel.ERROR, text = "Failed to create the JAAS configuration:
{0}" )
+  void failedToLoadJAASConfiguration(String configFileName);
+
+  @Message( level = MessageLevel.ERROR, text = "Failed to locate the specified JAAS configuration:
{0}" )
+  void failedToLocateJAASConfiguration(String message);
+
+  @Message( level = MessageLevel.ERROR, text = "The specified JAAS configuration does not
exist: {0}" )
+  void jaasConfigurationDoesNotExist(String jaasConf);
+
+  @Message( level = MessageLevel.INFO, text = "Using default JAAS configuration" )
+  void usingDefaultJAASConfiguration();
+
+  @Message( level = MessageLevel.DEBUG, text = "JAAS configuration: {0}" )
+  void jaasConfigurationLocation(String location);
+
 }
diff --git a/gateway-shell/src/main/resources/jaas.conf b/gateway-shell/src/main/resources/jaas.conf
index 330ae7a..7ff583b 100644
--- a/gateway-shell/src/main/resources/jaas.conf
+++ b/gateway-shell/src/main/resources/jaas.conf
@@ -19,10 +19,5 @@ com.sun.security.jgss.initiate {
     com.sun.security.auth.module.Krb5LoginModule required
     useTicketCache=true
     doNotPrompt=true
-    renewTGT=true
-    debug=true;
-    //ticketCache="/tmp/krb5cc_502"
-    //useKeyTab=true
-    //principal="guest/hostname.example.com@EXAMPLE.COM"
-    //keyTab="/Users/username/spnego.service.keytab";
+    renewTGT=true;
 };
\ No newline at end of file
diff --git a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxSessionTest.java
b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxSessionTest.java
index 56e5712..0c275b4 100644
--- a/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxSessionTest.java
+++ b/gateway-shell/src/test/java/org/apache/knox/gateway/shell/KnoxSessionTest.java
@@ -17,11 +17,21 @@
  */
 package org.apache.knox.gateway.shell;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
 public class KnoxSessionTest {
   public static final String PEM = "MIICOjCCAaOgAwIBAgIJAN5kp1oW3Up8MA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNVBAYTAlVTMQ0w\n"
       + "CwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ8wDQYDVQQKEwZIYWRvb3AxDTALBgNVBAsTBFRl\n"
@@ -72,4 +82,62 @@ public class KnoxSessionTest {
       fail("Should have been able to parse cert with BEGIN and END Certificate delimiters.");
     }
   }
+
+  /**
+   * Validate that the jaasConf option is applied when specified for a kerberos KnoxSession
login.
+   */
+  @Test
+  public void testJAASConfigOption() {
+    final String testJaasConf = "/etc/knoxsessiontest-jaas.conf";
+
+    final Logger logger = Logger.getLogger("org.apache.knox.gateway.shell");
+    final Level originalLevel = logger.getLevel();
+    logger.setLevel(Level.FINEST);
+    LogHandler logCapture = new LogHandler();
+    logger.addHandler(logCapture);
+
+    try {
+      ClientContext context = ClientContext.with("https://localhost:8443/gateway/dt")
+          .kerberos()
+          .enable(true)
+          .jaasConf(testJaasConf)
+          .end();
+      assertNotNull(context);
+      assertEquals(context.kerberos().jaasConf(), testJaasConf);
+
+      try {
+        KnoxSession.login(context).executeNow(null);
+      } catch (Exception e) {
+        // Expected because the HTTP request is null, which is irrelevant for this test
+      }
+
+      assertFalse(logCapture.logMessages.isEmpty());
+      assertEquals("The specified JAAS configuration does not exist: " + testJaasConf, logCapture.logMessages.get(0));
+      assertEquals("Using default JAAS configuration", logCapture.logMessages.get(1));
+      assertTrue(logCapture.logMessages.get(2).startsWith("JAAS configuration: "));
+      assertTrue(logCapture.logMessages.get(2).endsWith("jaas.conf"));
+    } finally {
+      logger.removeHandler(logCapture);
+      logger.setLevel(originalLevel);
+    }
+  }
+
+  private static class LogHandler extends Handler {
+
+    List<String> logMessages = new ArrayList<>();
+
+    @Override
+    public void publish(LogRecord record) {
+      logMessages.add(record.getMessage());
+    }
+
+    @Override
+    public void flush() {
+    }
+
+    @Override
+    public void close() throws SecurityException {
+    }
+  }
+
 }


Mime
View raw message