YARN-5975. Remove the agent - slider AM ssl related code. Contributed by Jian He

This commit is contained in:
Billie Rinaldi 2016-12-13 10:16:09 -08:00 committed by Jian He
parent efe7d44ead
commit 2cea59dc54
26 changed files with 14 additions and 2444 deletions

View File

@ -173,7 +173,4 @@ public interface SliderClusterProtocol extends VersionedProtocol {
Messages.WrappedJsonProto getModelResolvedResources(Messages.EmptyPayloadProto request) throws IOException;
Messages.WrappedJsonProto getLiveResources(Messages.EmptyPayloadProto request) throws IOException;
Messages.GetCertificateStoreResponseProto getClientCertificateStore(Messages.GetCertificateStoreRequestProto request)
throws IOException;
}

View File

@ -18,8 +18,6 @@
package org.apache.slider.api.proto;
import com.google.protobuf.ByteString;
import org.apache.commons.io.IOUtils;
import org.apache.slider.api.types.ApplicationLivenessInformation;
import org.apache.slider.api.types.ComponentInformation;
import org.apache.slider.api.types.ContainerInformation;
@ -30,15 +28,10 @@ import org.apache.slider.core.conf.ConfTree;
import org.apache.slider.core.conf.ConfTreeOperations;
import org.apache.slider.core.persist.AggregateConfSerDeser;
import org.apache.slider.core.persist.ConfTreeSerDeser;
import org.apache.slider.server.services.security.SecurityStore;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -95,35 +88,6 @@ public class RestTypeMarshalling {
}
return info;
}
public static Messages.GetCertificateStoreResponseProto marshall(
SecurityStore securityStore) throws IOException {
Messages.GetCertificateStoreResponseProto.Builder builder =
Messages.GetCertificateStoreResponseProto.newBuilder();
builder.setStore(ByteString.copyFrom(getStoreBytes(securityStore)));
return builder.build();
}
private static byte[] getStoreBytes(SecurityStore securityStore)
throws IOException {
InputStream is = null;
byte[] storeBytes;
try {
is = new FileInputStream(securityStore.getFile());
storeBytes = IOUtils.toByteArray(is);
} finally {
if (is != null) {
is.close();
}
}
return storeBytes;
}
public static byte[] unmarshall(Messages.GetCertificateStoreResponseProto response) {
return response.getStore().toByteArray();
}
public static Messages.ComponentInformationProto marshall(ComponentInformation info) {
Messages.ComponentInformationProto.Builder builder =

View File

@ -168,7 +168,6 @@ import org.apache.slider.providers.docker.DockerClientProvider;
import org.apache.slider.providers.slideram.SliderAMClientProvider;
import org.apache.slider.server.appmaster.SliderAppMaster;
import org.apache.slider.server.appmaster.rpc.RpcBinder;
import org.apache.slider.server.services.security.SecurityStore;
import org.apache.slider.server.services.utility.AbstractSliderLaunchedService;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
@ -1223,8 +1222,6 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
IOException {
if (clientInfo.install) {
return doClientInstall(clientInfo);
} else if (clientInfo.getCertStore) {
return doCertificateStoreRetrieval(clientInfo);
} else {
throw new BadCommandArgumentsException(
"Only install, keystore, and truststore commands are supported for the client.\n"
@ -1233,83 +1230,6 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
}
}
private int doCertificateStoreRetrieval(ActionClientArgs clientInfo)
throws YarnException, IOException {
if (clientInfo.keystore != null && clientInfo.truststore != null) {
throw new BadCommandArgumentsException(
"Only one of either keystore or truststore can be retrieved at one time. "
+ "Retrieval of both should be done separately\n"
+ CommonArgs.usage(serviceArgs, ACTION_CLIENT));
}
requireArgumentSet(Arguments.ARG_NAME, clientInfo.name);
File storeFile = null;
SecurityStore.StoreType type;
if (clientInfo.keystore != null) {
storeFile = clientInfo.keystore;
type = SecurityStore.StoreType.keystore;
} else {
storeFile = clientInfo.truststore;
type = SecurityStore.StoreType.truststore;
}
require (!storeFile.exists(),
"File %s already exists. Please remove that file or select a different file name.",
storeFile.getAbsolutePath());
String hostname = null;
if (type == SecurityStore.StoreType.keystore) {
hostname = clientInfo.hostname;
if (hostname == null) {
hostname = InetAddress.getLocalHost().getCanonicalHostName();
log.info("No hostname specified via command line. Using {}", hostname);
}
}
String password = clientInfo.password;
if (password == null) {
String provider = clientInfo.provider;
String alias = clientInfo.alias;
if (provider != null && alias != null) {
Configuration conf = new Configuration(getConfig());
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, provider);
char[] chars = conf.getPassword(alias);
if (chars == null) {
CredentialProvider credentialProvider =
CredentialProviderFactory.getProviders(conf).get(0);
chars = readOnePassword(alias);
credentialProvider.createCredentialEntry(alias, chars);
credentialProvider.flush();
}
password = String.valueOf(chars);
Arrays.fill(chars, ' ');
} else {
log.info("No password and no provider/alias pair were provided, " +
"prompting for password");
// get a password
password = String.valueOf(readOnePassword(type.name()));
}
}
byte[] keystore = createClusterOperations(clientInfo.name)
.getClientCertificateStore(hostname, "client", password, type.name());
// persist to file
FileOutputStream storeFileOutputStream = null;
try {
storeFileOutputStream = new FileOutputStream(storeFile);
IOUtils.write(keystore, storeFileOutputStream);
} catch (Exception e) {
log.error("Unable to persist to file {}", storeFile);
throw e;
} finally {
if (storeFileOutputStream != null) {
storeFileOutputStream.close();
}
}
return EXIT_SUCCESS;
}
private int doClientInstall(ActionClientArgs clientInfo)
throws IOException, SliderException {

View File

@ -19,17 +19,12 @@
package org.apache.slider.client.ipc;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.yarn.api.records.NodeReport;
import org.apache.hadoop.yarn.api.records.NodeState;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.slider.api.ClusterDescription;
import org.apache.slider.api.ClusterNode;
import org.apache.slider.api.SliderClusterProtocol;
import org.apache.slider.api.StateValues;
import org.apache.slider.api.proto.Messages;
import static org.apache.slider.api.proto.RestTypeMarshalling.*;
import org.apache.slider.api.types.ApplicationLivenessInformation;
import org.apache.slider.api.types.ComponentInformation;
import org.apache.slider.api.types.ContainerInformation;
@ -37,7 +32,6 @@ import org.apache.slider.api.types.NodeInformation;
import org.apache.slider.api.types.NodeInformationList;
import org.apache.slider.api.types.PingInformation;
import org.apache.slider.common.tools.Duration;
import org.apache.slider.common.tools.SliderUtils;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.ConfTree;
import org.apache.slider.core.conf.ConfTreeOperations;
@ -45,8 +39,6 @@ import org.apache.slider.core.exceptions.NoSuchNodeException;
import org.apache.slider.core.exceptions.SliderException;
import org.apache.slider.core.exceptions.WaitTimeoutException;
import org.apache.slider.core.persist.ConfTreeSerDeser;
import org.apache.slider.server.services.security.SecurityStore;
import org.apache.slider.server.services.security.SignCertResponse;
import org.codehaus.jackson.JsonParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -59,6 +51,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.slider.api.proto.RestTypeMarshalling.*;
/**
* Cluster operations at a slightly higher level than the RPC code
*/
@ -508,22 +502,4 @@ public class SliderClusterOperations {
);
return unmarshall(proto);
}
public byte[] getClientCertificateStore(String hostname, String clientId,
String password, String type) throws IOException {
Messages.GetCertificateStoreRequestProto.Builder
builder = Messages.GetCertificateStoreRequestProto.newBuilder();
if (hostname != null) {
builder.setHostname(hostname);
}
Messages.GetCertificateStoreRequestProto requestProto =
builder.setRequesterId(clientId)
.setPassword(password)
.setType(type)
.build();
Messages.GetCertificateStoreResponseProto response =
appMaster.getClientCertificateStore(requestProto);
return unmarshall(response);
}
}

View File

@ -37,39 +37,10 @@ public class ActionClientArgs extends AbstractActionArgs {
description = "Install client")
public boolean install;
@Parameter(names = {ARG_GETCERTSTORE},
description = "Get a certificate store")
public boolean getCertStore;
@Parameter(names = {ARG_KEYSTORE},
description = "Retrieve keystore to specified location")
public File keystore;
@Parameter(names = {ARG_TRUSTSTORE},
description = "Retrieve truststore to specified location")
public File truststore;
@Parameter(names = {ARG_HOSTNAME},
description = "(Optional) Specify the hostname to use for generation of keystore certificate")
public String hostname;
@Parameter(names = {ARG_NAME},
description = "The name of the application")
public String name;
@Parameter(names = {ARG_PROVIDER},
description = "The credential provider in which the password is stored")
public String provider;
@Parameter(names = {ARG_ALIAS},
description = "The credential provider alias associated with the password")
public String alias;
@Parameter(names = {ARG_PASSWORD},
description = "The certificate store password (alternative to " +
"provider/alias; if password is specified, those will be ignored)")
public String password;
@Parameter(names = {ARG_PACKAGE},
description = "Path to app package")
public String packageURI;

View File

@ -30,7 +30,6 @@ import org.apache.hadoop.registry.client.types.ServiceRecord;
import org.apache.hadoop.registry.client.types.yarn.PersistencePolicies;
import org.apache.hadoop.registry.client.types.yarn.YarnRegistryAttributes;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.api.records.LocalResourceType;
import org.apache.slider.api.ClusterNode;
@ -38,7 +37,6 @@ import org.apache.slider.api.InternalKeys;
import org.apache.slider.api.OptionKeys;
import org.apache.slider.api.ResourceKeys;
import org.apache.slider.api.RoleKeys;
import org.apache.slider.common.SliderExitCodes;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.common.SliderXmlConfKeys;
import org.apache.slider.common.tools.SliderFileSystem;
@ -59,9 +57,6 @@ import org.apache.slider.core.registry.docstore.PublishedConfigurationOutputter;
import org.apache.slider.core.registry.docstore.PublishedExports;
import org.apache.slider.server.appmaster.state.RoleInstance;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.services.security.CertificateManager;
import org.apache.slider.server.services.security.SecurityStore;
import org.apache.slider.server.services.security.StoresGenerator;
import org.apache.slider.server.services.yarnregistry.YarnRegistryViewForProviders;
import org.slf4j.Logger;
@ -398,61 +393,6 @@ public class ProviderUtils implements RoleKeys, SliderKeys {
}
}
/**
* Return whether two-way SSL is enabled for Agent / AM communication.
* @param amComponent component specification
* @return true if enabled
*/
public boolean hasTwoWaySSLEnabled(MapOperations amComponent) {
return amComponent != null ?
amComponent.getOptionBool(TWO_WAY_SSL_ENABLED, false) : false;
}
/**
* Generate and localize SSL certs for Agent / AM communication
* @param launcher container launcher
* @param container allocated container information
* @param fileSystem file system
* @param clusterName app name
* @throws SliderException certs cannot be generated/uploaded
*/
public void localizeContainerSSLResources(ContainerLauncher launcher,
Container container, SliderFileSystem fileSystem, String clusterName)
throws SliderException {
try {
// localize server cert
Path certsDir = fileSystem.buildClusterSecurityDirPath(clusterName);
LocalResource certResource = fileSystem.createAmResource(
new Path(certsDir, CRT_FILE_NAME),
LocalResourceType.FILE);
launcher.addLocalResource(CERT_FILE_LOCALIZATION_PATH, certResource);
// generate and localize agent cert
CertificateManager certMgr = new CertificateManager();
String hostname = container.getNodeId().getHost();
String containerId = container.getId().toString();
certMgr.generateContainerCertificate(hostname, containerId);
LocalResource agentCertResource = fileSystem.createAmResource(
uploadSecurityResource(
CertificateManager.getAgentCertficateFilePath(containerId),
fileSystem, clusterName), LocalResourceType.FILE);
// still using hostname as file name on the agent side, but the files
// do end up under the specific container's file space
launcher.addLocalResource(INFRA_RUN_SECURITY_DIR + hostname +
".crt", agentCertResource);
LocalResource agentKeyResource = fileSystem.createAmResource(
uploadSecurityResource(
CertificateManager.getAgentKeyFilePath(containerId), fileSystem,
clusterName),
LocalResourceType.FILE);
launcher.addLocalResource(INFRA_RUN_SECURITY_DIR + hostname +
".key", agentKeyResource);
} catch (Exception e) {
throw new SliderException(SliderExitCodes.EXIT_DEPLOYMENT_FAILED, e,
"Unable to localize certificates. Two-way SSL cannot be enabled");
}
}
/**
* Upload a local file to the cluster security dir in HDFS. If the file
@ -706,87 +646,6 @@ public class ProviderUtils implements RoleKeys, SliderKeys {
}
}
/**
* Generate and localize security stores requested by the app. Also perform
* last-minute substitution of cluster name into credentials strings.
* @param launcher container launcher
* @param container allocated container information
* @param role component name
* @param fileSystem file system
* @param instanceDefinition app specification
* @param compOps component specification
* @param clusterName app name
* @throws SliderException stores cannot be generated/uploaded
* @throws IOException stores cannot be generated/uploaded
*/
public void localizeContainerSecurityStores(ContainerLauncher launcher,
Container container,
String role,
SliderFileSystem fileSystem,
AggregateConf instanceDefinition,
MapOperations compOps,
String clusterName)
throws SliderException, IOException {
// substitute CLUSTER_NAME into credentials
Map<String,List<String>> newcred = new HashMap<>();
for (Entry<String,List<String>> entry :
instanceDefinition.getAppConf().credentials.entrySet()) {
List<String> resultList = new ArrayList<>();
for (String v : entry.getValue()) {
resultList.add(v.replaceAll(Pattern.quote("${CLUSTER_NAME}"),
clusterName).replaceAll(Pattern.quote("${CLUSTER}"),
clusterName));
}
newcred.put(entry.getKey().replaceAll(Pattern.quote("${CLUSTER_NAME}"),
clusterName).replaceAll(Pattern.quote("${CLUSTER}"),
clusterName),
resultList);
}
instanceDefinition.getAppConf().credentials = newcred;
// generate and localize security stores
SecurityStore[] stores = generateSecurityStores(container, role,
instanceDefinition, compOps);
for (SecurityStore store : stores) {
LocalResource keystoreResource = fileSystem.createAmResource(
uploadSecurityResource(store.getFile(), fileSystem, clusterName),
LocalResourceType.FILE);
launcher.addLocalResource(String.format("secstores/%s-%s.p12",
store.getType(), role),
keystoreResource);
}
}
/**
* Generate security stores requested by the app.
* @param container allocated container information
* @param role component name
* @param instanceDefinition app specification
* @param compOps component specification
* @return security stores
* @throws SliderException stores cannot be generated
* @throws IOException stores cannot be generated
*/
private SecurityStore[] generateSecurityStores(Container container,
String role,
AggregateConf instanceDefinition,
MapOperations compOps)
throws SliderException, IOException {
return StoresGenerator.generateSecurityStores(
container.getNodeId().getHost(), container.getId().toString(),
role, instanceDefinition, compOps);
}
/**
* Return whether security stores are requested by the app.
* @param compOps component specification
* @return true if stores are requested
*/
public boolean areStoresRequested(MapOperations compOps) {
return compOps != null ? compOps.
getOptionBool(COMP_STORES_REQUIRED_KEY, false) : false;
}
/**
* Localize application tarballs and other resources requested by the app.
* @param launcher container launcher

View File

@ -165,11 +165,6 @@ public class DockerProviderService extends AbstractProviderService implements
fileSystem, getClusterName());
}
if (providerUtils.areStoresRequested(appComponent)) {
providerUtils.localizeContainerSecurityStores(launcher, container,
roleName, fileSystem, instanceDefinition, appComponent, getClusterName());
}
if (appComponent.getOptionBool(AM_CONFIG_GENERATION, false)) {
// build and localize configuration files
Map<String, Map<String, String>> configurations =

View File

@ -26,8 +26,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.http.HttpConfig;
@ -155,7 +153,6 @@ import org.apache.slider.server.appmaster.web.rest.InsecureAmFilterInitializer;
import org.apache.slider.server.appmaster.web.rest.RestPaths;
import org.apache.slider.server.appmaster.web.rest.application.ApplicationResouceContentCacheFactory;
import org.apache.slider.server.appmaster.web.rest.application.resources.ContentCache;
import org.apache.slider.server.services.security.CertificateManager;
import org.apache.slider.server.services.utility.AbstractSliderLaunchedService;
import org.apache.slider.server.services.utility.WebAppService;
import org.apache.slider.server.services.workflow.ServiceThreadFactory;
@ -373,7 +370,6 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private InetSocketAddress rpcServiceAddress;
private SliderAMProviderService sliderAMProvider;
private CertificateManager certificateManager;
/**
* Executor.
@ -732,8 +728,6 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
}
}
certificateManager = new CertificateManager();
//bring up the Slider RPC service
buildPortScanner(instanceDefinition);
startSliderRPCServer(instanceDefinition);
@ -757,18 +751,12 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
// Start up the WebApp and track the URL for it
MapOperations component = instanceDefinition.getAppConfOperations()
.getComponent(SliderKeys.COMPONENT_AM);
certificateManager.initialize(component, appMasterHostname,
appMasterContainerID.toString(),
clustername);
certificateManager.setPassphrase(instanceDefinition.getPassphrase());
// Web service endpoints: initialize
WebAppApiImpl webAppApi =
new WebAppApiImpl(
stateForProviders,
providerService,
certificateManager,
registryOperations,
providerService, registryOperations,
metricsAndMonitoring,
actionQueues,
this,
@ -1551,9 +1539,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
verifyIPCAccess();
sliderIPCService = new SliderIPCService(
this,
certificateManager,
stateForProviders,
this, stateForProviders,
actionQueues,
metricsAndMonitoring,
contentCache);

View File

@ -303,16 +303,4 @@ public class SliderClusterProtocolPBImpl implements SliderClusterProtocolPB {
throw wrap(e);
}
}
@Override
public Messages.GetCertificateStoreResponseProto getClientCertificateStore(
RpcController controller,
Messages.GetCertificateStoreRequestProto request)
throws ServiceException {
try {
return real.getClientCertificateStore(request);
} catch (Exception e) {
throw wrap(e);
}
}
}

View File

@ -88,10 +88,9 @@ public class SliderClusterProtocolProxy implements SliderClusterProtocol {
return ioe;
}
@Override
public Messages.StopClusterResponseProto stopCluster(Messages.StopClusterRequestProto request) throws
IOException,
YarnException {
@Override public Messages.StopClusterResponseProto stopCluster(
Messages.StopClusterRequestProto request)
throws IOException, YarnException {
try {
return endpoint.stopCluster(NULL_CONTROLLER, request);
} catch (ServiceException e) {
@ -343,16 +342,5 @@ public class SliderClusterProtocolProxy implements SliderClusterProtocol {
} catch (ServiceException e) {
throw convert(e);
}
}
@Override
public Messages.GetCertificateStoreResponseProto getClientCertificateStore(Messages.GetCertificateStoreRequestProto request) throws
IOException {
try {
return endpoint.getClientCertificateStore(NULL_CONTROLLER, request);
} catch (ServiceException e) {
throw convert(e);
}
}
}

View File

@ -35,7 +35,6 @@ import org.apache.slider.api.types.NodeInformationList;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.ConfTree;
import org.apache.slider.core.exceptions.ServiceNotReadyException;
import org.apache.slider.core.exceptions.SliderException;
import org.apache.slider.core.main.LauncherExitCodes;
import org.apache.slider.core.persist.AggregateConfSerDeser;
import org.apache.slider.core.persist.ConfTreeSerDeser;
@ -51,8 +50,6 @@ import org.apache.slider.server.appmaster.management.MetricsAndMonitoring;
import org.apache.slider.server.appmaster.state.RoleInstance;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.appmaster.web.rest.application.resources.ContentCache;
import org.apache.slider.server.services.security.CertificateManager;
import org.apache.slider.server.services.security.SecurityStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -63,16 +60,7 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.apache.slider.api.proto.RestTypeMarshalling.marshall;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.LIVE_COMPONENTS;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.LIVE_CONTAINERS;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.LIVE_NODES;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.LIVE_RESOURCES;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_DESIRED;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_DESIRED_APPCONF;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_DESIRED_RESOURCES;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_RESOLVED;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_RESOLVED_APPCONF;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.MODEL_RESOLVED_RESOURCES;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.*;
/**
* Implement the {@link SliderClusterProtocol}.
@ -90,7 +78,6 @@ public class SliderIPCService extends AbstractService
private final MetricsAndMonitoring metricsAndMonitoring;
private final AppMasterActionOperations amOperations;
private final ContentCache cache;
private final CertificateManager certificateManager;
/**
* This is the prefix used for metrics
@ -107,11 +94,8 @@ public class SliderIPCService extends AbstractService
* @param cache
*/
public SliderIPCService(AppMasterActionOperations amOperations,
CertificateManager certificateManager,
StateAccessForProviders state,
QueueAccess actionQueues,
MetricsAndMonitoring metricsAndMonitoring,
ContentCache cache) {
StateAccessForProviders state, QueueAccess actionQueues,
MetricsAndMonitoring metricsAndMonitoring, ContentCache cache) {
super("SliderIPCService");
Preconditions.checkArgument(amOperations != null, "null amOperations");
Preconditions.checkArgument(state != null, "null appState");
@ -124,7 +108,6 @@ public class SliderIPCService extends AbstractService
this.metricsAndMonitoring = metricsAndMonitoring;
this.amOperations = amOperations;
this.cache = cache;
this.certificateManager = certificateManager;
}
@Override //SliderClusterProtocol
@ -517,35 +500,4 @@ public class SliderIPCService extends AbstractService
builder.setJson(json);
return builder.build();
}
@Override
public Messages.GetCertificateStoreResponseProto getClientCertificateStore(Messages.GetCertificateStoreRequestProto request) throws
IOException {
String hostname = request.getHostname();
String clientId = request.getRequesterId();
String password = request.getPassword();
String type = request.getType();
SecurityStore store = null;
try {
if ( SecurityStore.StoreType.keystore.equals(
SecurityStore.StoreType.valueOf(type))) {
store = certificateManager.generateContainerKeystore(hostname,
clientId,
null,
password);
} else if (SecurityStore.StoreType.truststore.equals(
SecurityStore.StoreType.valueOf(type))) {
store = certificateManager.generateContainerTruststore(clientId,
null,
password);
} else {
throw new IOException("Illegal store type");
}
} catch (SliderException e) {
throw new IOException(e);
}
return marshall(store);
}
}

View File

@ -18,16 +18,11 @@ package org.apache.slider.server.appmaster.web;
import org.apache.hadoop.registry.client.api.RegistryOperations;
import org.apache.slider.providers.ProviderService;
import org.apache.slider.server.appmaster.AppMasterActionOperations;
import org.apache.slider.server.appmaster.actions.QueueAccess;
import org.apache.slider.server.appmaster.management.MetricsAndMonitoring;
import org.apache.slider.server.appmaster.state.AppState;
import org.apache.slider.server.appmaster.state.RoleStatus;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.appmaster.web.rest.application.resources.ContentCache;
import org.apache.slider.server.services.security.CertificateManager;
import java.util.Map;
/**
* Interface to pass information from the Slider AppMaster to the WebApp
@ -44,18 +39,6 @@ public interface WebAppApi {
*/
ProviderService getProviderService();
/**
* The {@link CertificateManager} for the current cluster
*/
CertificateManager getCertificateManager();
/**
* Generate a mapping from role name to its {@link RoleStatus}. Be aware that this
* is a computed value and not just a getter
*/
Map<String, RoleStatus> getRoleStatusByName();
/**
* Registry operations accessor
* @return registry access
@ -74,12 +57,6 @@ public interface WebAppApi {
*/
QueueAccess getQueues();
/**
* API for AM operations
* @return current operations implementation
*/
AppMasterActionOperations getAMOperations();
/**
* Local cache of content
* @return the cache

View File

@ -21,17 +21,11 @@ import org.apache.slider.providers.ProviderService;
import org.apache.slider.server.appmaster.AppMasterActionOperations;
import org.apache.slider.server.appmaster.actions.QueueAccess;
import org.apache.slider.server.appmaster.management.MetricsAndMonitoring;
import org.apache.slider.server.appmaster.state.RoleStatus;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.appmaster.web.rest.application.resources.ContentCache;
import org.apache.slider.server.services.security.CertificateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
@ -42,7 +36,6 @@ public class WebAppApiImpl implements WebAppApi {
protected final StateAccessForProviders appState;
protected final ProviderService provider;
protected final CertificateManager certificateManager;
private final RegistryOperations registryOperations;
private final MetricsAndMonitoring metricsAndMonitoring;
private final QueueAccess queues;
@ -50,13 +43,9 @@ public class WebAppApiImpl implements WebAppApi {
private final ContentCache contentCache;
public WebAppApiImpl(StateAccessForProviders appState,
ProviderService provider,
CertificateManager certificateManager,
RegistryOperations registryOperations,
MetricsAndMonitoring metricsAndMonitoring,
QueueAccess queues,
AppMasterActionOperations appMasterOperations,
ContentCache contentCache) {
ProviderService provider, RegistryOperations registryOperations,
MetricsAndMonitoring metricsAndMonitoring, QueueAccess queues,
AppMasterActionOperations appMasterOperations, ContentCache contentCache) {
this.appMasterOperations = appMasterOperations;
this.contentCache = contentCache;
checkNotNull(appState);
@ -66,7 +55,6 @@ public class WebAppApiImpl implements WebAppApi {
this.registryOperations = registryOperations;
this.appState = appState;
this.provider = provider;
this.certificateManager = certificateManager;
this.metricsAndMonitoring = metricsAndMonitoring;
}
@ -80,21 +68,6 @@ public class WebAppApiImpl implements WebAppApi {
return provider;
}
@Override
public CertificateManager getCertificateManager() {
return certificateManager;
}
@Override
public Map<String,RoleStatus> getRoleStatusByName() {
List<RoleStatus> roleStatuses = appState.cloneRoleStatusList();
Map<String, RoleStatus> map = new TreeMap<>();
for (RoleStatus status : roleStatuses) {
map.put(status.getName(), status);
}
return map;
}
@Override
public RegistryOperations getRegistryOperations() {
return registryOperations;
@ -110,10 +83,6 @@ public class WebAppApiImpl implements WebAppApi {
return queues;
}
@Override
public AppMasterActionOperations getAMOperations() {
return appMasterOperations;
}
@Override
public ContentCache getContentCache() {

View File

@ -1,98 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
*
*/
public abstract class AbstractSecurityStoreGenerator implements
SecurityStoreGenerator {
private static final Logger LOG =
LoggerFactory.getLogger(AbstractSecurityStoreGenerator.class);
protected CertificateManager certificateMgr;
public AbstractSecurityStoreGenerator(CertificateManager certificateMgr) {
this.certificateMgr = certificateMgr;
}
protected String getStorePassword(Map<String, List<String>> credentials,
MapOperations compOps, String role)
throws SliderException, IOException {
String password = getPassword(compOps);
if (password == null) {
// need to leverage credential provider
String alias = getAlias(compOps);
LOG.debug("Alias {} found for role {}", alias, role);
if (alias == null) {
throw new SliderException("No store password or credential provider "
+ "alias found");
}
if (credentials.isEmpty()) {
LOG.info("Credentials can not be retrieved for store generation since "
+ "no CP paths are configured");
}
synchronized (this) {
for (Map.Entry<String, List<String>> cred : credentials.entrySet()) {
String provider = cred.getKey();
Configuration c = new Configuration();
c.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, provider);
LOG.debug("Configured provider {}", provider);
CredentialProvider cp =
CredentialProviderFactory.getProviders(c).get(0);
LOG.debug("Aliases: {}", cp.getAliases());
char[] credential = c.getPassword(alias);
if (credential != null) {
LOG.info("Credential found for role {}", role);
return String.valueOf(credential);
}
}
}
if (password == null) {
LOG.info("No store credential found for alias {}. "
+ "Generation of store for {} is not possible.", alias, role);
}
}
return password;
}
@Override
public boolean isStoreRequested(MapOperations compOps) {
return compOps.getOptionBool(SliderKeys.COMP_STORES_REQUIRED_KEY, false);
}
abstract String getPassword(MapOperations compOps);
abstract String getAlias(MapOperations compOps);
}

View File

@ -1,495 +0,0 @@
/**
* 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.slider.server.services.security;
import com.google.inject.Singleton;
import org.apache.commons.io.FileUtils;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.text.MessageFormat;
@Singleton
public class CertificateManager {
private static final Logger LOG =
LoggerFactory.getLogger(CertificateManager.class);
private static final String GEN_SRVR_KEY = "openssl genrsa -des3 " +
"-passout pass:{0} -out {1}" + File.separator + "{2} 4096 ";
private static final String GEN_SRVR_REQ = "openssl req -passin pass:{0} " +
"-new -key {1}" + File.separator + "{2} -out {1}" + File.separator +
"{5} -config {1}" + File.separator + "ca.config " +
"-subj {6} -batch";
private static final String SIGN_SRVR_CRT = "openssl ca -create_serial " +
"-out {1}" + File.separator + "{3} -days 365 -keyfile {1}" + File.separator
+ "{2} -key {0} -selfsign -extensions jdk7_ca -config {1}" + File.separator
+ "ca.config -batch -infiles {1}" + File.separator + "{5}";
private static final String EXPRT_KSTR = "openssl pkcs12 -export" +
" -in {2}" + File.separator + "{4} -inkey {2}" + File.separator +
"{3} -certfile {2}" + File.separator + "{4} -out {2}" + File.separator +
"{5} -password pass:{1} -passin pass:{0} \n";
private static final String REVOKE_AGENT_CRT = "openssl ca " +
"-config {0}" + File.separator + "ca.config -keyfile {0}" +
File.separator + "{4} -revoke {0}" + File.separator + "{2} -batch " +
"-passin pass:{3} -cert {0}" + File.separator + "{5}";
private static final String SIGN_AGENT_CRT = "openssl ca -config " +
"{0}" + File.separator + "ca.config -in {0}" + File.separator +
"{1} -out {0}" + File.separator + "{2} -batch -passin pass:{3} " +
"-keyfile {0}" + File.separator + "{4} -cert {0}" + File.separator + "{5}";
private static final String GEN_AGENT_KEY="openssl req -new -newkey " +
"rsa:1024 -nodes -keyout {0}" + File.separator +
"{2}.key -subj {1} -out {0}" + File.separator + "{2}.csr " +
"-config {3}" + File.separator + "ca.config ";
private String passphrase;
private String applicationName;
public void initialize(MapOperations compOperations) throws SliderException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
hostname = "localhost";
}
this.initialize(compOperations, hostname, null, null);
}
/**
* Verify that root certificate exists, generate it otherwise.
*/
public void initialize(MapOperations compOperations,
String hostname, String containerId,
String appName) throws SliderException {
SecurityUtils.initializeSecurityParameters(compOperations);
LOG.info("Initialization of root certificate");
boolean certExists = isCertExists();
LOG.info("Certificate exists:" + certExists);
this.applicationName = appName;
if (!certExists) {
generateAMKeystore(hostname, containerId);
}
}
/**
* Checks root certificate state.
* @return "true" if certificate exists
*/
private boolean isCertExists() {
String srvrKstrDir = SecurityUtils.getSecurityDir();
String srvrCrtName = SliderKeys.CRT_FILE_NAME;
File certFile = new File(srvrKstrDir + File.separator + srvrCrtName);
LOG.debug("srvrKstrDir = " + srvrKstrDir);
LOG.debug("srvrCrtName = " + srvrCrtName);
LOG.debug("certFile = " + certFile.getAbsolutePath());
return certFile.exists();
}
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
class StreamConsumer extends Thread
{
InputStream is;
boolean logOutput;
StreamConsumer(InputStream is, boolean logOutput)
{
this.is = is;
this.logOutput = logOutput;
}
StreamConsumer(InputStream is)
{
this(is, false);
}
public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is,
Charset.forName("UTF8"));
BufferedReader br = new BufferedReader(isr);
String line;
while ( (line = br.readLine()) != null)
if (logOutput) {
LOG.info(line);
}
} catch (IOException e)
{
LOG.error("Error during processing of process stream", e);
}
}
}
/**
* Runs os command
*
* @return command execution exit code
*/
private int runCommand(String command) throws SliderException {
int exitCode = -1;
String line = null;
Process process = null;
BufferedReader br= null;
try {
process = Runtime.getRuntime().exec(command);
StreamConsumer outputConsumer =
new StreamConsumer(process.getInputStream(), true);
StreamConsumer errorConsumer =
new StreamConsumer(process.getErrorStream(), true);
outputConsumer.start();
errorConsumer.start();
try {
process.waitFor();
SecurityUtils.logOpenSslExitCode(command, process.exitValue());
exitCode = process.exitValue();
if (exitCode != 0) {
throw new SliderException(exitCode, "Error running command %s", command);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
return exitCode;//some exception occurred
}
public synchronized void generateContainerCertificate(String hostname,
String identifier) {
LOG.info("Generation of certificate for {}", hostname);
String srvrKstrDir = SecurityUtils.getSecurityDir();
Object[] scriptArgs = {srvrKstrDir, getSubjectDN(hostname, identifier,
this.applicationName), identifier, SecurityUtils.getSecurityDir()};
try {
String command = MessageFormat.format(GEN_AGENT_KEY, scriptArgs);
runCommand(command);
signAgentCertificate(identifier);
} catch (SliderException e) {
LOG.error("Error generating the agent certificate", e);
}
}
public synchronized SecurityStore generateContainerKeystore(String hostname,
String requesterId,
String role,
String keystorePass)
throws SliderException {
LOG.info("Generation of container keystore for container {} on {}",
requesterId, hostname);
generateContainerCertificate(hostname, requesterId);
// come up with correct args to invoke keystore command
String srvrCrtPass = SecurityUtils.getKeystorePass();
String srvrKstrDir = SecurityUtils.getSecurityDir();
String containerCrtName = requesterId + ".crt";
String containerKeyName = requesterId + ".key";
String kstrName = getKeystoreFileName(requesterId, role);
Object[] scriptArgs = {srvrCrtPass, keystorePass, srvrKstrDir,
containerKeyName, containerCrtName, kstrName};
String command = MessageFormat.format(EXPRT_KSTR, scriptArgs);
runCommand(command);
return new SecurityStore(new File(srvrKstrDir, kstrName),
SecurityStore.StoreType.keystore);
}
private static String getKeystoreFileName(String containerId,
String role) {
return String.format("keystore-%s-%s.p12", containerId,
role != null ? role : "");
}
private void generateAMKeystore(String hostname, String containerId)
throws SliderException {
LOG.info("Generation of server certificate");
String srvrKstrDir = SecurityUtils.getSecurityDir();
String srvrCrtName = SliderKeys.CRT_FILE_NAME;
String srvrCsrName = SliderKeys.CSR_FILE_NAME;
String srvrKeyName = SliderKeys.KEY_FILE_NAME;
String kstrName = SliderKeys.KEYSTORE_FILE_NAME;
String srvrCrtPass = SecurityUtils.getKeystorePass();
Object[] scriptArgs = {srvrCrtPass, srvrKstrDir, srvrKeyName,
srvrCrtName, kstrName, srvrCsrName, getSubjectDN(hostname, containerId,
this.applicationName)};
String command = MessageFormat.format(GEN_SRVR_KEY, scriptArgs);
runCommand(command);
command = MessageFormat.format(GEN_SRVR_REQ, scriptArgs);
runCommand(command);
command = MessageFormat.format(SIGN_SRVR_CRT, scriptArgs);
runCommand(command);
Object[] keystoreArgs = {srvrCrtPass, srvrCrtPass, srvrKstrDir, srvrKeyName,
srvrCrtName, kstrName, srvrCsrName};
command = MessageFormat.format(EXPRT_KSTR, keystoreArgs);
runCommand(command);
}
public SecurityStore generateContainerTruststore(String containerId,
String role,
String truststorePass)
throws SliderException {
String srvrKstrDir = SecurityUtils.getSecurityDir();
String srvrCrtName = SliderKeys.CRT_FILE_NAME;
String srvrCsrName = SliderKeys.CSR_FILE_NAME;
String srvrKeyName = SliderKeys.KEY_FILE_NAME;
String kstrName = getTruststoreFileName(role, containerId);
String srvrCrtPass = SecurityUtils.getKeystorePass();
Object[] scriptArgs = {srvrCrtPass, truststorePass, srvrKstrDir, srvrKeyName,
srvrCrtName, kstrName, srvrCsrName};
String command = MessageFormat.format(EXPRT_KSTR, scriptArgs);
runCommand(command);
return new SecurityStore(new File(srvrKstrDir, kstrName),
SecurityStore.StoreType.truststore);
}
private static String getTruststoreFileName(String role, String containerId) {
return String.format("truststore-%s-%s.p12", containerId,
role != null ? role : "");
}
/**
* Returns server certificate content
* @return string with server certificate content
*/
public String getServerCert() {
File certFile = getServerCertficateFilePath();
String srvrCrtContent = null;
try {
srvrCrtContent = FileUtils.readFileToString(certFile);
} catch (IOException e) {
LOG.error(e.getMessage());
}
return srvrCrtContent;
}
public static File getServerCertficateFilePath() {
return new File(String.format("%s%s%s",
SecurityUtils.getSecurityDir(),
File.separator,
SliderKeys.CRT_FILE_NAME));
}
public static File getAgentCertficateFilePath(String containerId) {
return new File(String.format("%s%s%s.crt",
SecurityUtils.getSecurityDir(),
File.separator,
containerId));
}
public static File getContainerKeystoreFilePath(String containerId,
String role) {
return new File(SecurityUtils.getSecurityDir(), getKeystoreFileName(
containerId,
role
));
}
public static File getContainerTruststoreFilePath(String role,
String containerId) {
return new File(SecurityUtils.getSecurityDir(),
getTruststoreFileName(role, containerId));
}
public static File getAgentKeyFilePath(String containerId) {
return new File(String.format("%s%s%s.key",
SecurityUtils.getSecurityDir(),
File.separator,
containerId));
}
/**
* Signs agent certificate
* Adds agent certificate to server keystore
* @return string with agent signed certificate content
*/
public synchronized SignCertResponse signAgentCrt(String agentHostname,
String agentCrtReqContent,
String passphraseAgent) {
SignCertResponse response = new SignCertResponse();
LOG.info("Signing of agent certificate");
LOG.info("Verifying passphrase");
if (!this.passphrase.equals(passphraseAgent.trim())) {
LOG.warn("Incorrect passphrase from the agent");
response.setResult(SignCertResponse.ERROR_STATUS);
response.setMessage("Incorrect passphrase from the agent");
return response;
}
String srvrKstrDir = SecurityUtils.getSecurityDir();
String srvrCrtPass = SecurityUtils.getKeystorePass();
String srvrCrtName = SliderKeys.CRT_FILE_NAME;
String srvrKeyName = SliderKeys.KEY_FILE_NAME;
String agentCrtReqName = agentHostname + ".csr";
String agentCrtName = agentHostname + ".crt";
Object[] scriptArgs = {srvrKstrDir, agentCrtReqName, agentCrtName,
srvrCrtPass, srvrKeyName, srvrCrtName};
//Revoke previous agent certificate if exists
File agentCrtFile = new File(srvrKstrDir + File.separator + agentCrtName);
String command = null;
if (agentCrtFile.exists()) {
LOG.info("Revoking of " + agentHostname + " certificate.");
command = MessageFormat.format(REVOKE_AGENT_CRT, scriptArgs);
try {
runCommand(command);
} catch (SliderException e) {
int commandExitCode = e.getExitCode();
response.setResult(SignCertResponse.ERROR_STATUS);
response.setMessage(
SecurityUtils.getOpenSslCommandResult(command, commandExitCode));
return response;
}
}
File agentCrtReqFile = new File(srvrKstrDir + File.separator +
agentCrtReqName);
try {
FileUtils.writeStringToFile(agentCrtReqFile, agentCrtReqContent);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
command = MessageFormat.format(SIGN_AGENT_CRT, scriptArgs);
LOG.debug(SecurityUtils.hideOpenSslPassword(command));
try {
runCommand(command);
} catch (SliderException e) {
int commandExitCode = e.getExitCode();
response.setResult(SignCertResponse.ERROR_STATUS);
response.setMessage(
SecurityUtils.getOpenSslCommandResult(command, commandExitCode));
return response;
}
String agentCrtContent = "";
try {
agentCrtContent = FileUtils.readFileToString(agentCrtFile);
} catch (IOException e) {
e.printStackTrace();
LOG.error("Error reading signed agent certificate");
response.setResult(SignCertResponse.ERROR_STATUS);
response.setMessage("Error reading signed agent certificate");
return response;
}
response.setResult(SignCertResponse.OK_STATUS);
response.setSignedCa(agentCrtContent);
//LOG.info(ShellCommandUtil.getOpenSslCommandResult(command, commandExitCode));
return response;
}
private String signAgentCertificate (String containerId)
throws SliderException {
String srvrKstrDir = SecurityUtils.getSecurityDir();
String srvrCrtPass = SecurityUtils.getKeystorePass();
String srvrCrtName = SliderKeys.CRT_FILE_NAME;
String srvrKeyName = SliderKeys.KEY_FILE_NAME;
String agentCrtReqName = containerId + ".csr";
String agentCrtName = containerId + ".crt";
// server certificate must exist already
if (!(new File(srvrKstrDir, srvrCrtName).exists())) {
throw new SliderException("CA certificate not generated");
}
Object[] scriptArgs = {srvrKstrDir, agentCrtReqName, agentCrtName,
srvrCrtPass, srvrKeyName, srvrCrtName};
//Revoke previous agent certificate if exists
File agentCrtFile = new File(srvrKstrDir + File.separator + agentCrtName);
String command;
if (agentCrtFile.exists()) {
LOG.info("Revoking of " + containerId + " certificate.");
command = MessageFormat.format(REVOKE_AGENT_CRT, scriptArgs);
runCommand(command);
}
command = MessageFormat.format(SIGN_AGENT_CRT, scriptArgs);
LOG.debug(SecurityUtils.hideOpenSslPassword(command));
runCommand(command);
return agentCrtName;
}
private String getSubjectDN(String hostname, String containerId,
String appName) {
return String.format("/CN=%s%s%s",
hostname,
containerId != null ? "/OU=" + containerId : "",
appName != null ? "/OU=" + appName : "");
}
}

View File

@ -1,64 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import java.io.File;
import java.io.IOException;
/**
*
*/
public class KeystoreGenerator extends AbstractSecurityStoreGenerator {
public KeystoreGenerator(CertificateManager certificateMgr) {
super(certificateMgr);
}
@Override
public SecurityStore generate(String hostname, String containerId,
AggregateConf instanceDefinition,
MapOperations compOps, String role)
throws SliderException, IOException {
SecurityStore keystore = null;
String password = getStorePassword(
instanceDefinition.getAppConf().credentials, compOps, role);
if (password != null) {
keystore =
certificateMgr.generateContainerKeystore(hostname, containerId, role,
password);
}
return keystore;
}
@Override
String getPassword(MapOperations compOps) {
return compOps.get(
compOps.get(SliderKeys.COMP_KEYSTORE_PASSWORD_PROPERTY_KEY));
}
@Override
String getAlias(MapOperations compOps) {
return compOps.getOption(SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_KEY,
SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_DEFAULT);
}
}

View File

@ -1,66 +0,0 @@
/*
* 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.slider.server.services.security;
import java.io.File;
/**
*
*/
public class SecurityStore {
private File file;
public enum StoreType {truststore, keystore}
private StoreType type;
public String getType() {
return type.name();
}
public File getFile() {
return file;
}
public SecurityStore(File file,
StoreType type) {
this.file = file;
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SecurityStore that = (SecurityStore) o;
if (file != null ? !file.equals(that.file) : that.file != null)
return false;
if (type != that.type) return false;
return true;
}
@Override
public int hashCode() {
int result = file != null ? file.hashCode() : 0;
result = 31 * result + (type != null ? type.hashCode() : 0);
return result;
}
}

View File

@ -1,40 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import java.io.File;
import java.io.IOException;
/**
*
*/
public interface SecurityStoreGenerator {
SecurityStore generate(String hostname,
String containerId,
AggregateConf instanceDefinition,
MapOperations compOps,
String role)
throws SliderException, IOException;
boolean isStoreRequested(MapOperations compOps);
}

View File

@ -1,256 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RawLocalFileSystem;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.common.SliderXmlConfKeys;
import org.apache.slider.core.conf.MapOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
//import java.nio.file.Files;
//import java.nio.file.Path;
//import java.nio.file.Paths;
//import java.nio.file.attribute.PosixFilePermission;
//import java.nio.file.attribute.PosixFilePermissions;
/**
*
*/
public class SecurityUtils {
private static final Logger LOG =
LoggerFactory.getLogger(SecurityUtils.class);
private static String CA_CONFIG_CONTENTS = "HOME = .\n"
+ "RANDFILE = $ENV::HOME/.rnd\n\n"
+ "[ ca ]\n"
+ "default_ca = CA_CLIENT\n"
+ "[ CA_CLIENT ]\n"
+ "dir = ${SEC_DIR}/db\n"
+ "certs = $dir/certs\n"
+ "new_certs_dir = $dir/newcerts\n"
+ "\n"
+ "database = $dir/index.txt\n"
+ "serial = $dir/serial\n"
+ "default_days = 365 \n"
+ "\n"
+ "default_crl_days = 7 \n"
+ "default_md = sha256 \n"
+ "\n"
+ "policy = policy_anything \n"
+ "\n"
+ "[ policy_anything ]\n"
+ "countryName = optional\n"
+ "stateOrProvinceName = optional\n"
+ "localityName = optional\n"
+ "organizationName = optional\n"
+ "organizationalUnitName = optional\n"
+ "commonName = optional\n"
+ "emailAddress = optional\n"
+ "\n"
+ "[req]\n"
+ "distinguished_name = req_distinguished_name\n"
+ "\n"
+ "[ req_distinguished_name ]\n"
+ "\n"
+ "[ jdk7_ca ]\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ "basicConstraints = CA:true\n";
private static final String PASS_TOKEN = "pass:";
private static String keystorePass;
private static String securityDir;
public static void logOpenSslExitCode(String command, int exitCode) {
if (exitCode == 0) {
LOG.info(getOpenSslCommandResult(command, exitCode));
} else {
LOG.warn(getOpenSslCommandResult(command, exitCode));
}
}
public static String hideOpenSslPassword(String command){
int start = command.indexOf(PASS_TOKEN);
while (start >= 0) {
start += PASS_TOKEN.length();
CharSequence cs = command.subSequence(start, command.indexOf(" ", start));
command = command.replace(cs, "****");
start = command.indexOf(PASS_TOKEN, start + 1);
}
return command;
}
public static String getOpenSslCommandResult(String command, int exitCode) {
return new StringBuilder().append("Command ")
.append(hideOpenSslPassword(command))
.append(" was finished with exit code: ")
.append(exitCode).append(" - ")
.append(getOpenSslExitCodeDescription(exitCode)).toString();
}
private static String getOpenSslExitCodeDescription(int exitCode) {
switch (exitCode) {
case 0: {
return "the operation was completed successfully.";
}
case 1: {
return "an error occurred parsing the command options.";
}
case 2: {
return "one of the input files could not be read.";
}
case 3: {
return "an error occurred creating the PKCS#7 file or when reading the MIME message.";
}
case 4: {
return "an error occurred decrypting or verifying the message.";
}
case 5: {
return "the message was verified correctly but an error occurred writing out the signers certificates.";
}
default:
return "unsupported code";
}
}
public static void writeCaConfigFile(String path) throws IOException {
String contents = CA_CONFIG_CONTENTS.replace("${SEC_DIR}", path);
FileUtils.writeStringToFile(new File(path, "ca.config"), contents);
}
public static String getKeystorePass() {
return keystorePass;
}
public static String getSecurityDir() {
return securityDir;
}
public static void initializeSecurityParameters(MapOperations configMap) {
initializeSecurityParameters(configMap, false);
}
public static void initializeSecurityParameters(MapOperations configMap,
boolean persistPassword) {
String keyStoreLocation = configMap.getOption(
SliderXmlConfKeys.KEY_KEYSTORE_LOCATION, getDefaultKeystoreLocation());
if (keyStoreLocation == null) {
LOG.error(SliderXmlConfKeys.KEY_KEYSTORE_LOCATION
+ " is not specified. Unable to initialize security params.");
return;
}
File secDirFile = new File(keyStoreLocation).getParentFile();
if (!secDirFile.exists()) {
// create entire required directory structure
File dbDir = new File(secDirFile, "db");
File newCertsDir = new File(dbDir, "newcerts");
newCertsDir.mkdirs();
RawLocalFileSystem fileSystem = null;
try {
fileSystem = new RawLocalFileSystem();
FsPermission permissions = new FsPermission(FsAction.ALL, FsAction.NONE,
FsAction.NONE);
fileSystem.setPermission(new Path(dbDir.getAbsolutePath()),
permissions);
fileSystem.setPermission(new Path(dbDir.getAbsolutePath()), permissions);
fileSystem.setPermission(new Path(newCertsDir.getAbsolutePath()),
permissions);
File indexFile = new File(dbDir, "index.txt");
indexFile.createNewFile();
SecurityUtils.writeCaConfigFile(secDirFile.getAbsolutePath().replace('\\', '/'));
} catch (IOException e) {
LOG.error("Unable to create SSL configuration directories/files", e);
} finally {
if (fileSystem != null) {
try {
fileSystem.close();
} catch (IOException e) {
LOG.warn("Unable to close fileSystem", e);
}
}
}
// need to create the password
}
keystorePass = getKeystorePassword(secDirFile, persistPassword);
securityDir = secDirFile.getAbsolutePath();
}
private static String getKeystorePassword(File secDirFile,
boolean persistPassword) {
File passFile = new File(secDirFile, SliderKeys.CRT_PASS_FILE_NAME);
String password = null;
if (!passFile.exists()) {
LOG.info("Generating keystore password");
password = RandomStringUtils.randomAlphanumeric(
Integer.valueOf(SliderKeys.PASS_LEN));
if (persistPassword) {
try {
FileUtils.writeStringToFile(passFile, password);
passFile.setWritable(true);
passFile.setReadable(true);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(
"Error creating certificate password file");
}
}
} else {
LOG.info("Reading password from existing file");
try {
password = FileUtils.readFileToString(passFile);
password = password.replaceAll("\\p{Cntrl}", "");
} catch (IOException e) {
e.printStackTrace();
}
}
return password;
}
private static String getDefaultKeystoreLocation() {
File workDir = null;
try {
workDir = new File(FileUtils.getTempDirectory().getAbsolutePath()
+ "/sec" + System.currentTimeMillis());
if (!workDir.mkdirs()) {
throw new IOException("Unable to create temporary security directory");
}
} catch (IOException e) {
LOG.warn("Unable to create security directory");
return null;
}
return new StringBuilder().append(workDir.getAbsolutePath())
.append(File.separator)
.append(SliderKeys.SECURITY_DIR)
.append(File.separator)
.append(SliderKeys.KEYSTORE_FILE_NAME).toString();
}
}

View File

@ -1,67 +0,0 @@
/**
* 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.slider.server.services.security;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
*
* Sign certificate response data model.
*
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {})
public class SignCertResponse {
public static final String ERROR_STATUS = "ERROR";
public static final String OK_STATUS = "OK";
@XmlElement
private String result;
@XmlElement
private String signedCa;
@XmlElement
private String message;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getSignedCa() {
return signedCa;
}
public void setSignedCa(String signedCa) {
this.signedCa = signedCa;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -1,54 +0,0 @@
/**
* 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.slider.server.services.security;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
*
* Sign certificate request data model.
*
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {})
public class SignMessage {
@XmlElement
private String csr;
@XmlElement
private String passphrase;
public String getCsr() {
return csr;
}
public void setCsr(String csr) {
this.csr = csr;
}
public String getPassphrase() {
return passphrase;
}
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
}

View File

@ -1,68 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class StoresGenerator {
static CertificateManager certMgr = new CertificateManager();
private static SecurityStoreGenerator[] GENERATORS = {
new KeystoreGenerator(certMgr), new TruststoreGenerator(certMgr)
};
public static SecurityStore[] generateSecurityStores(String hostname,
String containerId,
String role,
AggregateConf instanceDefinition,
MapOperations compOps)
throws SliderException, IOException {
//discover which stores need generation based on the passwords configured
List<SecurityStore> files = new ArrayList<SecurityStore>();
for (SecurityStoreGenerator generator : GENERATORS) {
if (generator.isStoreRequested(compOps)) {
SecurityStore store = generator.generate(hostname,
containerId,
instanceDefinition,
compOps,
role);
if (store != null) {
files.add(store);
}
}
}
if (files.isEmpty()) {
throw new SliderException("Security stores were requested but none were "
+ "generated. Check the AM logs and ensure "
+ "passwords are configured for the components "
+ "requiring the stores.");
}
return files.toArray(new SecurityStore[files.size()]);
}
}

View File

@ -1,62 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import java.io.IOException;
/**
*
*/
public class TruststoreGenerator extends AbstractSecurityStoreGenerator {
public TruststoreGenerator(CertificateManager certificateMgr) {
super(certificateMgr);
}
@Override
public SecurityStore generate(String hostname, String containerId,
AggregateConf instanceDefinition,
MapOperations compOps, String role)
throws SliderException, IOException {
SecurityStore truststore = null;
String password = getStorePassword(
instanceDefinition.getAppConf().credentials, compOps, role);
if (password != null) {
truststore = certificateMgr.generateContainerTruststore(containerId,
role, password);
}
return truststore;
}
@Override
String getPassword(MapOperations compOps) {
return compOps.get(
compOps.get(SliderKeys.COMP_TRUSTSTORE_PASSWORD_PROPERTY_KEY));
}
@Override
String getAlias(MapOperations compOps) {
return compOps.getOption(SliderKeys.COMP_TRUSTSTORE_PASSWORD_ALIAS_KEY,
SliderKeys.COMP_TRUSTSTORE_PASSWORD_ALIAS_DEFAULT);
}
}

View File

@ -175,10 +175,4 @@ service SliderClusterProtocolPB {
// ConfTree getLiveResources
rpc getLiveResources(EmptyPayloadProto)
returns(WrappedJsonProto);
rpc getClientCertificateStore(GetCertificateStoreRequestProto)
returns(GetCertificateStoreResponseProto);
}

View File

@ -1,540 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
import org.apache.slider.Slider;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.common.SliderXmlConfKeys;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
/**
*
*/
public class TestCertificateManager {
@Rule
public TemporaryFolder workDir = new TemporaryFolder();
private File secDir;
private CertificateManager certMan;
@Before
public void setup() throws Exception {
certMan = new CertificateManager();
MapOperations compOperations = new MapOperations();
secDir = new File(workDir.getRoot(), SliderKeys.SECURITY_DIR);
File keystoreFile = new File(secDir, SliderKeys.KEYSTORE_FILE_NAME);
compOperations.put(SliderXmlConfKeys.KEY_KEYSTORE_LOCATION,
keystoreFile.getAbsolutePath());
certMan.initialize(compOperations, "cahost", null, null);
}
@Test
public void testServerCertificateGenerated() throws Exception {
File serverCrt = new File(secDir, SliderKeys.CRT_FILE_NAME);
Assert.assertTrue("Server CRD does not exist:" + serverCrt,
serverCrt.exists());
}
@Test
public void testAMKeystoreGenerated() throws Exception {
File keystoreFile = new File(secDir, SliderKeys.KEYSTORE_FILE_NAME);
Assert.assertTrue("Keystore does not exist: " + keystoreFile,
keystoreFile.exists());
InputStream is = null;
try {
is = new FileInputStream(keystoreFile);
KeyStore keystore = KeyStore.getInstance("pkcs12");
String password = SecurityUtils.getKeystorePass();
keystore.load(is, password.toCharArray());
Certificate certificate = keystore.getCertificate(
keystore.aliases().nextElement());
Assert.assertNotNull(certificate);
if (certificate instanceof X509Certificate) {
X509Certificate x509cert = (X509Certificate) certificate;
// Get subject
Principal principal = x509cert.getSubjectDN();
String subjectDn = principal.getName();
Assert.assertEquals("wrong DN",
"CN=cahost",
subjectDn);
// Get issuer
principal = x509cert.getIssuerDN();
String issuerDn = principal.getName();
Assert.assertEquals("wrong Issuer DN",
"CN=cahost",
issuerDn);
}
} finally {
if(null != is) {
is.close();
}
}
}
@Test
public void testContainerCertificateGeneration() throws Exception {
certMan.generateContainerCertificate("testhost", "container1");
Assert.assertTrue("container certificate not generated",
new File(secDir, "container1.crt").exists());
}
@Test
public void testContainerKeystoreGeneration() throws Exception {
SecurityStore keystoreFile = certMan.generateContainerKeystore("testhost",
"container1",
"component1",
"password");
validateKeystore(keystoreFile.getFile(), "testhost", "cahost");
}
private void validateKeystore(File keystoreFile, String certHostname,
String issuerHostname)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
Assert.assertTrue("container keystore not generated",
keystoreFile.exists());
InputStream is = null;
try {
is = new FileInputStream(keystoreFile);
KeyStore keystore = KeyStore.getInstance("pkcs12");
String password = "password";
keystore.load(is, password.toCharArray());
Certificate certificate = keystore.getCertificate(
keystore.aliases().nextElement());
Assert.assertNotNull(certificate);
if (certificate instanceof X509Certificate) {
X509Certificate x509cert = (X509Certificate) certificate;
// Get subject
Principal principal = x509cert.getSubjectDN();
String subjectDn = principal.getName();
Assert.assertEquals("wrong DN", "CN=" + certHostname + ", OU=container1",
subjectDn);
// Get issuer
principal = x509cert.getIssuerDN();
String issuerDn = principal.getName();
Assert.assertEquals("wrong Issuer DN",
"CN=" + issuerHostname,
issuerDn);
}
} finally {
if(null != is) {
is.close();
}
}
}
@Test
public void testContainerKeystoreGenerationViaStoresGenerator() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = new MapOperations();
instanceDefinition.getAppConf().components.put("component1", compOps);
compOps.put(SliderKeys.COMP_KEYSTORE_PASSWORD_PROPERTY_KEY,
"app1.component1.password.property");
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
instanceDefinition.getAppConf().global.put(
"app1.component1.password.property", "password");
instanceDefinition.resolve();
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
validateKeystore(files[0].getFile(), "testhost", "cahost");
}
@Test
public void testContainerKeystoreGenerationViaStoresGeneratorUsingGlobalProps() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = new MapOperations();
instanceDefinition.getAppConf().components.put("component1", compOps);
compOps.put(SliderKeys.COMP_KEYSTORE_PASSWORD_PROPERTY_KEY,
"app1.component1.password.property");
instanceDefinition.getAppConf().global.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
compOps.put(
"app1.component1.password.property", "password");
instanceDefinition.resolve();
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
validateKeystore(files[0].getFile(), "testhost", "cahost");
}
@Test
public void testContainerKeystoreGenerationViaStoresGeneratorOverrideGlobalSetting() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = setupComponentOptions(true, null,
"app1.component1.password.property",
null, null);
instanceDefinition.getAppConf().components.put("component1", compOps);
instanceDefinition.getAppConf().global.put(
"app1.component1.password.property", "password");
instanceDefinition.getAppConf().global.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "false");
instanceDefinition.resolve();
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
validateKeystore(files[0].getFile(), "testhost", "cahost");
}
@Test
public void testContainerTrusttoreGeneration() throws Exception {
SecurityStore keystoreFile =
certMan.generateContainerKeystore("testhost",
"container1",
"component1",
"keypass");
Assert.assertTrue("container keystore not generated",
keystoreFile.getFile().exists());
SecurityStore truststoreFile =
certMan.generateContainerTruststore("container1",
"component1", "trustpass"
);
Assert.assertTrue("container truststore not generated",
truststoreFile.getFile().exists());
validateTruststore(keystoreFile.getFile(), truststoreFile.getFile());
}
@Test
public void testContainerGenerationUsingStoresGeneratorNoTruststore() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = new MapOperations();
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
compOps.put(SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_KEY,
"test.keystore.password");
setupCredentials(instanceDefinition, "test.keystore.password", null);
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
File keystoreFile = CertificateManager.getContainerKeystoreFilePath(
"container1", "component1");
Assert.assertTrue("container keystore not generated",
keystoreFile.exists());
Assert.assertTrue("keystore not in returned list",
Arrays.asList(files).contains(new SecurityStore(keystoreFile,
SecurityStore.StoreType.keystore)));
File truststoreFile =
CertificateManager.getContainerTruststoreFilePath("component1",
"container1");
Assert.assertFalse("container truststore generated",
truststoreFile.exists());
Assert.assertFalse("truststore in returned list",
Arrays.asList(files).contains(new SecurityStore(truststoreFile,
SecurityStore.StoreType.truststore)));
}
@Test
public void testContainerGenerationUsingStoresGeneratorJustTruststoreWithDefaultAlias() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = setupComponentOptions(true);
setupCredentials(instanceDefinition, null,
SliderKeys.COMP_TRUSTSTORE_PASSWORD_ALIAS_DEFAULT);
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
File keystoreFile = CertificateManager.getContainerKeystoreFilePath(
"container1", "component1");
Assert.assertFalse("container keystore generated",
keystoreFile.exists());
Assert.assertFalse("keystore in returned list",
Arrays.asList(files).contains(keystoreFile));
File truststoreFile =
CertificateManager.getContainerTruststoreFilePath("component1",
"container1");
Assert.assertTrue("container truststore not generated",
truststoreFile.exists());
Assert.assertTrue("truststore not in returned list",
Arrays.asList(files).contains(new SecurityStore(truststoreFile,
SecurityStore.StoreType.truststore)));
}
@Test
public void testContainerTrusttoreGenerationUsingStoresGenerator() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = setupComponentOptions(true,
"test.keystore.password",
null,
"test.truststore.password",
null);
setupCredentials(instanceDefinition, "test.keystore.password",
"test.truststore.password");
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 2, files.length);
File keystoreFile = CertificateManager.getContainerKeystoreFilePath(
"container1", "component1");
Assert.assertTrue("container keystore not generated",
keystoreFile.exists());
Assert.assertTrue("keystore not in returned list",
Arrays.asList(files).contains(new SecurityStore(keystoreFile,
SecurityStore.StoreType.keystore)));
File truststoreFile =
CertificateManager.getContainerTruststoreFilePath("component1",
"container1");
Assert.assertTrue("container truststore not generated",
truststoreFile.exists());
Assert.assertTrue("truststore not in returned list",
Arrays.asList(files).contains(new SecurityStore(truststoreFile,
SecurityStore.StoreType.truststore)));
validateTruststore(keystoreFile, truststoreFile);
}
private void setupCredentials(AggregateConf instanceDefinition,
String keyAlias, String trustAlias)
throws Exception {
Configuration conf = new Configuration();
final Path jksPath = new Path(SecurityUtils.getSecurityDir(), "test.jks");
final String ourUrl =
JavaKeyStoreProvider.SCHEME_NAME + "://file" + jksPath.toUri();
File file = new File(SecurityUtils.getSecurityDir(), "test.jks");
file.delete();
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, ourUrl);
instanceDefinition.getAppConf().credentials.put(ourUrl, new ArrayList<String>());
CredentialProvider provider =
CredentialProviderFactory.getProviders(conf).get(0);
// create new aliases
try {
if (keyAlias != null) {
char[] storepass = {'k', 'e', 'y', 'p', 'a', 's', 's'};
provider.createCredentialEntry(
keyAlias, storepass);
}
if (trustAlias != null) {
char[] trustpass = {'t', 'r', 'u', 's', 't', 'p', 'a', 's', 's'};
provider.createCredentialEntry(
trustAlias, trustpass);
}
// write out so that it can be found in checks
provider.flush();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
private MapOperations setupComponentOptions(boolean storesRequired) {
return this.setupComponentOptions(storesRequired, null, null, null, null);
}
private MapOperations setupComponentOptions(boolean storesRequired,
String keyAlias,
String keyPwd,
String trustAlias,
String trustPwd) {
MapOperations compOps = new MapOperations();
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY,
Boolean.toString(storesRequired));
if (keyAlias != null) {
compOps.put(SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_KEY,
"test.keystore.password");
}
if (trustAlias != null) {
compOps.put(SliderKeys.COMP_TRUSTSTORE_PASSWORD_ALIAS_KEY,
"test.truststore.password");
}
if (keyPwd != null) {
compOps.put(SliderKeys.COMP_KEYSTORE_PASSWORD_PROPERTY_KEY,
keyPwd);
}
if (trustPwd != null) {
compOps.put(SliderKeys.COMP_TRUSTSTORE_PASSWORD_PROPERTY_KEY,
trustPwd);
}
return compOps;
}
@Test
public void testContainerStoresGenerationKeystoreOnly() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = new MapOperations();
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
setupCredentials(instanceDefinition,
SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_DEFAULT, null);
SecurityStore[]
files = StoresGenerator.generateSecurityStores("testhost",
"container1",
"component1",
instanceDefinition,
compOps);
assertEquals("wrong number of stores", 1, files.length);
File keystoreFile = CertificateManager.getContainerKeystoreFilePath(
"container1", "component1");
Assert.assertTrue("container keystore not generated",
keystoreFile.exists());
Assert.assertTrue("keystore not in returned list",
Arrays.asList(files).contains(new SecurityStore(keystoreFile,
SecurityStore.StoreType.keystore)));
File truststoreFile =
CertificateManager.getContainerTruststoreFilePath("component1",
"container1");
Assert.assertFalse("container truststore generated",
truststoreFile.exists());
Assert.assertFalse("truststore in returned list",
Arrays.asList(files).contains(new SecurityStore(truststoreFile,
SecurityStore.StoreType.truststore)));
}
@Test
public void testContainerStoresGenerationMisconfiguration() throws Exception {
AggregateConf instanceDefinition = new AggregateConf();
MapOperations compOps = new MapOperations();
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
setupCredentials(instanceDefinition, "cant.be.found", null);
try {
StoresGenerator.generateSecurityStores("testhost", "container1",
"component1", instanceDefinition,
compOps);
Assert.fail("SliderException should have been generated");
} catch (SliderException e) {
// ignore - should be thrown
}
}
private void validateTruststore(File keystoreFile, File truststoreFile)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
InputStream keyis = null;
InputStream trustis = null;
try {
// create keystore
keyis = new FileInputStream(keystoreFile);
KeyStore keystore = KeyStore.getInstance("pkcs12");
String password = "keypass";
keystore.load(keyis, password.toCharArray());
// obtain server cert
Certificate certificate = keystore.getCertificate(
keystore.aliases().nextElement());
Assert.assertNotNull(certificate);
// create trust store from generated trust store file
trustis = new FileInputStream(truststoreFile);
KeyStore truststore = KeyStore.getInstance("pkcs12");
password = "trustpass";
truststore.load(trustis, password.toCharArray());
// validate keystore cert using trust store
TrustManagerFactory
trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(truststore);
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
X509TrustManager x509TrustManager = (X509TrustManager)trustManager;
x509TrustManager.checkServerTrusted(
new X509Certificate[] {(X509Certificate) certificate},
"RSA_EXPORT");
}
}
} finally {
if(null != keyis) {
keyis.close();
}
if(null != trustis) {
trustis.close();
}
}
}
}

View File

@ -1,156 +0,0 @@
/*
* 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.slider.server.services.security;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.common.SliderXmlConfKeys;
import org.apache.slider.core.conf.AggregateConf;
import org.apache.slider.core.conf.MapOperations;
import org.apache.slider.core.exceptions.SliderException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import static org.junit.Assert.assertTrue;
/**
*
*/
public class TestMultiThreadedStoreGeneration {
public static final int NUM_THREADS = 30;
@Rule
public TemporaryFolder workDir = new TemporaryFolder();;
private void setupCredentials(AggregateConf instanceDefinition,
String keyAlias, String trustAlias)
throws Exception {
Configuration conf = new Configuration();
final Path jksPath = new Path(SecurityUtils.getSecurityDir(), "test.jks");
final String ourUrl =
JavaKeyStoreProvider.SCHEME_NAME + "://file" + jksPath.toUri();
File file = new File(SecurityUtils.getSecurityDir(), "test.jks");
file.delete();
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, ourUrl);
instanceDefinition.getAppConf().credentials.put(ourUrl, new ArrayList<String>());
CredentialProvider provider =
CredentialProviderFactory.getProviders(conf).get(0);
// create new aliases
try {
if (keyAlias != null) {
char[] storepass = {'k', 'e', 'y', 'p', 'a', 's', 's'};
provider.createCredentialEntry(
keyAlias, storepass);
}
if (trustAlias != null) {
char[] trustpass = {'t', 'r', 'u', 's', 't', 'p', 'a', 's', 's'};
provider.createCredentialEntry(
trustAlias, trustpass);
}
// write out so that it can be found in checks
provider.flush();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testMultiThreadedStoreGeneration() throws Exception {
CertificateManager certMan = new CertificateManager();
MapOperations compOperations = new MapOperations();
File secDir = new File(workDir.getRoot(), SliderKeys.SECURITY_DIR);
File keystoreFile = new File(secDir, SliderKeys.KEYSTORE_FILE_NAME);
compOperations.put(SliderXmlConfKeys.KEY_KEYSTORE_LOCATION,
keystoreFile.getAbsolutePath());
certMan.initialize(compOperations, "cahost", null, null);
final CountDownLatch latch = new CountDownLatch(1);
final List<SecurityStore> stores = new ArrayList<>();
List<Thread> threads = new ArrayList<>();
final AggregateConf instanceDefinition = new AggregateConf();
setupCredentials(instanceDefinition,
SliderKeys.COMP_KEYSTORE_PASSWORD_ALIAS_DEFAULT, null);
final MapOperations compOps = new MapOperations();
compOps.put(SliderKeys.COMP_STORES_REQUIRED_KEY, "true");
for (int i=0; i<NUM_THREADS; ++i) {
final int finalI = i;
Runnable runner = new Runnable() {
public void run() {
System.out.println ("----> In run");
try {
latch.await();
SecurityStore[] stores1 = StoresGenerator.generateSecurityStores(
"testhost",
"container" + finalI,
"component" + finalI,
instanceDefinition,
compOps);
System.out.println ("----> stores1" + stores1);
List<SecurityStore>
securityStores =
Arrays.asList(stores1);
stores.addAll(securityStores);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (SliderException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runner, "TestThread" + i);
threads.add(thread);
thread.start();
}
latch.countDown();
for (Thread t : threads) {
t.join();
}
for (int i=0; i < NUM_THREADS; i++) {
assertTrue("keystore " + i + " not generated", stores.get(i).getFile().exists());
}
}
}