HADOOP-10670. Allow AuthenticationFilters to load secret from signature secret files. Contributed by Kai Zheng.
This commit is contained in:
parent
2158cd2bef
commit
17f17dac4e
|
@ -18,12 +18,7 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
|
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
|
||||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
||||||
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
|
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
|
||||||
import org.apache.hadoop.security.authentication.util.Signer;
|
import org.apache.hadoop.security.authentication.util.*;
|
||||||
import org.apache.hadoop.security.authentication.util.SignerException;
|
|
||||||
import org.apache.hadoop.security.authentication.util.RandomSignerSecretProvider;
|
|
||||||
import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
|
|
||||||
import org.apache.hadoop.security.authentication.util.StringSignerSecretProvider;
|
|
||||||
import org.apache.hadoop.security.authentication.util.ZKSignerSecretProvider;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -147,6 +142,8 @@ public class AuthenticationFilter implements Filter {
|
||||||
*/
|
*/
|
||||||
public static final String SIGNATURE_SECRET = "signature.secret";
|
public static final String SIGNATURE_SECRET = "signature.secret";
|
||||||
|
|
||||||
|
public static final String SIGNATURE_SECRET_FILE = SIGNATURE_SECRET + ".file";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant for the configuration property that indicates the validity of the generated token.
|
* Constant for the configuration property that indicates the validity of the generated token.
|
||||||
*/
|
*/
|
||||||
|
@ -283,7 +280,12 @@ public class AuthenticationFilter implements Filter {
|
||||||
// fallback to old behavior
|
// fallback to old behavior
|
||||||
if (signerSecretProviderName == null) {
|
if (signerSecretProviderName == null) {
|
||||||
String signatureSecret = config.getProperty(SIGNATURE_SECRET, null);
|
String signatureSecret = config.getProperty(SIGNATURE_SECRET, null);
|
||||||
if (signatureSecret != null) {
|
String signatureSecretFile = config.getProperty(
|
||||||
|
SIGNATURE_SECRET_FILE, null);
|
||||||
|
// The precedence from high to low : file, inline string, random
|
||||||
|
if (signatureSecretFile != null) {
|
||||||
|
providerClassName = FileSignerSecretProvider.class.getName();
|
||||||
|
} else if (signatureSecret != null) {
|
||||||
providerClassName = StringSignerSecretProvider.class.getName();
|
providerClassName = StringSignerSecretProvider.class.getName();
|
||||||
} else {
|
} else {
|
||||||
providerClassName = RandomSignerSecretProvider.class.getName();
|
providerClassName = RandomSignerSecretProvider.class.getName();
|
||||||
|
@ -295,6 +297,8 @@ public class AuthenticationFilter implements Filter {
|
||||||
randomSecret = true;
|
randomSecret = true;
|
||||||
} else if ("string".equals(signerSecretProviderName)) {
|
} else if ("string".equals(signerSecretProviderName)) {
|
||||||
providerClassName = StringSignerSecretProvider.class.getName();
|
providerClassName = StringSignerSecretProvider.class.getName();
|
||||||
|
} else if ("file".equals(signerSecretProviderName)) {
|
||||||
|
providerClassName = FileSignerSecretProvider.class.getName();
|
||||||
} else if ("zookeeper".equals(signerSecretProviderName)) {
|
} else if ("zookeeper".equals(signerSecretProviderName)) {
|
||||||
providerClassName = ZKSignerSecretProvider.class.getName();
|
providerClassName = ZKSignerSecretProvider.class.getName();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.security.authentication.server;
|
package org.apache.hadoop.security.authentication.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
import java.net.HttpCookie;
|
import java.net.HttpCookie;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -197,7 +200,7 @@ public class TestAuthenticationFilter {
|
||||||
filter.destroy();
|
filter.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom secret
|
// custom secret as inline
|
||||||
filter = new AuthenticationFilter();
|
filter = new AuthenticationFilter();
|
||||||
try {
|
try {
|
||||||
FilterConfig config = Mockito.mock(FilterConfig.class);
|
FilterConfig config = Mockito.mock(FilterConfig.class);
|
||||||
|
@ -231,6 +234,39 @@ public class TestAuthenticationFilter {
|
||||||
filter.destroy();
|
filter.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom secret by file
|
||||||
|
File testDir = new File(System.getProperty("test.build.data",
|
||||||
|
"target/test-dir"));
|
||||||
|
testDir.mkdirs();
|
||||||
|
String secretValue = "hadoop";
|
||||||
|
File secretFile = new File(testDir, "http-secret.txt");
|
||||||
|
Writer writer = new FileWriter(secretFile);
|
||||||
|
writer.write(secretValue);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
filter = new AuthenticationFilter();
|
||||||
|
try {
|
||||||
|
FilterConfig config = Mockito.mock(FilterConfig.class);
|
||||||
|
Mockito.when(config.getInitParameter(
|
||||||
|
AuthenticationFilter.AUTH_TYPE)).thenReturn("simple");
|
||||||
|
Mockito.when(config.getInitParameter(
|
||||||
|
AuthenticationFilter.SIGNATURE_SECRET_FILE))
|
||||||
|
.thenReturn(secretFile.getAbsolutePath());
|
||||||
|
Mockito.when(config.getInitParameterNames()).thenReturn(
|
||||||
|
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
|
||||||
|
AuthenticationFilter.SIGNATURE_SECRET_FILE)).elements());
|
||||||
|
ServletContext context = Mockito.mock(ServletContext.class);
|
||||||
|
Mockito.when(context.getAttribute(
|
||||||
|
AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
|
||||||
|
.thenReturn(null);
|
||||||
|
Mockito.when(config.getServletContext()).thenReturn(context);
|
||||||
|
filter.init(config);
|
||||||
|
Assert.assertFalse(filter.isRandomSecret());
|
||||||
|
Assert.assertFalse(filter.isCustomSignerSecretProvider());
|
||||||
|
} finally {
|
||||||
|
filter.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// custom cookie domain and cookie path
|
// custom cookie domain and cookie path
|
||||||
filter = new AuthenticationFilter();
|
filter = new AuthenticationFilter();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -283,6 +283,9 @@ Release 2.7.0 - UNRELEASED
|
||||||
HADOOP-9329. document native build dependencies in BUILDING.txt (Vijay Bhat
|
HADOOP-9329. document native build dependencies in BUILDING.txt (Vijay Bhat
|
||||||
via Colin P. McCabe)
|
via Colin P. McCabe)
|
||||||
|
|
||||||
|
HADOOP-10670. Allow AuthenticationFilters to load secret from signature
|
||||||
|
secret files. (Kai Zheng via wheat9)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
HADOOP-11323. WritableComparator#compare keeps reference to byte array.
|
HADOOP-11323. WritableComparator#compare keeps reference to byte array.
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.security;
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import org.apache.hadoop.http.HttpServer2;
|
import org.apache.hadoop.http.HttpServer2;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
@ -25,11 +24,7 @@ import org.apache.hadoop.http.FilterContainer;
|
||||||
import org.apache.hadoop.http.FilterInitializer;
|
import org.apache.hadoop.http.FilterInitializer;
|
||||||
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -50,8 +45,6 @@ public class AuthenticationFilterInitializer extends FilterInitializer {
|
||||||
|
|
||||||
static final String PREFIX = "hadoop.http.authentication.";
|
static final String PREFIX = "hadoop.http.authentication.";
|
||||||
|
|
||||||
static final String SIGNATURE_SECRET_FILE = AuthenticationFilter.SIGNATURE_SECRET + ".file";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes hadoop-auth AuthenticationFilter.
|
* Initializes hadoop-auth AuthenticationFilter.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -77,25 +70,6 @@ public class AuthenticationFilterInitializer extends FilterInitializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String signatureSecretFile = filterConfig.get(SIGNATURE_SECRET_FILE);
|
|
||||||
if (signatureSecretFile == null) {
|
|
||||||
throw new RuntimeException("Undefined property: " + SIGNATURE_SECRET_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder secret = new StringBuilder();
|
|
||||||
try (Reader reader = new InputStreamReader(
|
|
||||||
new FileInputStream(signatureSecretFile), Charsets.UTF_8)) {
|
|
||||||
int c = reader.read();
|
|
||||||
while (c > -1) {
|
|
||||||
secret.append((char)c);
|
|
||||||
c = reader.read();
|
|
||||||
}
|
|
||||||
reader.close();
|
|
||||||
filterConfig.put(AuthenticationFilter.SIGNATURE_SECRET, secret.toString());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new RuntimeException("Could not read HTTP signature secret file: " + signatureSecretFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Resolve _HOST into bind address
|
//Resolve _HOST into bind address
|
||||||
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
||||||
String principal = filterConfig.get(KerberosAuthenticationHandler.PRINCIPAL);
|
String principal = filterConfig.get(KerberosAuthenticationHandler.PRINCIPAL);
|
||||||
|
|
|
@ -37,17 +37,6 @@ public class TestAuthenticationFilter extends TestCase {
|
||||||
public void testConfiguration() throws Exception {
|
public void testConfiguration() throws Exception {
|
||||||
Configuration conf = new Configuration();
|
Configuration conf = new Configuration();
|
||||||
conf.set("hadoop.http.authentication.foo", "bar");
|
conf.set("hadoop.http.authentication.foo", "bar");
|
||||||
|
|
||||||
File testDir = new File(System.getProperty("test.build.data",
|
|
||||||
"target/test-dir"));
|
|
||||||
testDir.mkdirs();
|
|
||||||
File secretFile = new File(testDir, "http-secret.txt");
|
|
||||||
Writer writer = new FileWriter(new File(testDir, "http-secret.txt"));
|
|
||||||
writer.write("hadoop");
|
|
||||||
writer.close();
|
|
||||||
conf.set(AuthenticationFilterInitializer.PREFIX +
|
|
||||||
AuthenticationFilterInitializer.SIGNATURE_SECRET_FILE,
|
|
||||||
secretFile.getAbsolutePath());
|
|
||||||
|
|
||||||
conf.set(HttpServer2.BIND_ADDRESS, "barhost");
|
conf.set(HttpServer2.BIND_ADDRESS, "barhost");
|
||||||
|
|
||||||
|
@ -68,7 +57,6 @@ public class TestAuthenticationFilter extends TestCase {
|
||||||
|
|
||||||
assertEquals("simple", conf.get("type"));
|
assertEquals("simple", conf.get("type"));
|
||||||
assertEquals("36000", conf.get("token.validity"));
|
assertEquals("36000", conf.get("token.validity"));
|
||||||
assertEquals("hadoop", conf.get("signature.secret"));
|
|
||||||
assertNull(conf.get("cookie.domain"));
|
assertNull(conf.get("cookie.domain"));
|
||||||
assertEquals("true", conf.get("simple.anonymous.allowed"));
|
assertEquals("true", conf.get("simple.anonymous.allowed"));
|
||||||
assertEquals("HTTP/barhost@LOCALHOST",
|
assertEquals("HTTP/barhost@LOCALHOST",
|
||||||
|
|
|
@ -18,20 +18,11 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.timeline.security;
|
package org.apache.hadoop.yarn.server.timeline.security;
|
||||||
|
|
||||||
import java.io.File;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.http.FilterContainer;
|
import org.apache.hadoop.http.FilterContainer;
|
||||||
import org.apache.hadoop.http.FilterInitializer;
|
import org.apache.hadoop.http.FilterInitializer;
|
||||||
import org.apache.hadoop.http.HttpServer2;
|
import org.apache.hadoop.http.HttpServer2;
|
||||||
import org.apache.hadoop.io.IOUtils;
|
|
||||||
import org.apache.hadoop.security.SecurityUtil;
|
import org.apache.hadoop.security.SecurityUtil;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
|
||||||
|
@ -42,7 +33,9 @@ import org.apache.hadoop.security.token.delegation.web.KerberosDelegationTokenAu
|
||||||
import org.apache.hadoop.security.token.delegation.web.PseudoDelegationTokenAuthenticationHandler;
|
import org.apache.hadoop.security.token.delegation.web.PseudoDelegationTokenAuthenticationHandler;
|
||||||
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes {@link TimelineAuthenticationFilter} which provides support for
|
* Initializes {@link TimelineAuthenticationFilter} which provides support for
|
||||||
|
@ -62,9 +55,6 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
*/
|
*/
|
||||||
public static final String PREFIX = "yarn.timeline-service.http-authentication.";
|
public static final String PREFIX = "yarn.timeline-service.http-authentication.";
|
||||||
|
|
||||||
private static final String SIGNATURE_SECRET_FILE =
|
|
||||||
TimelineAuthenticationFilter.SIGNATURE_SECRET + ".file";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Map<String, String> filterConfig;
|
Map<String, String> filterConfig;
|
||||||
|
|
||||||
|
@ -106,31 +96,6 @@ public class TimelineAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String signatureSecretFile = filterConfig.get(SIGNATURE_SECRET_FILE);
|
|
||||||
if (signatureSecretFile != null) {
|
|
||||||
Reader reader = null;
|
|
||||||
try {
|
|
||||||
StringBuilder secret = new StringBuilder();
|
|
||||||
reader = new InputStreamReader(new FileInputStream(new File(signatureSecretFile)),
|
|
||||||
Charset.forName("UTF-8"));
|
|
||||||
|
|
||||||
int c = reader.read();
|
|
||||||
while (c > -1) {
|
|
||||||
secret.append((char) c);
|
|
||||||
c = reader.read();
|
|
||||||
}
|
|
||||||
filterConfig
|
|
||||||
.put(TimelineAuthenticationFilter.SIGNATURE_SECRET,
|
|
||||||
secret.toString());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Could not read HTTP signature secret file: "
|
|
||||||
+ signatureSecretFile);
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeStream(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String authType = filterConfig.get(AuthenticationFilter.AUTH_TYPE);
|
String authType = filterConfig.get(AuthenticationFilter.AUTH_TYPE);
|
||||||
if (authType.equals(PseudoAuthenticationHandler.TYPE)) {
|
if (authType.equals(PseudoAuthenticationHandler.TYPE)) {
|
||||||
filterConfig.put(AuthenticationFilter.AUTH_TYPE,
|
filterConfig.put(AuthenticationFilter.AUTH_TYPE,
|
||||||
|
|
|
@ -43,14 +43,11 @@ import org.apache.hadoop.yarn.security.client.RMDelegationTokenIdentifier;
|
||||||
public class RMAuthenticationFilterInitializer extends FilterInitializer {
|
public class RMAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
|
|
||||||
String configPrefix;
|
String configPrefix;
|
||||||
String signatureSecretFileProperty;
|
|
||||||
String kerberosPrincipalProperty;
|
String kerberosPrincipalProperty;
|
||||||
String cookiePath;
|
String cookiePath;
|
||||||
|
|
||||||
public RMAuthenticationFilterInitializer() {
|
public RMAuthenticationFilterInitializer() {
|
||||||
this.configPrefix = "hadoop.http.authentication.";
|
this.configPrefix = "hadoop.http.authentication.";
|
||||||
this.signatureSecretFileProperty =
|
|
||||||
AuthenticationFilter.SIGNATURE_SECRET + ".file";
|
|
||||||
this.kerberosPrincipalProperty = KerberosAuthenticationHandler.PRINCIPAL;
|
this.kerberosPrincipalProperty = KerberosAuthenticationHandler.PRINCIPAL;
|
||||||
this.cookiePath = "/";
|
this.cookiePath = "/";
|
||||||
}
|
}
|
||||||
|
@ -77,34 +74,6 @@ public class RMAuthenticationFilterInitializer extends FilterInitializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String signatureSecretFile = filterConfig.get(signatureSecretFileProperty);
|
|
||||||
if (signatureSecretFile != null) {
|
|
||||||
Reader reader = null;
|
|
||||||
try {
|
|
||||||
StringBuilder secret = new StringBuilder();
|
|
||||||
reader =
|
|
||||||
new InputStreamReader(new FileInputStream(signatureSecretFile),
|
|
||||||
"UTF-8");
|
|
||||||
int c = reader.read();
|
|
||||||
while (c > -1) {
|
|
||||||
secret.append((char) c);
|
|
||||||
c = reader.read();
|
|
||||||
}
|
|
||||||
filterConfig.put(AuthenticationFilter.SIGNATURE_SECRET,
|
|
||||||
secret.toString());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// if running in non-secure mode, this filter only gets added
|
|
||||||
// because the user has not setup his own filter so just generate
|
|
||||||
// a random secret. in secure mode, the user needs to setup security
|
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Could not read HTTP signature secret file: " + signatureSecretFile);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve _HOST into bind address
|
// Resolve _HOST into bind address
|
||||||
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
String bindAddress = conf.get(HttpServer2.BIND_ADDRESS);
|
||||||
String principal = filterConfig.get(kerberosPrincipalProperty);
|
String principal = filterConfig.get(kerberosPrincipalProperty);
|
||||||
|
|
Loading…
Reference in New Issue