Merge r1617555 from trunk. YARN-2373. Changed WebAppUtils to use Configuration#getPassword for accessing SSL passwords. Contributed by Larry McCay
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1617557 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
70b2499b4b
commit
ebf92e44f9
|
@ -98,6 +98,9 @@ Release 2.6.0 - UNRELEASED
|
||||||
YARN-2138. Cleaned up notifyDone* APIs in RMStateStore. (Varun Saxena via
|
YARN-2138. Cleaned up notifyDone* APIs in RMStateStore. (Varun Saxena via
|
||||||
jianhe)
|
jianhe)
|
||||||
|
|
||||||
|
YARN-2373. Changed WebAppUtils to use Configuration#getPassword for
|
||||||
|
accessing SSL passwords. (Larry McCay via jianhe)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -19,12 +19,12 @@ package org.apache.hadoop.yarn.webapp.util;
|
||||||
|
|
||||||
import static org.apache.hadoop.yarn.util.StringHelper.PATH_JOINER;
|
import static org.apache.hadoop.yarn.util.StringHelper.PATH_JOINER;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
||||||
import org.apache.hadoop.classification.InterfaceStability.Evolving;
|
import org.apache.hadoop.classification.InterfaceStability.Evolving;
|
||||||
|
@ -40,6 +40,12 @@ import org.apache.hadoop.yarn.util.RMHAUtils;
|
||||||
@Private
|
@Private
|
||||||
@Evolving
|
@Evolving
|
||||||
public class WebAppUtils {
|
public class WebAppUtils {
|
||||||
|
public static final String WEB_APP_TRUSTSTORE_PASSWORD_KEY =
|
||||||
|
"ssl.server.truststore.password";
|
||||||
|
public static final String WEB_APP_KEYSTORE_PASSWORD_KEY =
|
||||||
|
"ssl.server.keystore.password";
|
||||||
|
public static final String WEB_APP_KEY_PASSWORD_KEY =
|
||||||
|
"ssl.server.keystore.keypassword";
|
||||||
public static final String HTTPS_PREFIX = "https://";
|
public static final String HTTPS_PREFIX = "https://";
|
||||||
public static final String HTTP_PREFIX = "http://";
|
public static final String HTTP_PREFIX = "http://";
|
||||||
|
|
||||||
|
@ -274,21 +280,56 @@ public class WebAppUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the SSL keystore / truststore into the HttpServer builder.
|
* Load the SSL keystore / truststore into the HttpServer builder.
|
||||||
|
* @param builder the HttpServer2.Builder to populate with ssl config
|
||||||
*/
|
*/
|
||||||
public static HttpServer2.Builder loadSslConfiguration(
|
public static HttpServer2.Builder loadSslConfiguration(
|
||||||
HttpServer2.Builder builder) {
|
HttpServer2.Builder builder) {
|
||||||
Configuration sslConf = new Configuration(false);
|
return loadSslConfiguration(builder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the SSL keystore / truststore into the HttpServer builder.
|
||||||
|
* @param builder the HttpServer2.Builder to populate with ssl config
|
||||||
|
* @param sslConf the Configuration instance to use during loading of SSL conf
|
||||||
|
*/
|
||||||
|
public static HttpServer2.Builder loadSslConfiguration(
|
||||||
|
HttpServer2.Builder builder, Configuration sslConf) {
|
||||||
|
if (sslConf == null) {
|
||||||
|
sslConf = new Configuration(false);
|
||||||
|
}
|
||||||
boolean needsClientAuth = YarnConfiguration.YARN_SSL_CLIENT_HTTPS_NEED_AUTH_DEFAULT;
|
boolean needsClientAuth = YarnConfiguration.YARN_SSL_CLIENT_HTTPS_NEED_AUTH_DEFAULT;
|
||||||
sslConf.addResource(YarnConfiguration.YARN_SSL_SERVER_RESOURCE_DEFAULT);
|
sslConf.addResource(YarnConfiguration.YARN_SSL_SERVER_RESOURCE_DEFAULT);
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.needsClientAuth(needsClientAuth)
|
.needsClientAuth(needsClientAuth)
|
||||||
.keyPassword(sslConf.get("ssl.server.keystore.keypassword"))
|
.keyPassword(getPassword(sslConf, WEB_APP_KEY_PASSWORD_KEY))
|
||||||
.keyStore(sslConf.get("ssl.server.keystore.location"),
|
.keyStore(sslConf.get("ssl.server.keystore.location"),
|
||||||
sslConf.get("ssl.server.keystore.password"),
|
getPassword(sslConf, WEB_APP_KEYSTORE_PASSWORD_KEY),
|
||||||
sslConf.get("ssl.server.keystore.type", "jks"))
|
sslConf.get("ssl.server.keystore.type", "jks"))
|
||||||
.trustStore(sslConf.get("ssl.server.truststore.location"),
|
.trustStore(sslConf.get("ssl.server.truststore.location"),
|
||||||
sslConf.get("ssl.server.truststore.password"),
|
getPassword(sslConf, WEB_APP_TRUSTSTORE_PASSWORD_KEY),
|
||||||
sslConf.get("ssl.server.truststore.type", "jks"));
|
sslConf.get("ssl.server.truststore.type", "jks"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leverages the Configuration.getPassword method to attempt to get
|
||||||
|
* passwords from the CredentialProvider API before falling back to
|
||||||
|
* clear text in config - if falling back is allowed.
|
||||||
|
* @param conf Configuration instance
|
||||||
|
* @param alias name of the credential to retreive
|
||||||
|
* @return String credential value or null
|
||||||
|
*/
|
||||||
|
static String getPassword(Configuration conf, String alias) {
|
||||||
|
String password = null;
|
||||||
|
try {
|
||||||
|
char[] passchars = conf.getPassword(alias);
|
||||||
|
if (passchars != null) {
|
||||||
|
password = new String(passchars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
|
password = null;
|
||||||
|
}
|
||||||
|
return password;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
* 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.hadoop.yarn.webapp.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.http.HttpServer2;
|
||||||
|
import org.apache.hadoop.http.HttpServer2.Builder;
|
||||||
|
import org.apache.hadoop.security.alias.CredentialProvider;
|
||||||
|
import org.apache.hadoop.security.alias.CredentialProviderFactory;
|
||||||
|
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TestWebAppUtils {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPassword() throws Exception {
|
||||||
|
Configuration conf = provisionCredentialsForSSL();
|
||||||
|
|
||||||
|
// use WebAppUtils as would be used by loadSslConfiguration
|
||||||
|
Assert.assertEquals("keypass",
|
||||||
|
WebAppUtils.getPassword(conf, WebAppUtils.WEB_APP_KEY_PASSWORD_KEY));
|
||||||
|
Assert.assertEquals("storepass",
|
||||||
|
WebAppUtils.getPassword(conf, WebAppUtils.WEB_APP_KEYSTORE_PASSWORD_KEY));
|
||||||
|
Assert.assertEquals("trustpass",
|
||||||
|
WebAppUtils.getPassword(conf, WebAppUtils.WEB_APP_TRUSTSTORE_PASSWORD_KEY));
|
||||||
|
|
||||||
|
// let's make sure that a password that doesn't exist returns null
|
||||||
|
Assert.assertEquals(null, WebAppUtils.getPassword(conf,"invalid-alias"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoadSslConfiguration() throws Exception {
|
||||||
|
Configuration conf = provisionCredentialsForSSL();
|
||||||
|
TestBuilder builder = (TestBuilder) new TestBuilder();
|
||||||
|
|
||||||
|
builder = (TestBuilder) WebAppUtils.loadSslConfiguration(
|
||||||
|
builder, conf);
|
||||||
|
|
||||||
|
String keypass = "keypass";
|
||||||
|
String storepass = "storepass";
|
||||||
|
String trustpass = "trustpass";
|
||||||
|
|
||||||
|
// make sure we get the right passwords in the builder
|
||||||
|
assertEquals(keypass, ((TestBuilder)builder).keypass);
|
||||||
|
assertEquals(storepass, ((TestBuilder)builder).keystorePassword);
|
||||||
|
assertEquals(trustpass, ((TestBuilder)builder).truststorePassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Configuration provisionCredentialsForSSL() throws IOException,
|
||||||
|
Exception {
|
||||||
|
File testDir = new File(System.getProperty("test.build.data",
|
||||||
|
"target/test-dir"));
|
||||||
|
|
||||||
|
Configuration conf = new Configuration();
|
||||||
|
final String ourUrl =
|
||||||
|
JavaKeyStoreProvider.SCHEME_NAME + "://file/" + testDir + "/test.jks";
|
||||||
|
|
||||||
|
File file = new File(testDir, "test.jks");
|
||||||
|
file.delete();
|
||||||
|
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, ourUrl);
|
||||||
|
|
||||||
|
CredentialProvider provider =
|
||||||
|
CredentialProviderFactory.getProviders(conf).get(0);
|
||||||
|
char[] keypass = {'k', 'e', 'y', 'p', 'a', 's', 's'};
|
||||||
|
char[] storepass = {'s', 't', 'o', 'r', 'e', 'p', 'a', 's', 's'};
|
||||||
|
char[] trustpass = {'t', 'r', 'u', 's', 't', 'p', 'a', 's', 's'};
|
||||||
|
|
||||||
|
// ensure that we get nulls when the key isn't there
|
||||||
|
assertEquals(null, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEY_PASSWORD_KEY));
|
||||||
|
assertEquals(null, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEYSTORE_PASSWORD_KEY));
|
||||||
|
assertEquals(null, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_TRUSTSTORE_PASSWORD_KEY));
|
||||||
|
|
||||||
|
// create new aliases
|
||||||
|
try {
|
||||||
|
provider.createCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEY_PASSWORD_KEY, keypass);
|
||||||
|
|
||||||
|
provider.createCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEYSTORE_PASSWORD_KEY, storepass);
|
||||||
|
|
||||||
|
provider.createCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_TRUSTSTORE_PASSWORD_KEY, trustpass);
|
||||||
|
|
||||||
|
// write out so that it can be found in checks
|
||||||
|
provider.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
// make sure we get back the right key directly from api
|
||||||
|
assertArrayEquals(keypass, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEY_PASSWORD_KEY).getCredential());
|
||||||
|
assertArrayEquals(storepass, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_KEYSTORE_PASSWORD_KEY).getCredential());
|
||||||
|
assertArrayEquals(trustpass, provider.getCredentialEntry(
|
||||||
|
WebAppUtils.WEB_APP_TRUSTSTORE_PASSWORD_KEY).getCredential());
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestBuilder extends HttpServer2.Builder {
|
||||||
|
public String keypass;
|
||||||
|
public String keystorePassword;
|
||||||
|
public String truststorePassword;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder trustStore(String location, String password, String type) {
|
||||||
|
truststorePassword = password;
|
||||||
|
return super.trustStore(location, password, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder keyStore(String location, String password, String type) {
|
||||||
|
keystorePassword = password;
|
||||||
|
return super.keyStore(location, password, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder keyPassword(String password) {
|
||||||
|
keypass = password;
|
||||||
|
return super.keyPassword(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue