HADOOP-11113. Namenode not able to reconnect to KMS after KMS restart. (Arun Suresh via wang)

This commit is contained in:
Andrew Wang 2014-09-30 16:46:58 -07:00
parent bbff96be48
commit a4c9b80a7c
4 changed files with 132 additions and 18 deletions

View File

@ -780,6 +780,9 @@ Release 2.6.0 - UNRELEASED
HADOOP-11130. NFS updateMaps OS check is reversed (brandonli) HADOOP-11130. NFS updateMaps OS check is reversed (brandonli)
HADOOP-11113. Namenode not able to reconnect to KMS after KMS restart.
(Arun Suresh via wang)
BREAKDOWN OF HDFS-6134 AND HADOOP-10150 SUBTASKS AND RELATED JIRAS BREAKDOWN OF HDFS-6134 AND HADOOP-10150 SUBTASKS AND RELATED JIRAS
HADOOP-10734. Implement high-performance secure random number sources. HADOOP-10734. Implement high-performance secure random number sources.

View File

@ -415,7 +415,7 @@ public HttpURLConnection run() throws Exception {
return conn; return conn;
} }
private static <T> T call(HttpURLConnection conn, Map jsonOutput, private <T> T call(HttpURLConnection conn, Map jsonOutput,
int expectedResponse, Class<T> klass) int expectedResponse, Class<T> klass)
throws IOException { throws IOException {
T ret = null; T ret = null;
@ -427,6 +427,14 @@ private static <T> T call(HttpURLConnection conn, Map jsonOutput,
conn.getInputStream().close(); conn.getInputStream().close();
throw ex; throw ex;
} }
if (conn.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
// Ideally, this should happen only when there is an Authentication
// failure. Unfortunately, the AuthenticationFilter returns 403 when it
// cannot authenticate (Since a 401 requires Server to send
// WWW-Authenticate header as well)..
KMSClientProvider.this.authToken =
new DelegationTokenAuthenticatedURL.Token();
}
HttpExceptionUtils.validateResponse(conn, expectedResponse); HttpExceptionUtils.validateResponse(conn, expectedResponse);
if (APPLICATION_JSON_MIME.equalsIgnoreCase(conn.getContentType()) if (APPLICATION_JSON_MIME.equalsIgnoreCase(conn.getContentType())
&& klass != null) { && klass != null) {

View File

@ -43,12 +43,12 @@
public class MiniKMS { public class MiniKMS {
private static Server createJettyServer(String keyStore, String password) { private static Server createJettyServer(String keyStore, String password, int inPort) {
try { try {
boolean ssl = keyStore != null; boolean ssl = keyStore != null;
InetAddress localhost = InetAddress.getByName("localhost"); InetAddress localhost = InetAddress.getByName("localhost");
String host = "localhost"; String host = "localhost";
ServerSocket ss = new ServerSocket(0, 50, localhost); ServerSocket ss = new ServerSocket((inPort < 0) ? 0 : inPort, 50, localhost);
int port = ss.getLocalPort(); int port = ss.getLocalPort();
ss.close(); ss.close();
Server server = new Server(0); Server server = new Server(0);
@ -91,6 +91,7 @@ public static class Builder {
private String log4jConfFile; private String log4jConfFile;
private File keyStoreFile; private File keyStoreFile;
private String keyStorePassword; private String keyStorePassword;
private int inPort = -1;
public Builder() { public Builder() {
kmsConfDir = new File("target/test-classes").getAbsoluteFile(); kmsConfDir = new File("target/test-classes").getAbsoluteFile();
@ -111,6 +112,12 @@ public Builder setLog4jConfFile(String log4jConfFile) {
return this; return this;
} }
public Builder setPort(int port) {
Preconditions.checkArgument(port > 0, "input port must be greater than 0");
this.inPort = port;
return this;
}
public Builder setSslConf(File keyStoreFile, String keyStorePassword) { public Builder setSslConf(File keyStoreFile, String keyStorePassword) {
Preconditions.checkNotNull(keyStoreFile, "keystore file is NULL"); Preconditions.checkNotNull(keyStoreFile, "keystore file is NULL");
Preconditions.checkNotNull(keyStorePassword, "keystore password is NULL"); Preconditions.checkNotNull(keyStorePassword, "keystore password is NULL");
@ -126,7 +133,7 @@ public MiniKMS build() {
"KMS conf dir does not exist"); "KMS conf dir does not exist");
return new MiniKMS(kmsConfDir.getAbsolutePath(), log4jConfFile, return new MiniKMS(kmsConfDir.getAbsolutePath(), log4jConfFile,
(keyStoreFile != null) ? keyStoreFile.getAbsolutePath() : null, (keyStoreFile != null) ? keyStoreFile.getAbsolutePath() : null,
keyStorePassword); keyStorePassword, inPort);
} }
} }
@ -135,14 +142,16 @@ public MiniKMS build() {
private String keyStore; private String keyStore;
private String keyStorePassword; private String keyStorePassword;
private Server jetty; private Server jetty;
private int inPort;
private URL kmsURL; private URL kmsURL;
public MiniKMS(String kmsConfDir, String log4ConfFile, String keyStore, public MiniKMS(String kmsConfDir, String log4ConfFile, String keyStore,
String password) { String password, int inPort) {
this.kmsConfDir = kmsConfDir; this.kmsConfDir = kmsConfDir;
this.log4jConfFile = log4ConfFile; this.log4jConfFile = log4ConfFile;
this.keyStore = keyStore; this.keyStore = keyStore;
this.keyStorePassword = password; this.keyStorePassword = password;
this.inPort = inPort;
} }
public void start() throws Exception { public void start() throws Exception {
@ -174,7 +183,7 @@ public void start() throws Exception {
writer.close(); writer.close();
} }
System.setProperty("log4j.configuration", log4jConfFile); System.setProperty("log4j.configuration", log4jConfFile);
jetty = createJettyServer(keyStore, keyStorePassword); jetty = createJettyServer(keyStore, keyStorePassword, inPort);
// we need to do a special handling for MiniKMS to work when in a dir and // we need to do a special handling for MiniKMS to work when in a dir and
// when in a JAR in the classpath thanks to Jetty way of handling of webapps // when in a JAR in the classpath thanks to Jetty way of handling of webapps

View File

@ -89,7 +89,7 @@ public static File getTestDir() throws Exception {
return file; return file;
} }
public static abstract class KMSCallable implements Callable<Void> { public static abstract class KMSCallable<T> implements Callable<T> {
private URL kmsUrl; private URL kmsUrl;
protected URL getKMSUrl() { protected URL getKMSUrl() {
@ -97,19 +97,27 @@ protected URL getKMSUrl() {
} }
} }
protected void runServer(String keystore, String password, File confDir, protected <T> T runServer(String keystore, String password, File confDir,
KMSCallable callable) throws Exception { KMSCallable<T> callable) throws Exception {
return runServer(-1, keystore, password, confDir, callable);
}
protected <T> T runServer(int port, String keystore, String password, File confDir,
KMSCallable<T> callable) throws Exception {
MiniKMS.Builder miniKMSBuilder = new MiniKMS.Builder().setKmsConfDir(confDir) MiniKMS.Builder miniKMSBuilder = new MiniKMS.Builder().setKmsConfDir(confDir)
.setLog4jConfFile("log4j.properties"); .setLog4jConfFile("log4j.properties");
if (keystore != null) { if (keystore != null) {
miniKMSBuilder.setSslConf(new File(keystore), password); miniKMSBuilder.setSslConf(new File(keystore), password);
} }
if (port > 0) {
miniKMSBuilder.setPort(port);
}
MiniKMS miniKMS = miniKMSBuilder.build(); MiniKMS miniKMS = miniKMSBuilder.build();
miniKMS.start(); miniKMS.start();
try { try {
System.out.println("Test KMS running at: " + miniKMS.getKMSUrl()); System.out.println("Test KMS running at: " + miniKMS.getKMSUrl());
callable.kmsUrl = miniKMS.getKMSUrl(); callable.kmsUrl = miniKMS.getKMSUrl();
callable.call(); return callable.call();
} finally { } finally {
miniKMS.stop(); miniKMS.stop();
} }
@ -284,7 +292,7 @@ public void testStartStop(final boolean ssl, final boolean kerberos)
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(keystore, password, testDir, new KMSCallable() { runServer(keystore, password, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
@ -351,7 +359,7 @@ public void testKMSProvider() throws Exception {
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k6.ALL", "*"); conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k6.ALL", "*");
writeConf(confDir, conf); writeConf(confDir, conf);
runServer(null, null, confDir, new KMSCallable() { runServer(null, null, confDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
Date started = new Date(); Date started = new Date();
@ -616,7 +624,7 @@ public void testKeyACLs() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
@ -782,6 +790,92 @@ public Void run() throws Exception {
}); });
} }
@Test
public void testKMSRestart() throws Exception {
Configuration conf = new Configuration();
conf.set("hadoop.security.authentication", "kerberos");
UserGroupInformation.setConfiguration(conf);
final File testDir = getTestDir();
conf = createBaseKMSConf(testDir);
conf.set("hadoop.kms.authentication.kerberos.keytab",
keytab.getAbsolutePath());
conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost");
conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT");
for (KMSACLs.Type type : KMSACLs.Type.values()) {
conf.set(type.getAclConfigKey(), type.toString());
}
conf.set(KMSACLs.Type.CREATE.getAclConfigKey(),
KMSACLs.Type.CREATE.toString() + ",SET_KEY_MATERIAL");
conf.set(KMSACLs.Type.ROLLOVER.getAclConfigKey(),
KMSACLs.Type.ROLLOVER.toString() + ",SET_KEY_MATERIAL");
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k0.ALL", "*");
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k1.ALL", "*");
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k2.ALL", "*");
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "k3.ALL", "*");
writeConf(testDir, conf);
KMSCallable<KeyProvider> c =
new KMSCallable<KeyProvider>() {
@Override
public KeyProvider call() throws Exception {
final Configuration conf = new Configuration();
conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 128);
final URI uri = createKMSUri(getKMSUrl());
final KeyProvider kp =
doAs("SET_KEY_MATERIAL",
new PrivilegedExceptionAction<KeyProvider>() {
@Override
public KeyProvider run() throws Exception {
KMSClientProvider kp = new KMSClientProvider(uri, conf);
kp.createKey("k1", new byte[16],
new KeyProvider.Options(conf));
return kp;
}
});
return kp;
}
};
final KeyProvider retKp =
runServer(null, null, testDir, c);
// Restart server (using the same port)
runServer(c.getKMSUrl().getPort(), null, null, testDir,
new KMSCallable<Void>() {
@Override
public Void call() throws Exception {
final Configuration conf = new Configuration();
conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 128);
doAs("SET_KEY_MATERIAL",
new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
retKp.createKey("k2", new byte[16],
new KeyProvider.Options(conf));
Assert.fail("Should fail first time !!");
} catch (IOException e) {
String message = e.getMessage();
Assert.assertTrue("Should be a 403 error : " + message,
message.contains("403"));
}
retKp.createKey("k2", new byte[16],
new KeyProvider.Options(conf));
retKp.createKey("k3", new byte[16],
new KeyProvider.Options(conf));
return null;
}
});
return null;
}
});
}
@Test @Test
public void testACLs() throws Exception { public void testACLs() throws Exception {
Configuration conf = new Configuration(); Configuration conf = new Configuration();
@ -809,7 +903,7 @@ public void testACLs() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
@ -1117,7 +1211,7 @@ public void testKMSBlackList() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
@ -1201,7 +1295,7 @@ public void testServicePrincipalACLs() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
@ -1326,7 +1420,7 @@ public void testDelegationTokenAccess() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
@ -1398,7 +1492,7 @@ public void testProxyUser() throws Exception {
writeConf(testDir, conf); writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() { runServer(null, null, testDir, new KMSCallable<Void>() {
@Override @Override
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();