You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/03/05 15:36:40 UTC
[knox] branch master updated: KNOX-474 - Improve Kerberos config
validation and diagnostics at startup (#62)
This is an automated email from the ASF dual-hosted git repository.
krisden 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 fe15d44 KNOX-474 - Improve Kerberos config validation and diagnostics at startup (#62)
fe15d44 is described below
commit fe15d443d1df4020c3cba94bb88cd5d907113f14
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Tue Mar 5 16:36:34 2019 +0100
KNOX-474 - Improve Kerberos config validation and diagnostics at startup (#62)
---
.../org/apache/knox/gateway/GatewayServer.java | 31 +++++++++++
.../zk/RemoteConfigurationRegistryJAASConfig.java | 21 ++++++++
.../RemoteConfigurationRegistryJAASConfigTest.java | 62 +++++++++++++++-------
.../config/GatewayConfigurationException.java | 37 +++++++++++++
4 files changed, 133 insertions(+), 18 deletions(-)
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
index 6539c68..e4a9e76 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
@@ -30,6 +30,7 @@ import org.apache.knox.gateway.audit.api.Auditor;
import org.apache.knox.gateway.audit.api.ResourceType;
import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.config.GatewayConfigurationException;
import org.apache.knox.gateway.config.impl.GatewayConfigImpl;
import org.apache.knox.gateway.deploy.DeploymentException;
import org.apache.knox.gateway.deploy.DeploymentFactory;
@@ -96,6 +97,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@@ -104,12 +106,14 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
@@ -152,6 +156,7 @@ public class GatewayServer {
}
GatewayConfig config = new GatewayConfigImpl();
if (config.isHadoopKerberosSecured()) {
+ validateKerberosConfig(config);
configureKerberosSecurity( config );
}
Map<String,String> options = new HashMap<>();
@@ -244,6 +249,32 @@ public class GatewayServer {
setSystemProperty(GatewayConfig.KRB5_USE_SUBJECT_CREDS_ONLY, "false");
}
+ private static void validateKerberosConfig(GatewayConfig config) throws GatewayConfigurationException {
+ final Set<String> errors = new HashSet<>();
+ if (config.isHadoopKerberosSecured()) {
+ if (config.getKerberosConfig() != null) {
+ final File krb5ConfFile = Paths.get(config.getKerberosConfig()).toFile();
+ checkIfFileExistsAndCanBeRead(krb5ConfFile, GatewayConfig.KRB5_CONFIG, errors);
+ }
+
+ if (config.getKerberosLoginConfig() != null) {
+ final File loginConfigFile = Paths.get(config.getKerberosLoginConfig()).toFile();
+ checkIfFileExistsAndCanBeRead(loginConfigFile, GatewayConfig.KRB5_LOGIN_CONFIG, errors);
+ }
+ }
+ if (!errors.isEmpty()) {
+ throw new GatewayConfigurationException(errors);
+ }
+ }
+
+ private static void checkIfFileExistsAndCanBeRead(File fileToBeChecked, String propertyName, Set<String> errors) {
+ if (!fileToBeChecked.exists()) {
+ errors.add(propertyName + " is set to a non-existing file: " + fileToBeChecked);
+ } else if (!fileToBeChecked.canRead()) {
+ errors.add(propertyName + " is set to a non-readable file: " + fileToBeChecked);
+ }
+ }
+
private static void setSystemProperty(String name, String value) {
System.setProperty(name, value);
log.logSysProp(name, System.getProperty(name));
diff --git a/gateway-service-remoteconfig/src/main/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfig.java b/gateway-service-remoteconfig/src/main/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfig.java
index d702d17..9e138a9 100644
--- a/gateway-service-remoteconfig/src/main/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfig.java
+++ b/gateway-service-remoteconfig/src/main/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfig.java
@@ -26,6 +26,9 @@ import org.apache.knox.gateway.services.security.AliasServiceException;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
+
+import java.io.File;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -37,6 +40,7 @@ import java.util.Map;
class RemoteConfigurationRegistryJAASConfig extends Configuration {
static final String JAAS_CONFIG_ERRROR_PREFIX = "Error while getting secure configuration. This error usually indicates an issue within the supplied JAAS configuration";
+ static final String JGSS_LOGIN_MODUDLE = "com.sun.security.jgss.initiate";
// Underlying SASL mechanisms supported
enum SASLMechanism {
@@ -72,6 +76,8 @@ class RemoteConfigurationRegistryJAASConfig extends Configuration {
throw new ConfigurationException(message, e);
}
+ validateKeytabFile();
+
this.aliasService = aliasService;
// Populate context entries
@@ -89,6 +95,21 @@ class RemoteConfigurationRegistryJAASConfig extends Configuration {
}
}
+ private void validateKeytabFile() {
+ final AppConfigurationEntry[] appConfigurationEntries = delegate.getAppConfigurationEntry(JGSS_LOGIN_MODUDLE);
+ if (appConfigurationEntries != null) {
+ for (AppConfigurationEntry appConfigurationEntry : appConfigurationEntries) {
+ String keytabFileName = (String) appConfigurationEntry.getOptions().get("keyTab");
+ if (keytabFileName != null && keytabFileName.length() > 0) {
+ final File keytabFile = Paths.get(keytabFileName).toFile();
+ if (!keytabFile.exists() || !keytabFile.canRead()) {
+ throw new ConfigurationException("The specified keytab file " + keytabFile.getAbsolutePath() + " is either non-existing or cannot be read!");
+ }
+ }
+ }
+ }
+ }
+
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] result;
diff --git a/gateway-service-remoteconfig/src/test/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java b/gateway-service-remoteconfig/src/test/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
index e80c823..8685808 100644
--- a/gateway-service-remoteconfig/src/test/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
+++ b/gateway-service-remoteconfig/src/test/java/org/apache/knox/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
@@ -30,6 +30,7 @@ import org.junit.rules.TemporaryFolder;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
+import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.startsWith;
import java.io.File;
@@ -91,8 +92,8 @@ public class RemoteConfigurationRegistryJAASConfigTest {
final String ENTRY_NAME = "my_kerberos_context";
final String PRINCIPAL = "myIdentity";
- File dummyKeyTab = File.createTempFile("my_context", "keytab");
- registryConfigs.add(createKerberosConfig(ENTRY_NAME, PRINCIPAL, dummyKeyTab.getAbsolutePath()));
+ final String dummyKeyTab = createTempKeytabFile("dummyKeytab1");
+ registryConfigs.add(createKerberosConfig(ENTRY_NAME, PRINCIPAL, dummyKeyTab));
try {
RemoteConfigurationRegistryJAASConfig jaasConfig =
@@ -105,7 +106,7 @@ public class RemoteConfigurationRegistryJAASConfigTest {
validateKerberosContext(jaasConfig,
ENTRY_NAME,
PRINCIPAL,
- dummyKeyTab.getAbsolutePath(),
+ dummyKeyTab,
true,
false);
@@ -128,8 +129,8 @@ public class RemoteConfigurationRegistryJAASConfigTest {
EasyMock.expect(aliasService.getPasswordFromAliasForGateway(DIGEST_PWD_ALIAS)).andReturn(DIGEST_PWD.toCharArray()).anyTimes();
EasyMock.replay(aliasService);
- File dummyKeyTab = File.createTempFile("my_context", "keytab");
- registryConfigs.add(createKerberosConfig(KERBEROS_ENTRY_NAME, KERBEROS_PRINCIPAL, dummyKeyTab.getAbsolutePath()));
+ final String dummyKeyTab = createTempKeytabFile("dummyKeytab2");
+ registryConfigs.add(createKerberosConfig(KERBEROS_ENTRY_NAME, KERBEROS_PRINCIPAL, dummyKeyTab));
registryConfigs.add(createDigestConfig(DIGEST_ENTRY_NAME, DIGEST_PRINCIPAL, DIGEST_PWD_ALIAS));
try {
@@ -143,7 +144,7 @@ public class RemoteConfigurationRegistryJAASConfigTest {
validateKerberosContext(jaasConfig,
KERBEROS_ENTRY_NAME,
KERBEROS_PRINCIPAL,
- dummyKeyTab.getAbsolutePath(),
+ dummyKeyTab,
true,
false);
@@ -195,13 +196,14 @@ public class RemoteConfigurationRegistryJAASConfigTest {
RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, null);
} finally {
System.clearProperty(GatewayConfig.KRB5_LOGIN_CONFIG);
+ Configuration.setConfiguration(null);
}
}
@Test
public void shouldRaiseAnErrorWithMeaningfulErrorMessageIfAuthLoginConfigCannotBeParsed() throws Exception {
final List<RemoteConfigurationRegistryConfig> registryConfigs = new ArrayList<>();
- final String jaasConfigFilePath = writeInvalidJaasConf();
+ final String jaasConfigFilePath = writeInvalidJaasConf(false, "jaasConfWithInvalidKeytab", createTempKeytabFile("invalidKeytab"));
System.setProperty(GatewayConfig.KRB5_LOGIN_CONFIG, jaasConfigFilePath);
expectedException.expect(ConfigurationException.class);
@@ -211,20 +213,44 @@ public class RemoteConfigurationRegistryJAASConfigTest {
RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, null);
} finally {
System.clearProperty(GatewayConfig.KRB5_LOGIN_CONFIG);
+ Configuration.setConfiguration(null);
}
}
- private String writeInvalidJaasConf() throws IOException {
- final File jaasConfigFile = testFolder.newFile("jaas.conf");
- final String jaasConfig = "com.sun.security.jgss.initiate {" +
- "com.sun.security.auth.module.Krb5LoginModule required" +
- "renewTGT=false" +
- "doNotPrompt=true" +
- "useKeyTab=true" +
- "keyTab=/etc/security/keytabs/knox.service.keytab" + //note the missing quotes; it should be keyTab="/etc/security/keytabs/knox.service.keytab"
- "principal=\"knox/myHost@myRealm\"" +
- "storeKey=true" +
- "useTicketCache=false; " +
+ @Test
+ public void shouldRaiseAnErrorWithMeaningfulErrorMessageIfReferencedKeytabFileDoesNotExists() throws Exception {
+ final String jaasConfigFilePath = writeInvalidJaasConf(true, "jaasConfWithMissingKeytab", "nonExistingKeytabFile");
+ System.setProperty(GatewayConfig.KRB5_LOGIN_CONFIG, jaasConfigFilePath);
+
+ expectedException.expect(ConfigurationException.class);
+ expectedException.expectMessage(startsWith("The specified keytab file"));
+ expectedException.expectMessage(endsWith("is either non-existing or cannot be read!"));
+
+ try {
+ RemoteConfigurationRegistryJAASConfig.configure(new ArrayList<>(), null);
+ } finally {
+ System.clearProperty(GatewayConfig.KRB5_LOGIN_CONFIG);
+ Configuration.setConfiguration(null);
+ }
+ }
+
+ private String createTempKeytabFile(String keytabFileName) throws IOException {
+ final File keytabFile = testFolder.newFile(keytabFileName);
+ FileUtils.writeStringToFile(keytabFile, "dummyBinaryContent", StandardCharsets.UTF_8);
+ return keytabFile.getAbsolutePath();
+ }
+
+ private String writeInvalidJaasConf(boolean valid, String jaasConfFileName, String keytabFileName) throws IOException {
+ final File jaasConfigFile = testFolder.newFile(jaasConfFileName);
+ final String jaasConfig = "com.sun.security.jgss.initiate {\n" +
+ "com.sun.security.auth.module.Krb5LoginModule required\n" +
+ "renewTGT=false\n" +
+ "doNotPrompt=true\n" +
+ "useKeyTab=true\n" +
+ "keyTab=" + (valid ? "\"" : "" ) + keytabFileName + (valid ? "\"" : "" ) + "\n" + //note the missing quotes in case valid=false; it should be keyTab="/etc/security/keytabs/knox.service.keytab"
+ "principal=\"knox/myHost@myRealm\"\n" +
+ "storeKey=true\n" +
+ "useTicketCache=false;\n" +
"};";
FileUtils.writeStringToFile(jaasConfigFile, jaasConfig, StandardCharsets.UTF_8);
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfigurationException.java b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfigurationException.java
new file mode 100644
index 0000000..f444b8c
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfigurationException.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.config;
+
+import java.util.Set;
+
+@SuppressWarnings("serial")
+public class GatewayConfigurationException extends Exception {
+
+ public GatewayConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public GatewayConfigurationException(String message) {
+ super(message);
+ }
+
+ public GatewayConfigurationException(Set<String> configurationErrors) {
+ this("Found configurations errors:" + System.lineSeparator() + String.join(System.lineSeparator(), configurationErrors));
+ }
+
+}