mirror of https://github.com/apache/jclouds.git
Issue 543, 473: new AdminAccess statement, which locks down compute nodes and creates a default admin account
This commit is contained in:
parent
3a690186f4
commit
a925d704f4
|
@ -42,6 +42,7 @@ import org.jclouds.compute.domain.Image;
|
|||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.TemplateBuilder;
|
||||
import org.jclouds.compute.internal.BaseComputeService;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
||||
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
|
||||
|
@ -60,6 +61,7 @@ import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
|
|||
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
|
||||
import org.jclouds.ec2.domain.KeyPair;
|
||||
import org.jclouds.ec2.domain.RunningInstance;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
|
@ -89,13 +91,14 @@ public class EC2ComputeService extends BaseComputeService {
|
|||
@Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning,
|
||||
@Named("NODE_TERMINATED") Predicate<NodeMetadata> nodeTerminated,
|
||||
@Named("NODE_SUSPENDED") Predicate<NodeMetadata> nodeSuspended,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client,
|
||||
Map<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Map<RegionAndName, String> securityGroupMap) {
|
||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
|
||||
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy,
|
||||
templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended,
|
||||
initScriptRunnerFactory, timeouts, executor);
|
||||
initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor);
|
||||
this.ec2Client = ec2Client;
|
||||
this.credentialsMap = credentialsMap;
|
||||
this.securityGroupMap = securityGroupMap;
|
||||
|
@ -125,7 +128,8 @@ public class EC2ComputeService extends BaseComputeService {
|
|||
// when the keypair is unique per group
|
||||
keyPair.getKeyName().equals("jclouds#" + group)
|
||||
|| keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+"))
|
||||
// old keypair pattern too verbose as it has an unnecessary region qualifier
|
||||
// old keypair pattern too verbose as it has an unnecessary
|
||||
// region qualifier
|
||||
|| keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+"))) {
|
||||
Set<String> instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.getInstanceServices()
|
||||
.describeInstancesInRegion(region)), usingKeyPairAndNotDead(keyPair)));
|
||||
|
@ -170,8 +174,8 @@ public class EC2ComputeService extends BaseComputeService {
|
|||
}
|
||||
|
||||
/**
|
||||
* like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit
|
||||
* keypairs and security groups.
|
||||
* like {@link BaseComputeService#destroyNodesMatching} except that this will
|
||||
* clean implicit keypairs and security groups.
|
||||
*/
|
||||
@Override
|
||||
public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
|
||||
|
|
|
@ -36,18 +36,20 @@ import org.jclouds.compute.domain.Image;
|
|||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.TemplateBuilder;
|
||||
import org.jclouds.compute.internal.BaseComputeService;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
||||
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
|
||||
import org.jclouds.compute.strategy.DestroyNodeStrategy;
|
||||
import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
|
||||
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
|
||||
import org.jclouds.compute.strategy.ListNodesStrategy;
|
||||
import org.jclouds.compute.strategy.RebootNodeStrategy;
|
||||
import org.jclouds.compute.strategy.ResumeNodeStrategy;
|
||||
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
|
||||
import org.jclouds.compute.strategy.SuspendNodeStrategy;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
import org.jclouds.vcloud.terremark.compute.domain.KeyPairCredentials;
|
||||
import org.jclouds.vcloud.terremark.compute.domain.OrgAndName;
|
||||
import org.jclouds.vcloud.terremark.compute.functions.NodeMetadataToOrgAndName;
|
||||
|
@ -75,19 +77,20 @@ public class TerremarkVCloudComputeService extends BaseComputeService {
|
|||
@Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning,
|
||||
@Named("NODE_TERMINATED") Predicate<NodeMetadata> nodeTerminated,
|
||||
@Named("NODE_SUSPENDED") Predicate<NodeMetadata> nodeSuspended,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, CleanupOrphanKeys cleanupOrphanKeys,
|
||||
ConcurrentMap<OrgAndName, KeyPairCredentials> credentialsMap, NodeMetadataToOrgAndName nodeToOrgAndName) {
|
||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
|
||||
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, resumeNodeStrategy,
|
||||
suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated,
|
||||
nodeSuspended, initScriptRunnerFactory, timeouts, executor);
|
||||
nodeSuspended, initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor);
|
||||
this.cleanupOrphanKeys = cleanupOrphanKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* like {@link BaseComputeService#destroyNodesMatching} except that this will clean implicit
|
||||
* keypairs.
|
||||
* like {@link BaseComputeService#destroyNodesMatching} except that this will
|
||||
* clean implicit keypairs.
|
||||
*/
|
||||
@Override
|
||||
public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
|
||||
|
@ -97,7 +100,8 @@ public class TerremarkVCloudComputeService extends BaseComputeService {
|
|||
}
|
||||
|
||||
/**
|
||||
* returns template options, except of type {@link TerremarkVCloudTemplateOptions}.
|
||||
* returns template options, except of type
|
||||
* {@link TerremarkVCloudTemplateOptions}.
|
||||
*/
|
||||
@Override
|
||||
public TerremarkVCloudTemplateOptions templateOptions() {
|
||||
|
|
|
@ -73,6 +73,7 @@ import com.google.inject.name.Names;
|
|||
* @author Adrian Cole
|
||||
*/
|
||||
public abstract class BaseComputeServiceContextModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new LocationModule(authException));
|
||||
|
@ -89,6 +90,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
|
|||
.implement(RunScriptOnNode.class, Names.named("nonblocking"), RunScriptOnNodeAsInitScriptUsingSsh.class)
|
||||
.build(RunScriptOnNodeFactoryImpl.Factory.class));
|
||||
|
||||
install(new PersistNodeCredentialsModule());
|
||||
|
||||
bind(RunScriptOnNode.Factory.class).to(RunScriptOnNodeFactoryImpl.class);
|
||||
|
||||
install(new FactoryModuleBuilder().implement(new TypeLiteral<Callable<Void>>() {
|
||||
|
@ -170,8 +173,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
|
|||
}
|
||||
|
||||
/**
|
||||
* supplies how the tag is encoded into the name. A string of hex characters is the last argument
|
||||
* and tag is the first
|
||||
* supplies how the tag is encoded into the name. A string of hex characters
|
||||
* is the last argument and tag is the first
|
||||
*/
|
||||
@Provides
|
||||
@Named("NAMING_CONVENTION")
|
||||
|
@ -207,8 +210,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
|
|||
@Memoized
|
||||
protected Supplier<Set<? extends Image>> supplyImageCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
|
||||
final Supplier<Set<? extends Image>> imageSupplier) {
|
||||
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Image>>(authException, seconds,
|
||||
new Supplier<Set<? extends Image>>() {
|
||||
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Image>>(authException,
|
||||
seconds, new Supplier<Set<? extends Image>>() {
|
||||
@Override
|
||||
public Set<? extends Image> get() {
|
||||
return imageSupplier.get();
|
||||
|
@ -241,8 +244,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
|
|||
@Memoized
|
||||
protected Supplier<Set<? extends Hardware>> supplySizeCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
|
||||
final Supplier<Set<? extends Hardware>> hardwareSupplier) {
|
||||
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Hardware>>(authException, seconds,
|
||||
new Supplier<Set<? extends Hardware>>() {
|
||||
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Hardware>>(authException,
|
||||
seconds, new Supplier<Set<? extends Hardware>>() {
|
||||
@Override
|
||||
public Set<? extends Hardware> get() {
|
||||
return hardwareSupplier.get();
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.compute.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.NodeMetadataBuilder;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.functions.CredentialsFromAdminAccess;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class PersistNodeCredentialsModule extends AbstractModule {
|
||||
|
||||
static class RefreshCredentialsForNodeIfRanAdminAccess implements Function<NodeMetadata, NodeMetadata> {
|
||||
protected final Map<String, Credentials> credentialStore;
|
||||
protected final Statement statement;
|
||||
|
||||
@Inject
|
||||
protected RefreshCredentialsForNodeIfRanAdminAccess(Map<String, Credentials> credentialStore,
|
||||
@Nullable @Assisted Statement statement) {
|
||||
this.credentialStore = credentialStore;
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeMetadata apply(NodeMetadata input) {
|
||||
if (statement == null)
|
||||
return input;
|
||||
Credentials credentials = CredentialsFromAdminAccess.INSTANCE.apply(statement);
|
||||
if (credentials != null) {
|
||||
input = NodeMetadataBuilder.fromNodeMetadata(input).credentials(credentials).build();
|
||||
credentialStore.put("node#" + input.getId(), input.getCredentials());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RefreshCredentialsForNode extends RefreshCredentialsForNodeIfRanAdminAccess {
|
||||
|
||||
@Inject
|
||||
public RefreshCredentialsForNode(Map<String, Credentials> credentialStore, @Assisted @Nullable Statement statement) {
|
||||
super(credentialStore, statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeMetadata apply(NodeMetadata input) {
|
||||
input = super.apply(input);
|
||||
if (input.getCredentials() != null)
|
||||
credentialStore.put("node#" + input.getId(), input.getCredentials());
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
install(new FactoryModuleBuilder().implement(new TypeLiteral<Function<NodeMetadata, NodeMetadata>>() {
|
||||
}, Names.named("ifAdminAccess"), RefreshCredentialsForNodeIfRanAdminAccess.class)
|
||||
.implement(new TypeLiteral<Function<NodeMetadata, NodeMetadata>>() {
|
||||
}, Names.named("always"), RefreshCredentialsForNode.class).build(PersistNodeCredentials.class));
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import static com.google.common.base.Predicates.and;
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.base.Predicates.notNull;
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Maps.newLinkedHashMap;
|
||||
import static com.google.common.collect.Sets.filter;
|
||||
|
@ -86,6 +85,8 @@ import org.jclouds.logging.Logger;
|
|||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.Statements;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
import org.jclouds.util.Maps2;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
@ -93,6 +94,7 @@ import com.google.common.base.Predicate;
|
|||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -128,22 +130,23 @@ public class BaseComputeService implements ComputeService {
|
|||
private final Predicate<NodeMetadata> nodeSuspended;
|
||||
private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory;
|
||||
private final Timeouts timeouts;
|
||||
private final InitAdminAccess initAdminAccess;
|
||||
private final PersistNodeCredentials persistNodeCredentials;
|
||||
private final ExecutorService executor;
|
||||
|
||||
@Inject
|
||||
protected BaseComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
|
||||
@Memoized Supplier<Set<? extends Image>> images,
|
||||
@Memoized Supplier<Set<? extends Hardware>> hardwareProfiles,
|
||||
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwareProfiles,
|
||||
@Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
|
||||
GetNodeMetadataStrategy getNodeMetadataStrategy,
|
||||
CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
|
||||
DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy resumeNodeStrategy,
|
||||
SuspendNodeStrategy suspendNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
|
||||
Provider<TemplateOptions> templateOptionsProvider,
|
||||
GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy,
|
||||
RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy,
|
||||
ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy,
|
||||
Provider<TemplateBuilder> templateBuilderProvider, Provider<TemplateOptions> templateOptionsProvider,
|
||||
@Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning,
|
||||
@Named("NODE_TERMINATED") Predicate<NodeMetadata> nodeTerminated,
|
||||
@Named("NODE_SUSPENDED") Predicate<NodeMetadata> nodeSuspended,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
|
||||
this.context = checkNotNull(context, "context");
|
||||
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
|
||||
|
@ -164,6 +167,8 @@ public class BaseComputeService implements ComputeService {
|
|||
this.nodeSuspended = checkNotNull(nodeSuspended, "nodeSuspended");
|
||||
this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory");
|
||||
this.timeouts = checkNotNull(timeouts, "timeouts");
|
||||
this.initAdminAccess = checkNotNull(initAdminAccess, "initAdminAccess");
|
||||
this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials");
|
||||
this.executor = checkNotNull(executor, "executor");
|
||||
}
|
||||
|
||||
|
@ -213,13 +218,16 @@ public class BaseComputeService implements ComputeService {
|
|||
Map<NodeMetadata, Exception> badNodes = newLinkedHashMap();
|
||||
Multimap<NodeMetadata, CustomizationResponse> customizationResponses = LinkedHashMultimap.create();
|
||||
|
||||
if (template.getOptions().getRunScript() != null)
|
||||
template.getOptions().runScript(initAdminAccess.apply(template.getOptions().getRunScript()));
|
||||
|
||||
Map<?, Future<Void>> responses = runNodesAndAddToSetStrategy.execute(group, count, template, goodNodes, badNodes,
|
||||
customizationResponses);
|
||||
Map<?, Exception> executionExceptions = awaitCompletion(responses, executor, null, logger, "runNodesWithTag("
|
||||
+ group + ")");
|
||||
for (NodeMetadata node : concat(goodNodes, badNodes.keySet()))
|
||||
if (node.getCredentials() != null)
|
||||
credentialStore.put("node#" + node.getId(), node.getCredentials());
|
||||
Function<NodeMetadata, NodeMetadata> fn = persistNodeCredentials.always(template.getOptions().getRunScript());
|
||||
badNodes = Maps2.transformKeys(badNodes, fn);
|
||||
goodNodes = ImmutableSet.copyOf(Iterables.transform(goodNodes, fn));
|
||||
if (executionExceptions.size() > 0 || badNodes.size() > 0) {
|
||||
throw new RunNodesException(group, count, template, goodNodes, executionExceptions, badNodes);
|
||||
}
|
||||
|
@ -230,7 +238,6 @@ public class BaseComputeService implements ComputeService {
|
|||
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions)
|
||||
throws RunNodesException {
|
||||
return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -489,8 +496,8 @@ public class BaseComputeService implements ComputeService {
|
|||
public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Payload runScript,
|
||||
RunScriptOptions options) throws RunScriptOnNodesException {
|
||||
try {
|
||||
return runScriptOnNodesMatching(filter, Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript,
|
||||
"runScript").getInput())), options);
|
||||
return runScriptOnNodesMatching(filter,
|
||||
Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript, "runScript").getInput())), options);
|
||||
} catch (IOException e) {
|
||||
Throwables.propagate(e);
|
||||
return null;
|
||||
|
@ -538,6 +545,8 @@ public class BaseComputeService implements ComputeService {
|
|||
Map<NodeMetadata, Future<ExecResponse>> responses = newLinkedHashMap();
|
||||
Map<?, Exception> exceptions = ImmutableMap.<Object, Exception> of();
|
||||
|
||||
runScript = initAdminAccess.apply(runScript);
|
||||
|
||||
Iterable<? extends RunScriptOnNode> scriptRunners = transformNodesIntoInitializedScriptRunners(
|
||||
nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes);
|
||||
if (Iterables.size(scriptRunners) > 0) {
|
||||
|
@ -548,6 +557,10 @@ public class BaseComputeService implements ComputeService {
|
|||
exceptions = awaitCompletion(responses, executor, null, logger, "runScriptOnNodesMatching(" + filter + ")");
|
||||
}
|
||||
|
||||
Function<NodeMetadata, NodeMetadata> fn = persistNodeCredentials.ifAdminAccess(runScript);
|
||||
badNodes = Maps2.transformKeys(badNodes, fn);
|
||||
goodNodes = Maps2.transformKeys(goodNodes, fn);
|
||||
|
||||
if (exceptions.size() > 0 || badNodes.size() > 0) {
|
||||
throw new RunScriptOnNodesException(runScript, options, goodNodes, exceptions, badNodes);
|
||||
}
|
||||
|
@ -557,7 +570,8 @@ public class BaseComputeService implements ComputeService {
|
|||
private Iterable<? extends RunScriptOnNode> transformNodesIntoInitializedScriptRunners(
|
||||
Iterable<? extends NodeMetadata> nodes, Statement script, RunScriptOptions options,
|
||||
Map<NodeMetadata, Exception> badNodes) {
|
||||
return filter(transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes),
|
||||
return filter(
|
||||
transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes),
|
||||
executor, null, logger, "initialize script runners"), notNull());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.compute.internal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
public interface PersistNodeCredentials {
|
||||
@Named("ifAdminAccess")
|
||||
Function<NodeMetadata, NodeMetadata> ifAdminAccess(@Nullable Statement statement);
|
||||
|
||||
@Named("always")
|
||||
Function<NodeMetadata, NodeMetadata> always(@Nullable Statement statement);
|
||||
}
|
|
@ -44,12 +44,12 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
@ -77,6 +77,7 @@ import org.jclouds.predicates.SocketOpen;
|
|||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.RestContextFactory;
|
||||
import org.jclouds.scriptbuilder.domain.Statements;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.jclouds.ssh.SshClient;
|
||||
import org.jclouds.ssh.SshException;
|
||||
import org.testng.annotations.AfterTest;
|
||||
|
@ -159,8 +160,8 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
if (context != null)
|
||||
context.close();
|
||||
Properties props = setupProperties();
|
||||
context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet.of(
|
||||
new Log4JLoggingModule(), getSshModule()), props);
|
||||
context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider,
|
||||
ImmutableSet.of(new Log4JLoggingModule(), getSshModule()), props);
|
||||
client = context.getComputeService();
|
||||
}
|
||||
|
||||
|
@ -180,8 +181,8 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
public void testCorrectAuthException() throws Exception {
|
||||
ComputeServiceContext context = null;
|
||||
try {
|
||||
context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, "MOMMA", "MIA", ImmutableSet
|
||||
.<Module> of(new Log4JLoggingModule()));
|
||||
context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, "MOMMA", "MIA",
|
||||
ImmutableSet.<Module> of(new Log4JLoggingModule()));
|
||||
context.getComputeService().listNodes();
|
||||
} catch (AuthorizationException e) {
|
||||
throw e;
|
||||
|
@ -294,8 +295,10 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
private void refreshTemplate() {
|
||||
template = buildTemplate(client.templateBuilder());
|
||||
|
||||
template.getOptions().installPrivateKey(keyPair.get("private")).authorizePublicKey(keyPair.get("public"))
|
||||
.runScript(buildScript(template.getImage().getOperatingSystem()));
|
||||
// template.getOptions().installPrivateKey(keyPair.get("private")).authorizePublicKey(keyPair.get("public"))
|
||||
// .runScript(buildScript(template.getImage().getOperatingSystem()));
|
||||
template.getOptions().runScript(
|
||||
Statements.newStatementList(AdminAccess.standard(), buildScript(template.getImage().getOperatingSystem())));
|
||||
}
|
||||
|
||||
protected void checkImageIdMatchesTemplate(NodeMetadata node) {
|
||||
|
@ -306,8 +309,8 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
protected void checkOsMatchesTemplate(NodeMetadata node) {
|
||||
if (node.getOperatingSystem() != null)
|
||||
assert node.getOperatingSystem().getFamily().equals(template.getImage().getOperatingSystem().getFamily()) : String
|
||||
.format("expecting family %s but got %s", template.getImage().getOperatingSystem().getFamily(), node
|
||||
.getOperatingSystem());
|
||||
.format("expecting family %s but got %s", template.getImage().getOperatingSystem().getFamily(),
|
||||
node.getOperatingSystem());
|
||||
}
|
||||
|
||||
void assertLocationSameOrChild(Location test, Location expected) {
|
||||
|
@ -372,8 +375,8 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
|
||||
@Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired")
|
||||
public void testGet() throws Exception {
|
||||
Map<String, ? extends NodeMetadata> metadataMap = newLinkedHashMap(uniqueIndex(filter(client
|
||||
.listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))),
|
||||
Map<String, ? extends NodeMetadata> metadataMap = newLinkedHashMap(uniqueIndex(
|
||||
filter(client.listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))),
|
||||
new Function<NodeMetadata, String>() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,6 +28,7 @@ import static org.testng.Assert.assertEquals;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -43,11 +44,16 @@ import org.jclouds.net.IPSocket;
|
|||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.jclouds.predicates.SocketOpen;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
|
||||
import org.jclouds.ssh.SshClient;
|
||||
import org.jclouds.ssh.SshException;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
@ -97,6 +103,35 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AdminAccess.Configuration.class).toInstance(new Configuration() {
|
||||
|
||||
@Override
|
||||
public Supplier<String> defaultAdminUsername() {
|
||||
return Suppliers.ofInstance("defaultAdminUsername");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Map<String, String>> defaultAdminSshKeys() {
|
||||
return Suppliers.<Map<String, String>> ofInstance(ImmutableMap.of("public", "publicKey", "private",
|
||||
"privateKey"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> cryptFunction() {
|
||||
return new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return String.format("crypt(%s)", input);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public Supplier<String> passwordGenerator() {
|
||||
return Suppliers.ofInstance("randompassword");
|
||||
}
|
||||
});
|
||||
SshClient.Factory factory = createMock(SshClient.Factory.class);
|
||||
SshClient client1 = createMock(SshClient.class);
|
||||
SshClient client2 = createMock(SshClient.class);
|
||||
|
@ -140,20 +175,20 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes
|
|||
runScriptAndInstallSsh(client5, "bootstrap", 5);
|
||||
|
||||
expect(
|
||||
factory.create(eq(new IPSocket("144.175.1.1", 22)), eq(new Credentials("root", keyPair
|
||||
.get("private"))))).andReturn(client1);
|
||||
factory.create(eq(new IPSocket("144.175.1.1", 22)),
|
||||
eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client1);
|
||||
expect(
|
||||
factory.create(eq(new IPSocket("144.175.1.2", 22)), eq(new Credentials("root", keyPair
|
||||
.get("private"))))).andReturn(client2);
|
||||
factory.create(eq(new IPSocket("144.175.1.2", 22)),
|
||||
eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client2);
|
||||
expect(
|
||||
factory.create(eq(new IPSocket("144.175.1.3", 22)), eq(new Credentials("root", keyPair
|
||||
.get("private"))))).andReturn(client3);
|
||||
factory.create(eq(new IPSocket("144.175.1.3", 22)),
|
||||
eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client3);
|
||||
expect(
|
||||
factory.create(eq(new IPSocket("144.175.1.4", 22)), eq(new Credentials("root", keyPair
|
||||
.get("private"))))).andReturn(client4);
|
||||
factory.create(eq(new IPSocket("144.175.1.4", 22)),
|
||||
eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client4);
|
||||
expect(
|
||||
factory.create(eq(new IPSocket("144.175.1.5", 22)), eq(new Credentials("root", keyPair
|
||||
.get("private"))))).andReturn(client5);
|
||||
factory.create(eq(new IPSocket("144.175.1.5", 22)),
|
||||
eq(new Credentials("defaultAdminUsername", "privateKey")))).andReturn(client5);
|
||||
|
||||
helloAndJava(client2);
|
||||
helloAndJava(client3);
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.compute.config;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.compute.config.PersistNodeCredentialsModule.RefreshCredentialsForNode;
|
||||
import org.jclouds.compute.config.PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.NodeMetadataBuilder;
|
||||
import org.jclouds.compute.domain.NodeState;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class PersistNodeCredentialsTest {
|
||||
|
||||
public void testReturnsCorrectFunction() {
|
||||
PersistNodeCredentials persistNodeCredentials = Guice.createInjector(new PersistNodeCredentialsModule(),
|
||||
new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Map<String, Credentials>>() {
|
||||
}).toInstance(ImmutableMap.<String, Credentials> of());
|
||||
}
|
||||
|
||||
}).getInstance(PersistNodeCredentials.class);
|
||||
assertEquals(persistNodeCredentials.always(null).getClass(),
|
||||
PersistNodeCredentialsModule.RefreshCredentialsForNode.class);
|
||||
assertEquals(persistNodeCredentials.ifAdminAccess(null).getClass(),
|
||||
PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess.class);
|
||||
}
|
||||
|
||||
public void testRefreshCredentialsForNodeIfRanAdminAccessWhenStatementIsNullSameCredentialsAndNoCaching() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Credentials> credstore = createMock(Map.class);
|
||||
|
||||
replay(credstore);
|
||||
|
||||
NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build();
|
||||
RefreshCredentialsForNodeIfRanAdminAccess fn = new PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess(
|
||||
credstore, null);
|
||||
assertEquals(node, fn.apply(node));
|
||||
|
||||
verify(credstore);
|
||||
|
||||
}
|
||||
|
||||
public void testRefreshCredentialsForNodeWhenStatementIsNullSameCredentialsAndDoesCache() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Credentials> credstore = createMock(Map.class);
|
||||
Credentials credentials = createMock(Credentials.class);
|
||||
|
||||
expect(credstore.put("node#id", credentials)).andReturn(null);
|
||||
|
||||
replay(credstore);
|
||||
|
||||
NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).credentials(credentials).build();
|
||||
RefreshCredentialsForNode fn = new PersistNodeCredentialsModule.RefreshCredentialsForNode(credstore, null);
|
||||
assertEquals(node, fn.apply(node));
|
||||
|
||||
verify(credstore);
|
||||
|
||||
}
|
||||
|
||||
public void testRefreshCredentialsForNodeIfRanAdminAccessWhenStatementIsAdminAccessNewCredentialsAndDoesCache() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Credentials> credstore = createMock(Map.class);
|
||||
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
Credentials credentials = createMock(Credentials.class);
|
||||
|
||||
expect(statement.getAdminCredentials()).andReturn(credentials).atLeastOnce();
|
||||
expect(credstore.put("node#id", credentials)).andReturn(null);
|
||||
|
||||
replay(statement);
|
||||
replay(credstore);
|
||||
|
||||
NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build();
|
||||
RefreshCredentialsForNodeIfRanAdminAccess fn = new PersistNodeCredentialsModule.RefreshCredentialsForNodeIfRanAdminAccess(
|
||||
credstore, statement);
|
||||
assertEquals(fn.apply(node).getCredentials(), credentials);
|
||||
|
||||
verify(statement);
|
||||
verify(credstore);
|
||||
|
||||
}
|
||||
|
||||
public void testRefreshCredentialsForNodeWhenStatementIsAdminAccessNewCredentialsAndDoesCache() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Credentials> credstore = createMock(Map.class);
|
||||
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
Credentials credentials = createMock(Credentials.class);
|
||||
expect(statement.getAdminCredentials()).andReturn(credentials).atLeastOnce();
|
||||
expect(credstore.put("node#id", credentials)).andReturn(null);
|
||||
expect(credstore.put("node#id", credentials)).andReturn(null); // TODO
|
||||
// optimize
|
||||
// this
|
||||
|
||||
replay(statement);
|
||||
replay(credstore);
|
||||
|
||||
NodeMetadata node = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build();
|
||||
RefreshCredentialsForNode fn = new PersistNodeCredentialsModule.RefreshCredentialsForNode(credstore, statement);
|
||||
assertEquals(fn.apply(node).getCredentials(), credentials);
|
||||
|
||||
verify(statement);
|
||||
verify(credstore);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -76,23 +76,33 @@ END_OF_SCRIPT
|
|||
# add desired commands from the user
|
||||
cat >> $INSTANCE_HOME/bootstrap.sh <<'END_OF_SCRIPT'
|
||||
cd $INSTANCE_HOME
|
||||
mkdir -p ~/.ssh
|
||||
cat >> ~/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
ssh-rsa
|
||||
rm /etc/sudoers
|
||||
cat >> /etc/sudoers <<'END_OF_FILE'
|
||||
root ALL = (ALL) ALL
|
||||
%wheel ALL = (ALL) NOPASSWD:ALL
|
||||
END_OF_FILE
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
chmod 0440 /etc/sudoers
|
||||
mkdir -p /home/users/defaultAdminUsername
|
||||
groupadd -f wheel
|
||||
useradd -s /bin/bash -g wheel -d /home/users/defaultAdminUsername -p 'crypt(randompassword)' defaultAdminUsername
|
||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
publicKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
|
||||
chown -R defaultAdminUsername /home/users/defaultAdminUsername
|
||||
exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
|
||||
/etc/init.d/sshd reload||/etc/init.d/ssh reload
|
||||
awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
|
||||
test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
|
||||
which curl || (apt-get install -f -y -qq --force-yes curl || (apt-get update && apt-get install -f -y -qq --force-yes curl))
|
||||
(which java && java -fullversion 2>&1|egrep -q 1.6 ) ||
|
||||
curl -X GET -s --retry 20 http://whirr.s3.amazonaws.com/0.2.0-incubating-SNAPSHOT/sun/java/install |(bash)
|
||||
echo nameserver 208.67.222.222 >> /etc/resolv.conf
|
||||
rm -rf /var/cache/apt /usr/lib/vmware-tools
|
||||
echo "export PATH=\"\$JAVA_HOME/bin/:\$PATH\"" >> /root/.bashrc
|
||||
mkdir -p ~/.ssh
|
||||
rm ~/.ssh/id_rsa
|
||||
cat >> ~/.ssh/id_rsa <<'END_OF_FILE'
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
END_OF_FILE
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
|
||||
END_OF_SCRIPT
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.jclouds.crypto;
|
|||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
|
@ -31,13 +32,14 @@ import org.jclouds.encryption.internal.JCECrypto;
|
|||
import com.google.inject.ImplementedBy;
|
||||
|
||||
/**
|
||||
* Allows you to access cryptographic objects and factories without adding a provider to the JCE
|
||||
* runtime.
|
||||
* Allows you to access cryptographic objects and factories without adding a
|
||||
* provider to the JCE runtime.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@ImplementedBy(JCECrypto.class)
|
||||
public interface Crypto {
|
||||
KeyPairGenerator rsaKeyPairGenerator();
|
||||
|
||||
KeyFactory rsaKeyFactory();
|
||||
|
||||
|
|
|
@ -93,8 +93,8 @@ public class Pems {
|
|||
if (parsers.containsKey(reader.getBeginMarker())) {
|
||||
return parsers.get(reader.getBeginMarker()).parseResult(bytes);
|
||||
} else {
|
||||
throw new IOException(String.format("Invalid PEM file: no parsers for marker %s in %s", reader
|
||||
.getBeginMarker(), parsers.keySet()));
|
||||
throw new IOException(String.format("Invalid PEM file: no parsers for marker %s in %s",
|
||||
reader.getBeginMarker(), parsers.keySet()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -103,7 +103,8 @@ public class Pems {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the object of generic type {@code T} that is pem encoded in the supplier.
|
||||
* Returns the object of generic type {@code T} that is pem encoded in the
|
||||
* supplier.
|
||||
*
|
||||
* @param supplier
|
||||
* the input stream factory
|
||||
|
@ -111,7 +112,8 @@ public class Pems {
|
|||
* header that begins the PEM block
|
||||
* @param processor
|
||||
* how to parser the object from a byte array
|
||||
* @return the object of generic type {@code T} which was PEM encoded in the stream
|
||||
* @return the object of generic type {@code T} which was PEM encoded in the
|
||||
* stream
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
|
@ -138,8 +140,10 @@ public class Pems {
|
|||
* if an I/O error occurs
|
||||
*/
|
||||
public static KeySpec privateKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||
return fromPem(supplier, new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(
|
||||
PRIVATE_PKCS1_MARKER, new ResultParser<KeySpec>() {
|
||||
return fromPem(
|
||||
supplier,
|
||||
new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(PRIVATE_PKCS1_MARKER,
|
||||
new ResultParser<KeySpec>() {
|
||||
|
||||
public KeySpec parseResult(byte[] bytes) throws IOException {
|
||||
return (new PKCS1EncodedKeySpec(bytes)).getKeySpec();
|
||||
|
@ -155,8 +159,8 @@ public class Pems {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which contains an encoded
|
||||
* private key in PEM format.
|
||||
* Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which
|
||||
* contains an encoded private key in PEM format.
|
||||
*
|
||||
* @param pem
|
||||
* private key in pem encoded format.
|
||||
|
@ -177,8 +181,10 @@ public class Pems {
|
|||
* if an I/O error occurs
|
||||
*/
|
||||
public static KeySpec publicKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||
return fromPem(supplier, new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(
|
||||
PUBLIC_PKCS1_MARKER, new ResultParser<KeySpec>() {
|
||||
return fromPem(
|
||||
supplier,
|
||||
new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(PUBLIC_PKCS1_MARKER,
|
||||
new ResultParser<KeySpec>() {
|
||||
|
||||
public KeySpec parseResult(byte[] bytes) throws IOException {
|
||||
return (new PKCS1EncodedPublicKeySpec(bytes)).getKeySpec();
|
||||
|
@ -194,8 +200,8 @@ public class Pems {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which contains an encoded
|
||||
* public key in PEM format.
|
||||
* Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which
|
||||
* contains an encoded public key in PEM format.
|
||||
*
|
||||
* @param pem
|
||||
* public key in pem encoded format.
|
||||
|
@ -206,7 +212,8 @@ public class Pems {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link X509EncodedKeySpec} that is pem encoded in the supplier.
|
||||
* Returns the {@link X509EncodedKeySpec} that is pem encoded in the
|
||||
* supplier.
|
||||
*
|
||||
* @param supplier
|
||||
* the input stream factory
|
||||
|
@ -223,14 +230,15 @@ public class Pems {
|
|||
final CertificateFactory finalCertFactory = certFactory != null ? certFactory : CertificateFactory
|
||||
.getInstance("X.509");
|
||||
try {
|
||||
return fromPem(supplier, new PemProcessor<X509Certificate>(ImmutableMap
|
||||
.<String, ResultParser<X509Certificate>> of(CERTIFICATE_X509_MARKER,
|
||||
new ResultParser<X509Certificate>() {
|
||||
return fromPem(
|
||||
supplier,
|
||||
new PemProcessor<X509Certificate>(ImmutableMap.<String, ResultParser<X509Certificate>> of(
|
||||
CERTIFICATE_X509_MARKER, new ResultParser<X509Certificate>() {
|
||||
|
||||
public X509Certificate parseResult(byte[] bytes) throws IOException {
|
||||
try {
|
||||
return (X509Certificate) finalCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(bytes));
|
||||
return (X509Certificate) finalCertFactory.generateCertificate(new ByteArrayInputStream(
|
||||
bytes));
|
||||
} catch (CertificateException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -246,8 +254,8 @@ public class Pems {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)} on the string which
|
||||
* contains an X.509 certificate in PEM format.
|
||||
* Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)}
|
||||
* on the string which contains an X.509 certificate in PEM format.
|
||||
*
|
||||
* @param pem
|
||||
* certificate in pem encoded format.
|
||||
|
@ -302,9 +310,9 @@ public class Pems {
|
|||
|
||||
// TODO find a way to do this without using bouncycastle
|
||||
static byte[] getEncoded(RSAPrivateCrtKey key) {
|
||||
RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(key.getModulus(), key.getPublicExponent(), key
|
||||
.getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(), key
|
||||
.getPrimeExponentQ(), key.getCrtCoefficient());
|
||||
RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(key.getModulus(), key.getPublicExponent(),
|
||||
key.getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(),
|
||||
key.getPrimeExponentQ(), key.getCrtCoefficient());
|
||||
|
||||
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
|
||||
|
@ -320,9 +328,13 @@ public class Pems {
|
|||
}
|
||||
|
||||
private static String pem(byte[] key, String marker) {
|
||||
return new StringBuilder(marker + "\n").append(
|
||||
Joiner.on('\n').join(Splitter.fixedLength(64).split(CryptoStreams.base64(key)))).append(
|
||||
"\n" + marker.replace("BEGIN", "END") + "\n").toString().trim();
|
||||
return pem(key, marker, 64);
|
||||
}
|
||||
|
||||
static String pem(byte[] key, String marker, int length) {
|
||||
return new StringBuilder(marker + "\n")
|
||||
.append(Joiner.on('\n').join(Splitter.fixedLength(length).split(CryptoStreams.base64(key))))
|
||||
.append("\n" + marker.replace("BEGIN", "END") + "\n").toString().trim();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,28 +44,32 @@
|
|||
|
||||
*/
|
||||
|
||||
package org.jclouds.scriptbuilder.util;
|
||||
package org.jclouds.crypto;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
/**
|
||||
* This class defines a method,
|
||||
* {@link Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int) Sha512_crypt()}, which
|
||||
* takes a password and a salt string and generates a Sha512 encrypted password entry.
|
||||
* {@link Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int)
|
||||
* Sha512_crypt()}, which takes a password and a salt string and generates a
|
||||
* Sha512 encrypted password entry.
|
||||
*
|
||||
* This class implements the new generation, scalable, SHA512-based Unix 'crypt' algorithm developed
|
||||
* by a group of engineers from Red Hat, Sun, IBM, and HP for common use in the Unix and Linux
|
||||
* /etc/shadow files.
|
||||
* This class implements the new generation, scalable, SHA512-based Unix 'crypt'
|
||||
* algorithm developed by a group of engineers from Red Hat, Sun, IBM, and HP
|
||||
* for common use in the Unix and Linux /etc/shadow files.
|
||||
*
|
||||
* The Linux glibc library (starting at version 2.7) includes support for validating passwords
|
||||
* hashed using this algorithm.
|
||||
* The Linux glibc library (starting at version 2.7) includes support for
|
||||
* validating passwords hashed using this algorithm.
|
||||
*
|
||||
* The algorithm itself was released into the Public Domain by Ulrich Drepper
|
||||
* <drepper@redhat.com>. A discussion of the rationale and development of this algorithm is at
|
||||
* <drepper@redhat.com>. A discussion of the rationale and development of
|
||||
* this algorithm is at
|
||||
*
|
||||
* http://people.redhat.com/drepper/sha-crypt.html
|
||||
*
|
||||
|
@ -74,6 +78,34 @@ import org.jclouds.crypto.Crypto;
|
|||
* http://people.redhat.com/drepper/SHA-crypt.txt
|
||||
*/
|
||||
public class Sha512Crypt {
|
||||
public static com.google.common.base.Function<String, String> function() {
|
||||
return Function.INSTANCE;
|
||||
}
|
||||
|
||||
public static enum Function implements com.google.common.base.Function<String, String> {
|
||||
INSTANCE;
|
||||
private Crypto crypto;
|
||||
|
||||
Function() {
|
||||
try {
|
||||
this.crypto = new JCECrypto();
|
||||
} catch (Exception e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return Sha512Crypt.makeShadowLine(input, null, crypto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "sha512Crypt()";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static private final String sha512_salt_prefix = "$6$";
|
||||
static private final String sha512_rounds_prefix = "rounds=";
|
||||
static private final int SALT_LEN_MAX = 16;
|
||||
|
@ -84,19 +116,20 @@ public class Sha512Crypt {
|
|||
static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/**
|
||||
* This method actually generates an Sha512 crypted password hash from a plaintext password and a
|
||||
* salt.
|
||||
* This method actually generates an Sha512 crypted password hash from a
|
||||
* plaintext password and a salt.
|
||||
*
|
||||
* <p>
|
||||
* The resulting string will be in the form '$6$<rounds=n>$<salt>$<hashed mess>
|
||||
* The resulting string will be in the form
|
||||
* '$6$<rounds=n>$<salt>$<hashed mess>
|
||||
* </p>
|
||||
*
|
||||
* @param password
|
||||
* Plaintext password
|
||||
*
|
||||
* @param shadowPrefix
|
||||
* An encoded salt/rounds which will be consulted to determine the salt and round
|
||||
* count, if not null
|
||||
* An encoded salt/rounds which will be consulted to determine the
|
||||
* salt and round count, if not null
|
||||
*
|
||||
* @return The Sha512 Unix Crypt hash text for the password
|
||||
*/
|
||||
|
@ -206,7 +239,8 @@ public class Sha512Crypt {
|
|||
System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt);
|
||||
|
||||
/*
|
||||
* Repeatedly run the collected hash value through SHA512 to burn CPU cycles.
|
||||
* Repeatedly run the collected hash value through SHA512 to burn CPU
|
||||
* cycles.
|
||||
*/
|
||||
|
||||
for (cnt = 0; cnt < rounds; ++cnt) {
|
||||
|
@ -270,8 +304,8 @@ public class Sha512Crypt {
|
|||
buffer.append(b64_from_24bit((byte) 0x00, (byte) 0x00, alt_result[63], 2));
|
||||
|
||||
/*
|
||||
* Clear the buffer for the intermediate result so that people attaching to processes or
|
||||
* reading core dumps cannot get any information.
|
||||
* Clear the buffer for the intermediate result so that people attaching
|
||||
* to processes or reading core dumps cannot get any information.
|
||||
*/
|
||||
|
||||
ctx.reset();
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.crypto;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
/**
|
||||
* Creates OpenSSH RSA keypairs
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @see <a href=
|
||||
* "http://stackoverflow.com/questions/3706177/how-to-generate-ssh-compatible-id-rsa-pub-from-java"
|
||||
* />
|
||||
*/
|
||||
@Beta
|
||||
public class SshKeys {
|
||||
// All data type encoding is defined in the section #5 of RFC #4251. string
|
||||
// and mpint (multiple precision integer) types are encoded this way :
|
||||
private static final byte[] sshrsa = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' };
|
||||
|
||||
/**
|
||||
*
|
||||
* @param used
|
||||
* to generate RSA key pairs
|
||||
* @return new 2048 bit keyPair
|
||||
* @see Crypto#rsaKeyPairGenerator()
|
||||
*/
|
||||
public static KeyPair generateRsaKeyPair(KeyPairGenerator generator) {
|
||||
generator.initialize(2048);
|
||||
return generator.genKeyPair();
|
||||
}
|
||||
|
||||
/**
|
||||
* return a "public" -> rsa public key, "private" -> its corresponding
|
||||
* private key
|
||||
*/
|
||||
public static Map<String, String> generate() {
|
||||
try {
|
||||
return generate(KeyPairGenerator.getInstance("RSA"));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Throwables.propagate(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> generate(KeyPairGenerator generator) {
|
||||
KeyPair pair = generateRsaKeyPair(generator);
|
||||
Builder<String, String> builder = ImmutableMap.<String, String> builder();
|
||||
builder.put("public", encodeAsOpenSSH(RSAPublicKey.class.cast(pair.getPublic())));
|
||||
builder.put("private", encodeAsPem(RSAPrivateKey.class.cast(pair.getPrivate())));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static String encodeAsOpenSSH(RSAPublicKey key) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
/* encode the "ssh-rsa" string */
|
||||
out.write(sshrsa);
|
||||
/* Encode the public exponent */
|
||||
BigInteger e = key.getPublicExponent();
|
||||
byte[] data = e.toByteArray();
|
||||
encodeUint32(data.length, out);
|
||||
out.write(data);
|
||||
/* Encode the modulus */
|
||||
BigInteger m = key.getModulus();
|
||||
data = m.toByteArray();
|
||||
encodeUint32(data.length, out);
|
||||
out.write(data);
|
||||
return "ssh-rsa " + CryptoStreams.base64(out.toByteArray());
|
||||
}
|
||||
|
||||
public static String encodeAsPem(RSAPrivateKey key) {
|
||||
return Pems.pem(key.getEncoded(), Pems.PRIVATE_PKCS1_MARKER, 64);
|
||||
}
|
||||
|
||||
public static void encodeUint32(int value, ByteArrayDataOutput out) {
|
||||
out.write((byte) ((value >>> 24) & 0xff));
|
||||
out.write((byte) ((value >>> 16) & 0xff));
|
||||
out.write((byte) ((value >>> 8) & 0xff));
|
||||
out.write((byte) (value & 0xff));
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ package org.jclouds.encryption.internal;
|
|||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
|
@ -41,6 +42,7 @@ import org.jclouds.crypto.Crypto;
|
|||
@Singleton
|
||||
public class JCECrypto implements Crypto {
|
||||
|
||||
private final KeyPairGenerator rsaKeyPairGenerator;
|
||||
private final KeyFactory rsaKeyFactory;
|
||||
private final CertificateFactory certFactory;
|
||||
private final Provider provider;
|
||||
|
@ -51,6 +53,8 @@ public class JCECrypto implements Crypto {
|
|||
}
|
||||
|
||||
public JCECrypto(@Nullable Provider provider) throws NoSuchAlgorithmException, CertificateException {
|
||||
this.rsaKeyPairGenerator = provider == null ? KeyPairGenerator.getInstance("RSA") : KeyPairGenerator.getInstance(
|
||||
"RSA", provider);
|
||||
this.rsaKeyFactory = provider == null ? KeyFactory.getInstance("RSA") : KeyFactory.getInstance("RSA", provider);
|
||||
this.certFactory = provider == null ? CertificateFactory.getInstance("X.509") : CertificateFactory.getInstance(
|
||||
"X.509", provider);
|
||||
|
@ -142,4 +146,9 @@ public class JCECrypto implements Crypto {
|
|||
public KeyFactory rsaKeyFactory() {
|
||||
return rsaKeyFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairGenerator rsaKeyPairGenerator() {
|
||||
return rsaKeyPairGenerator;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.util;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
/**
|
||||
* Cheap, lightweight, low-security password generator.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://www.java-forums.org/java-lang/7355-how-create-lightweight-low-security-password-generator.html"
|
||||
* />
|
||||
*/
|
||||
public enum PasswordGenerator implements Supplier<String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/** Minimum length for a decent password */
|
||||
public static final int MIN_LENGTH = 10;
|
||||
|
||||
/** The random number generator. */
|
||||
protected static final SecureRandom r = new SecureRandom();
|
||||
|
||||
/*
|
||||
* Set of characters that is valid. Must be printable, memorable, and
|
||||
* "won't break HTML" (i.e., not ' <', '>', '&', '=', ...). or break shell
|
||||
* commands (i.e., not ' <', '>', '$', '!', ...). I, L and O are good to
|
||||
* leave out, as are numeric zero and one.
|
||||
*/
|
||||
public final static char[] goodChar = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
|
||||
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '@', };
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < MIN_LENGTH; i++) {
|
||||
sb.append(goodChar[r.nextInt(goodChar.length)]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -16,11 +16,10 @@
|
|||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
package org.jclouds.scriptbuilder.util;
|
||||
package org.jclouds.crypto;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.crypto;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", singleThreaded = true)
|
||||
public class SshKeysTest {
|
||||
|
||||
public static final String SSH_PUBLIC_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJvZkkmoabQopH7yd9Ak2xJ34X21c0xXsJ85xbqOyqzwRmCJVHT2EPUhg6PhiozSok42WDKDjFFZ7B1IbtBM+PWUmlUCJ1r2xfLb6TPJqBkDUCbQ5lxupvmGh4gOBxf54Nrv2zS7QOiaNx870QqG8csHP7Mz7dCo9GQ9XydhNt+z4eNXPM5ToPUHRdHf4g9lmXYCdazZ3SqGdK0dwNS+dFVDQ/jzZjA31WBx45m2k/PQMIoQnlPHlJO5vyTT/O39UAwdBp8tK5Awt3azhku45mm03/+4CxObGKt6p3fvP7xlN0FsnwsSkn6IJe5J+blpSHuLDqjG9OhjYAvnf6MrZR";
|
||||
public static final String SSH_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDJvZkkmoabQopH\\n7yd9Ak2xJ34X21c0xXsJ85xbqOyqzwRmCJVHT2EPUhg6PhiozSok42WDKDjFFZ7B\\n1IbtBM+PWUmlUCJ1r2xfLb6TPJqBkDUCbQ5lxupvmGh4gOBxf54Nrv2zS7QOiaNx\\n870QqG8csHP7Mz7dCo9GQ9XydhNt+z4eNXPM5ToPUHRdHf4g9lmXYCdazZ3SqGdK\\n0dwNS+dFVDQ/jzZjA31WBx45m2k/PQMIoQnlPHlJO5vyTT/O39UAwdBp8tK5Awt3\\nazhku45mm03/+4CxObGKt6p3fvP7xlN0FsnwsSkn6IJe5J+blpSHuLDqjG9OhjYA\\nvnf6MrZRAgMBAAECggEBAMDzwHeL/FafS9cFXEVqYJih5y42MbBdeRLJl7DrXoD4\\nQ4K7jtuHhpO6t0VtgvRgVoC1pa/OVo3Z4eANv4cO5N58Tb35aRwaTpKyE+aLPlPR\\nc4IAgJbDrBJUOQeYbBLiNm9sAWbtbyfAaT1iHGDEWJGeCzAlkWik4ugXlZeza13y\\nC4hg2yUymju27nSIWcEfAo7Zmw8CTy32edLGUNq8qu7KYSaocKvf522Kjl5p6cly\\nnd+OhcFA4pSTeKkgjb1dEHw2p4JVX/srHCpySo4ERtHAFyVyTIhsP5GICAq856U5\\nGMSIT1Hja+6q/FEDqT2R32ju62VfQBS2kSfeBlzot6kCgYEA/1p6Xljvn6wrYklQ\\n046E7xp7jlx2yKWnbIjRf5QFpK5Czb53I+mSkQcDF3imaZK+f/xz1OURmGv6phsA\\nFdU+UOdzGki7DYCNuTuz9LhdpIqxGqlcvnvOP9A6ouRGTz45VX2rqUx5A2ou0SPW\\n7ok++JfvRGj4VC1G2005ht90/98CgYEAykBeNHnChXmxRv5ESD28S5ijxFLI5qvG\\nooUIhdVQ2ws7H7bb4Arj4JClu55Y3uOi2uVgPq5Pqy9aLcAALuSJjRQj9+NU9EJS\\nLy1WhBQTRNpTlUshNpg4H5b9sFoMTgz3nrTU24NY73RtoDl19TjiK7O9rTasYlcr\\n36dLejyeT88CgYEAs4vp2OcN7ibABobolyhx3jGvyOTI/MJFm7IEJIFvCmEhRctz\\nuEOms+TLTridwkPVQObAh2Rd39+kySDZCYD8JSTosQWMyKyoeiM5oIv2BBkk+Es3\\nlBQ3bHU8lYaOzW9CHxOTHSJRQI5rxtA9c1H7fg5OxbpNSdrgJJkDJwt+F98CgYEA\\niCBWx58EK+5CQXQ15SGYMJFl+Gd3zLnlEdHUcK+ooiWm/6uFxf/ObIEu616iljJE\\nlGw6ITYVbTSLz6sg9G7hndDmfJvHvDc/NX2gc3lHltoT07IjgqllbO2lhiK1kXrs\\n1ycC9VQscc69UlAacph8scliaskXsYDWiMwC4x0VuMUCgYA/FSHHAXLyBqoFqEpQ\\nRmfgXzUcU/mKEaGRPhAvoAU87Z74mEgaO5Hbv7zmFwWpD4cdW0WybzVv9r5Hs/hS\\n4JGBzx4KPFAygnVeXiftVuM9scycg3RHK907hmlYNZicI7TbUlM5RQ4ngn/LTjXo\\nG+TDTPn3Luw1fvAMEEcdsr9cJw==\\n-----END RSA PRIVATE KEY-----";
|
||||
|
||||
@Test
|
||||
public void testCanGenerate() {
|
||||
Map<String, String> map = SshKeys.generate();
|
||||
assert map.get("public").startsWith("ssh-rsa ") : map;
|
||||
assert map.get("private").startsWith("-----BEGIN RSA PRIVATE KEY-----") : map;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAsPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||
String encoded = SshKeys.encodeAsPem((RSAPrivateCrtKey) KeyFactory.getInstance("RSA").generatePrivate(
|
||||
Pems.privateKeySpec(Payloads.newStringPayload(PemsTest.PRIVATE_KEY))));
|
||||
assertEquals(encoded.replace("\n", "\\n").trim(), SSH_PRIVATE_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAsOpenSSH() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||
String encoded = SshKeys.encodeAsOpenSSH((RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
|
||||
Pems.publicKeySpec(Payloads.newStringPayload(PemsTest.PUBLIC_KEY))));
|
||||
assertEquals(encoded, SSH_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
}
|
|
@ -40,6 +40,7 @@ import org.jclouds.compute.domain.Hardware;
|
|||
import org.jclouds.compute.domain.Image;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.TemplateBuilder;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
||||
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
|
||||
|
@ -56,6 +57,7 @@ import org.jclouds.ec2.compute.EC2ComputeService;
|
|||
import org.jclouds.ec2.compute.domain.RegionAndName;
|
||||
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
|
||||
import org.jclouds.ec2.domain.KeyPair;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
import org.jclouds.util.Preconditions2;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -83,7 +85,8 @@ public class AWSEC2ComputeService extends EC2ComputeService {
|
|||
@Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning,
|
||||
@Named("NODE_TERMINATED") Predicate<NodeMetadata> nodeTerminated,
|
||||
@Named("NODE_SUSPENDED") Predicate<NodeMetadata> nodeSuspended,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client,
|
||||
Map<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Map<RegionAndName, String> securityGroupMap,
|
||||
@Named("PLACEMENT") Map<RegionAndName, String> placementGroupMap,
|
||||
|
@ -91,7 +94,8 @@ public class AWSEC2ComputeService extends EC2ComputeService {
|
|||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
|
||||
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy,
|
||||
templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended,
|
||||
initScriptRunnerFactory, timeouts, executor, ec2Client, credentialsMap, securityGroupMap);
|
||||
initScriptRunnerFactory, initAdminAccess, persistNodeCredentials, timeouts, executor, ec2Client,
|
||||
credentialsMap, securityGroupMap);
|
||||
this.ec2Client = ec2Client;
|
||||
this.placementGroupMap = placementGroupMap;
|
||||
this.placementGroupDeleted = placementGroupDeleted;
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.testng.annotations.Test;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "live", enabled = true, sequential = true)
|
||||
@Test(groups = "live", enabled = true, singleThreaded = true)
|
||||
public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest {
|
||||
public SlicehostComputeServiceLiveTest() {
|
||||
provider = "slicehost";
|
||||
|
@ -49,7 +49,7 @@ public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest
|
|||
assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
|
||||
assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04");
|
||||
assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
|
||||
assertEquals(defaultTemplate.getLocation().getId(), "DFW1");
|
||||
assertEquals(defaultTemplate.getLocation().getId(), "slicehost");
|
||||
assertEquals(getCores(defaultTemplate.getHardware()), 0.25d);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* Statements used in a shell script
|
||||
|
@ -35,11 +35,11 @@ public class StatementList implements Statement {
|
|||
public final List<Statement> statements;
|
||||
|
||||
public StatementList(Statement... statements) {
|
||||
this.statements = Lists.newArrayList(checkNotNull(statements, "statements"));
|
||||
this.statements = ImmutableList.copyOf(checkNotNull(statements, "statements"));
|
||||
}
|
||||
|
||||
public StatementList(Iterable<Statement> statements) {
|
||||
this.statements = Lists.newArrayList(checkNotNull(statements, "statements"));
|
||||
this.statements = ImmutableList.copyOf(checkNotNull(statements, "statements"));
|
||||
}
|
||||
|
||||
public String render(OsFamily family) {
|
||||
|
@ -52,10 +52,39 @@ public class StatementList implements Statement {
|
|||
|
||||
@Override
|
||||
public Iterable<String> functionDependencies(OsFamily family) {
|
||||
List<String> functions = Lists.newArrayList();
|
||||
Builder<String> functions = ImmutableList.<String> builder();
|
||||
for (Statement statement : statements) {
|
||||
Iterables.addAll(functions, statement.functionDependencies(family));
|
||||
functions.addAll(statement.functionDependencies(family));
|
||||
}
|
||||
return functions;
|
||||
return functions.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((statements == null) ? 0 : statements.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
StatementList other = (StatementList) obj;
|
||||
if (statements == null) {
|
||||
if (other.statements != null)
|
||||
return false;
|
||||
} else if (!statements.equals(other.statements))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Statement> getStatements() {
|
||||
return statements;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.functions;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.StatementList;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public enum CredentialsFromAdminAccess implements Function<Statement, Credentials> {
|
||||
INSTANCE;
|
||||
@Override
|
||||
public Credentials apply(@Nullable Statement input) {
|
||||
if (input == null)
|
||||
return null;
|
||||
if (input instanceof StatementList) {
|
||||
try {
|
||||
return apply(Iterables.find(StatementList.class.cast(input).getStatements(),
|
||||
Predicates.instanceOf(AdminAccess.class)));
|
||||
} catch (NoSuchElementException e) {
|
||||
return null;
|
||||
}
|
||||
} else if (input instanceof AdminAccess) {
|
||||
return AdminAccess.class.cast(input).getAdminCredentials();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.functions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.StatementList;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* Statement used in a shell script
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class InitAdminAccess implements Function<Statement, Statement> {
|
||||
private final AdminAccess.Configuration adminAccessConfiguration;
|
||||
|
||||
@Inject
|
||||
public InitAdminAccess(Configuration adminAccessConfiguration) {
|
||||
this.adminAccessConfiguration = adminAccessConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement input) {
|
||||
if (input instanceof StatementList) {
|
||||
Builder<Statement> statements = ImmutableList.<Statement> builder();
|
||||
for (Statement statement : StatementList.class.cast(input).getStatements())
|
||||
statements.add(apply(statement));
|
||||
return new StatementList(statements.build());
|
||||
} else if (input instanceof AdminAccess) {
|
||||
return AdminAccess.class.cast(input).apply(adminAccessConfiguration);
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.statements.login;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.jclouds.crypto.Sha512Crypt;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.StatementList;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
|
||||
import org.jclouds.scriptbuilder.statements.ssh.SshStatements;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
||||
/**
|
||||
* Controls the administrative access to a node. By default, it will perform the
|
||||
* following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>setup a new admin user which folks should use as opposed to the built-in
|
||||
* vcloud account</li>
|
||||
* <ul>
|
||||
* <li>associate a random password to account</li>
|
||||
* <ul>
|
||||
* <li>securely ( use sha 512 on client side and literally rewrite the shadow
|
||||
* entry, rather than pass password to OS in a script )</li>
|
||||
* </ul>
|
||||
* <li>associate the users' ssh public key with the account for login</li> <li>
|
||||
* associate it with the os group wheel</li> </ul> <li>create os group wheel</li>
|
||||
* <li>add sudoers for nopassword access to root by group wheel</li> <li>reset
|
||||
* root password securely</li> <li>lockdown sshd_config for no root login, nor
|
||||
* passwords allowed</li> </ul>
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class AdminAccess implements Statement, Function<Configuration, AdminAccess> {
|
||||
public static AdminAccess.Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static AdminAccess.Builder builder(Function<String, String> cryptFunction) {
|
||||
return new Builder(cryptFunction);
|
||||
}
|
||||
|
||||
public static AdminAccess standard() {
|
||||
return new Builder().build();
|
||||
}
|
||||
|
||||
@ImplementedBy(DefaultConfiguration.class)
|
||||
public static interface Configuration {
|
||||
Supplier<String> defaultAdminUsername();
|
||||
|
||||
Supplier<Map<String, String>> defaultAdminSshKeys();
|
||||
|
||||
Supplier<String> passwordGenerator();
|
||||
|
||||
Function<String, String> cryptFunction();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final Function<String, String> cryptFunction;
|
||||
|
||||
public Builder() {
|
||||
this(Sha512Crypt.function());
|
||||
}
|
||||
|
||||
public Builder(Function<String, String> cryptFunction) {
|
||||
this.cryptFunction = cryptFunction;
|
||||
}
|
||||
|
||||
private String adminUsername;
|
||||
private String adminPublicKey;
|
||||
private File adminPublicKeyFile;
|
||||
private String adminPrivateKey;
|
||||
private File adminPrivateKeyFile;
|
||||
private String adminPassword;
|
||||
private String loginPassword;
|
||||
private boolean lockSsh = true;
|
||||
private boolean grantSudoToAdminUser = true;
|
||||
private boolean authorizeAdminPublicKey = true;
|
||||
private boolean installAdminPrivateKey = false;
|
||||
private boolean resetLoginPassword = true;
|
||||
|
||||
public AdminAccess.Builder adminUsername(String adminUsername) {
|
||||
this.adminUsername = adminUsername;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder adminPassword(String adminPassword) {
|
||||
this.adminPassword = adminPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder loginPassword(String loginPassword) {
|
||||
this.loginPassword = loginPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder lockSsh(boolean lockSsh) {
|
||||
this.lockSsh = lockSsh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder resetLoginPassword(boolean resetLoginPassword) {
|
||||
this.resetLoginPassword = resetLoginPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder authorizeAdminPublicKey(boolean authorizeAdminPublicKey) {
|
||||
this.authorizeAdminPublicKey = authorizeAdminPublicKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder installAdminPrivateKey(boolean installAdminPrivateKey) {
|
||||
this.installAdminPrivateKey = installAdminPrivateKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder grantSudoToAdminUser(boolean grantSudoToAdminUser) {
|
||||
this.grantSudoToAdminUser = grantSudoToAdminUser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder adminPublicKey(File adminPublicKey) {
|
||||
this.adminPublicKeyFile = adminPublicKey;
|
||||
this.adminPublicKey = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder adminPublicKey(String adminPublicKey) {
|
||||
this.adminPublicKey = adminPublicKey;
|
||||
this.adminPublicKeyFile = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder adminPrivateKey(File adminPrivateKey) {
|
||||
this.adminPrivateKeyFile = adminPrivateKey;
|
||||
this.adminPrivateKey = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess.Builder adminPrivateKey(String adminPrivateKey) {
|
||||
this.adminPrivateKey = adminPrivateKey;
|
||||
this.adminPrivateKeyFile = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminAccess build() {
|
||||
try {
|
||||
String adminPublicKey = this.adminPublicKey;
|
||||
if (adminPublicKey == null && adminPublicKeyFile != null)
|
||||
adminPublicKey = Files.toString(adminPublicKeyFile, UTF_8);
|
||||
String adminPrivateKey = this.adminPrivateKey;
|
||||
if (adminPrivateKey == null && adminPrivateKeyFile != null)
|
||||
adminPrivateKey = Files.toString(adminPrivateKeyFile, UTF_8);
|
||||
return new AdminAccess(adminUsername, adminPublicKey, adminPrivateKey, adminPassword, loginPassword,
|
||||
lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey, resetLoginPassword,
|
||||
cryptFunction);
|
||||
} catch (IOException e) {
|
||||
Throwables.propagate(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String adminUsername;
|
||||
private final String adminPublicKey;
|
||||
private final String adminPrivateKey;
|
||||
private final String adminPassword;
|
||||
private final String loginPassword;
|
||||
private final boolean lockSsh;
|
||||
private final boolean grantSudoToAdminUser;
|
||||
private final boolean authorizeAdminPublicKey;
|
||||
private final boolean installAdminPrivateKey;
|
||||
private final boolean resetLoginPassword;
|
||||
private final Function<String, String> cryptFunction;
|
||||
private final Credentials adminCredentials;
|
||||
|
||||
protected AdminAccess(@Nullable String adminUsername, @Nullable String adminPublicKey,
|
||||
@Nullable String adminPrivateKey, @Nullable String adminPassword, @Nullable String loginPassword,
|
||||
boolean lockSsh, boolean grantSudoToAdminUser, boolean authorizeAdminPublicKey,
|
||||
boolean installAdminPrivateKey, boolean resetLoginPassword, Function<String, String> cryptFunction) {
|
||||
this.adminUsername = adminUsername;
|
||||
this.adminPublicKey = adminPublicKey;
|
||||
this.adminPrivateKey = adminPrivateKey;
|
||||
this.adminPassword = adminPassword;
|
||||
this.loginPassword = loginPassword;
|
||||
this.lockSsh = lockSsh;
|
||||
this.grantSudoToAdminUser = grantSudoToAdminUser;
|
||||
this.authorizeAdminPublicKey = authorizeAdminPublicKey;
|
||||
this.installAdminPrivateKey = installAdminPrivateKey;
|
||||
this.resetLoginPassword = resetLoginPassword;
|
||||
this.cryptFunction = cryptFunction;
|
||||
if (adminUsername != null && authorizeAdminPublicKey && adminPrivateKey != null)
|
||||
this.adminCredentials = new Credentials(adminUsername, adminPrivateKey);
|
||||
else
|
||||
this.adminCredentials = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return new credentials or null if unchanged or unavailable
|
||||
*/
|
||||
@Nullable
|
||||
public Credentials getAdminCredentials() {
|
||||
return adminCredentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> functionDependencies(OsFamily family) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminAccess apply(Configuration configuration) {
|
||||
Builder builder = AdminAccess.builder(configuration.cryptFunction());
|
||||
builder.adminUsername(this.adminUsername != null ? this.adminUsername : configuration.defaultAdminUsername()
|
||||
.get());
|
||||
builder.adminPassword(this.adminPassword != null ? this.adminPassword : configuration.passwordGenerator().get());
|
||||
Map<String, String> adminSshKeys = (adminPublicKey != null && adminPrivateKey != null) ? ImmutableMap.of(
|
||||
"public", adminPublicKey, "private", adminPrivateKey) : configuration.defaultAdminSshKeys().get();
|
||||
builder.adminPublicKey(adminSshKeys.get("public"));
|
||||
builder.adminPrivateKey(adminSshKeys.get("private"));
|
||||
builder.loginPassword(this.loginPassword != null ? this.loginPassword : configuration.passwordGenerator().get());
|
||||
builder.grantSudoToAdminUser(this.grantSudoToAdminUser);
|
||||
builder.authorizeAdminPublicKey(this.authorizeAdminPublicKey);
|
||||
builder.installAdminPrivateKey(this.installAdminPrivateKey);
|
||||
builder.lockSsh(this.lockSsh);
|
||||
builder.resetLoginPassword(this.resetLoginPassword);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(OsFamily family) {
|
||||
checkNotNull(family, "family");
|
||||
if (family == OsFamily.WINDOWS)
|
||||
throw new UnsupportedOperationException("windows not yet implemented");
|
||||
checkNotNull(adminUsername, "adminUsername");
|
||||
checkNotNull(adminPassword, "adminPassword");
|
||||
checkNotNull(adminPublicKey, "adminPublicKey");
|
||||
checkNotNull(adminPrivateKey, "adminPrivateKey");
|
||||
checkNotNull(loginPassword, "loginPassword");
|
||||
|
||||
ImmutableList.Builder<Statement> statements = ImmutableList.<Statement> builder();
|
||||
UserAdd.Builder userBuilder = UserAdd.builder();
|
||||
userBuilder.login(adminUsername);
|
||||
if (authorizeAdminPublicKey)
|
||||
userBuilder.authorizeRSAPublicKey(adminPublicKey);
|
||||
userBuilder.password(adminPassword);
|
||||
if (installAdminPrivateKey)
|
||||
userBuilder.installRSAPrivateKey(adminPrivateKey);
|
||||
if (grantSudoToAdminUser) {
|
||||
statements.add(SudoStatements.createWheel());
|
||||
userBuilder.group("wheel");
|
||||
}
|
||||
statements.add(userBuilder.build().cryptFunction(cryptFunction));
|
||||
if (lockSsh)
|
||||
statements.add(SshStatements.lockSshd());
|
||||
if (resetLoginPassword) {
|
||||
statements.add(ShadowStatements.resetLoginUserPasswordTo(loginPassword).cryptFunction(cryptFunction));
|
||||
}
|
||||
return new StatementList(statements.build()).render(family);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.statements.login;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.crypto.Sha512Crypt;
|
||||
import org.jclouds.crypto.SshKeys;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
|
||||
import org.jclouds.util.PasswordGenerator;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@Singleton
|
||||
public class DefaultConfiguration implements Configuration {
|
||||
|
||||
private final Supplier<String> defaultAdminUsername = Suppliers.ofInstance(System.getProperty("user.name"));
|
||||
private final Supplier<Map<String, String>> defaultAdminSshKeys = new Supplier<Map<String, String>>() {
|
||||
|
||||
@Override
|
||||
public Map<String, String> get() {
|
||||
try {
|
||||
return ImmutableMap.of("public",
|
||||
Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa.pub"), UTF_8), "private",
|
||||
Files.toString(new File(System.getProperty("user.home") + "/.ssh/id_rsa"), UTF_8));
|
||||
} catch (IOException e) {
|
||||
return SshKeys.generate();
|
||||
}
|
||||
}
|
||||
};
|
||||
private final Supplier<String> passwordGenerator = PasswordGenerator.INSTANCE;
|
||||
private final Function<String, String> cryptFunction = Sha512Crypt.function();
|
||||
|
||||
@Override
|
||||
public Supplier<String> defaultAdminUsername() {
|
||||
return defaultAdminUsername;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Map<String, String>> defaultAdminSshKeys() {
|
||||
return defaultAdminSshKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<String> passwordGenerator() {
|
||||
return passwordGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> cryptFunction() {
|
||||
return cryptFunction;
|
||||
}
|
||||
}
|
|
@ -23,13 +23,17 @@ import static com.google.common.base.Throwables.propagate;
|
|||
import static java.lang.String.format;
|
||||
import static org.jclouds.scriptbuilder.domain.Statements.exec;
|
||||
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.crypto.Sha512Crypt;
|
||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.StatementList;
|
||||
import org.jclouds.scriptbuilder.util.Sha512Crypt;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
* Replaces the password entry for a user in the shadow file, using SHA-512
|
||||
|
@ -42,6 +46,16 @@ public class ReplaceShadowPasswordEntry implements Statement {
|
|||
private final String login;
|
||||
private final String password;
|
||||
|
||||
private Function<String, String> cryptFunction = Sha512Crypt.function();
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named("CRYPT")
|
||||
@VisibleForTesting
|
||||
ReplaceShadowPasswordEntry cryptFunction(Function<String, String> cryptFunction) {
|
||||
this.cryptFunction = cryptFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplaceShadowPasswordEntry(String login, String password) {
|
||||
this.login = checkNotNull(login, "login");
|
||||
this.password = checkNotNull(password, "password");
|
||||
|
@ -58,7 +72,7 @@ public class ReplaceShadowPasswordEntry implements Statement {
|
|||
if (family == OsFamily.WINDOWS)
|
||||
throw new UnsupportedOperationException("windows not yet implemented");
|
||||
try {
|
||||
String shadowPasswordEntry = Sha512Crypt.makeShadowLine(password, null, new JCECrypto());
|
||||
String shadowPasswordEntry = cryptFunction.apply(password);
|
||||
String shadowFile = "/etc/shadow";
|
||||
// note we are using awk variables so that the user can be defined as a
|
||||
// shell variable (ex. $USER) As the block is in single quotes,
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package org.jclouds.scriptbuilder.statements.login;
|
||||
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
|
||||
/**
|
||||
* Statements used to manipulate the shadow file
|
||||
|
@ -31,7 +30,7 @@ public class ShadowStatements {
|
|||
* note must be run as root, and will either reset the root password, or
|
||||
* whoever sudoed to root.
|
||||
*/
|
||||
public static Statement resetLoginUserPasswordTo(String password) {
|
||||
public static ReplaceShadowPasswordEntryOfLoginUser resetLoginUserPasswordTo(String password) {
|
||||
return new ReplaceShadowPasswordEntryOfLoginUser(password);
|
||||
}
|
||||
}
|
|
@ -23,21 +23,24 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
import org.jclouds.crypto.Sha512Crypt;
|
||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.StatementList;
|
||||
import org.jclouds.scriptbuilder.domain.Statements;
|
||||
import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
|
||||
import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
|
||||
import org.jclouds.scriptbuilder.util.Sha512Crypt;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -127,6 +130,16 @@ public class UserAdd implements Statement {
|
|||
private final List<String> authorizeRSAPublicKeys;
|
||||
private final String shell;
|
||||
|
||||
private Function<String, String> cryptFunction = Sha512Crypt.function();
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named("CRYPT")
|
||||
@VisibleForTesting
|
||||
UserAdd cryptFunction(Function<String, String> cryptFunction) {
|
||||
this.cryptFunction = cryptFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> functionDependencies(OsFamily family) {
|
||||
return ImmutableList.of();
|
||||
|
@ -157,7 +170,7 @@ public class UserAdd implements Statement {
|
|||
userAddOptions.put("-d", homeDir);
|
||||
if (password != null) {
|
||||
try {
|
||||
userAddOptions.put("-p", "'" + Sha512Crypt.makeShadowLine(password, null, new JCECrypto()) + "'");
|
||||
userAddOptions.put("-p", "'" + cryptFunction.apply(password) + "'");
|
||||
} catch (Exception e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.functions;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.Statements;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class CredentialsFromAdminAccessTest {
|
||||
|
||||
public void testWhenNotAdminAccess() {
|
||||
|
||||
Statement statement = Statements.exec("echo hello");
|
||||
assertEquals(CredentialsFromAdminAccess.INSTANCE.apply(statement), null);
|
||||
|
||||
Statement statementList = Statements.newStatementList(statement);
|
||||
assertEquals(CredentialsFromAdminAccess.INSTANCE.apply(statementList), null);
|
||||
|
||||
}
|
||||
|
||||
public void testWhenAdminAccess() {
|
||||
AdminAccess.Configuration configuration = createMock(AdminAccess.Configuration.class);
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
Credentials creds = createMock(Credentials.class);
|
||||
|
||||
expect(statement.getAdminCredentials()).andReturn(creds);
|
||||
|
||||
replay(configuration);
|
||||
replay(statement);
|
||||
replay(creds);
|
||||
|
||||
assertEquals(CredentialsFromAdminAccess.INSTANCE.apply(statement), creds);
|
||||
|
||||
verify(configuration);
|
||||
verify(statement);
|
||||
verify(creds);
|
||||
}
|
||||
|
||||
public void testWhenAdminAccessInsideList() {
|
||||
AdminAccess.Configuration configuration = createMock(AdminAccess.Configuration.class);
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
Credentials creds = createMock(Credentials.class);
|
||||
|
||||
expect(statement.getAdminCredentials()).andReturn(creds);
|
||||
|
||||
replay(configuration);
|
||||
replay(statement);
|
||||
replay(creds);
|
||||
|
||||
assertEquals(CredentialsFromAdminAccess.INSTANCE.apply(Statements.newStatementList(statement)), creds);
|
||||
|
||||
verify(configuration);
|
||||
verify(statement);
|
||||
verify(creds);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.functions;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
import org.jclouds.scriptbuilder.domain.Statements;
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class InitAdminAccessTest {
|
||||
|
||||
public void testWhenNotAdminAccess() {
|
||||
|
||||
InitAdminAccess initAdminAccess = new InitAdminAccess(createMock(AdminAccess.Configuration.class));
|
||||
Statement statement = Statements.exec("echo hello");
|
||||
assertEquals(initAdminAccess.apply(statement), statement);
|
||||
|
||||
Statement statementList = Statements.newStatementList(statement);
|
||||
assertEquals(initAdminAccess.apply(statementList), statementList);
|
||||
|
||||
}
|
||||
|
||||
public void testWhenAdminAccess() {
|
||||
AdminAccess.Configuration configuration = createMock(AdminAccess.Configuration.class);
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
AdminAccess newStatement = createMock(AdminAccess.class);
|
||||
|
||||
expect(statement.apply(configuration)).andReturn(newStatement);
|
||||
|
||||
replay(configuration);
|
||||
replay(statement);
|
||||
replay(newStatement);
|
||||
InitAdminAccess initAdminAccess = new InitAdminAccess(configuration);
|
||||
|
||||
assertEquals(initAdminAccess.apply(statement), newStatement);
|
||||
|
||||
verify(configuration);
|
||||
verify(statement);
|
||||
verify(newStatement);
|
||||
}
|
||||
|
||||
public void testWhenAdminAccessInsideList() {
|
||||
AdminAccess.Configuration configuration = createMock(AdminAccess.Configuration.class);
|
||||
AdminAccess statement = createMock(AdminAccess.class);
|
||||
AdminAccess newStatement = createMock(AdminAccess.class);
|
||||
|
||||
expect(statement.apply(configuration)).andReturn(newStatement);
|
||||
|
||||
replay(configuration);
|
||||
replay(statement);
|
||||
replay(newStatement);
|
||||
InitAdminAccess initAdminAccess = new InitAdminAccess(configuration);
|
||||
|
||||
assertEquals(initAdminAccess.apply(Statements.newStatementList(statement)),
|
||||
Statements.newStatementList(newStatement));
|
||||
|
||||
verify(configuration);
|
||||
verify(statement);
|
||||
verify(newStatement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.statements.login;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Resources;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", singleThreaded = true)
|
||||
public class AdminAccessTest {
|
||||
|
||||
public void testStandardUNIX() throws IOException {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
try {
|
||||
assertEquals(AdminAccess.standard().apply(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
|
||||
CharStreams.toString(Resources.newReaderSupplier(Resources.getResource("test_adminaccess_standard.sh"),
|
||||
Charsets.UTF_8)));
|
||||
} finally {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void testWithParamsUNIX() throws IOException {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
try {
|
||||
assertEquals(
|
||||
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
|
||||
.adminPublicKey("fooPublicKey").adminUsername("foo").build().apply(TestConfiguration.INSTANCE)
|
||||
.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(
|
||||
Resources.getResource("test_adminaccess_params.sh"), Charsets.UTF_8)));
|
||||
} finally {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void testOnlyInstallUserUNIX() throws IOException {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
try {
|
||||
assertEquals(
|
||||
AdminAccess.builder().grantSudoToAdminUser(false).authorizeAdminPublicKey(true)
|
||||
.installAdminPrivateKey(true).lockSsh(false).resetLoginPassword(false).build()
|
||||
.apply(TestConfiguration.INSTANCE).render(OsFamily.UNIX), CharStreams.toString(Resources
|
||||
.newReaderSupplier(Resources.getResource("test_adminaccess_plainuser.sh"), Charsets.UTF_8)));
|
||||
} finally {
|
||||
TestConfiguration.INSTANCE.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testCreateWheelWindowsNotSupported() {
|
||||
AdminAccess.standard().apply(TestConfiguration.INSTANCE).render(OsFamily.WINDOWS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed 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.jclouds.scriptbuilder.statements.login;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.scriptbuilder.statements.login.AdminAccess.Configuration;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public enum TestConfiguration implements Configuration {
|
||||
INSTANCE;
|
||||
int pwCount = 0;
|
||||
|
||||
public TestConfiguration reset() {
|
||||
pwCount = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
private final Supplier<String> defaultAdminUsername = Suppliers.ofInstance("defaultAdminUsername");
|
||||
private final Supplier<Map<String, String>> defaultAdminSshKeys = Suppliers
|
||||
.<Map<String, String>> ofInstance(ImmutableMap.of("public", "publicKey", "private", "privateKey"));
|
||||
private final Supplier<String> passwordGenerator = new Supplier<String>() {
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return pwCount++ + "";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final Function<String, String> cryptFunction = new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return String.format("crypt(%s)", input);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@Override
|
||||
public Supplier<String> defaultAdminUsername() {
|
||||
return defaultAdminUsername;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Map<String, String>> defaultAdminSshKeys() {
|
||||
return defaultAdminSshKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<String> passwordGenerator() {
|
||||
return passwordGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> cryptFunction() {
|
||||
return cryptFunction;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
rm /etc/sudoers
|
||||
cat >> /etc/sudoers <<'END_OF_FILE'
|
||||
root ALL = (ALL) ALL
|
||||
%wheel ALL = (ALL) NOPASSWD:ALL
|
||||
END_OF_FILE
|
||||
chmod 0440 /etc/sudoers
|
||||
mkdir -p /home/users/defaultAdminUsername
|
||||
groupadd -f wheel
|
||||
useradd -s /bin/bash -g wheel -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
publicKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
|
||||
chown -R defaultAdminUsername /home/users/defaultAdminUsername
|
||||
exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
|
||||
/etc/init.d/sshd reload||/etc/init.d/ssh reload
|
||||
awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(1)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
|
||||
test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
|
|
@ -0,0 +1,21 @@
|
|||
rm /etc/sudoers
|
||||
cat >> /etc/sudoers <<'END_OF_FILE'
|
||||
root ALL = (ALL) ALL
|
||||
%wheel ALL = (ALL) NOPASSWD:ALL
|
||||
END_OF_FILE
|
||||
chmod 0440 /etc/sudoers
|
||||
mkdir -p /home/users/foo
|
||||
groupadd -f wheel
|
||||
useradd -s /bin/bash -g wheel -d /home/users/foo -p 'crypt(bar)' foo
|
||||
mkdir -p /home/users/foo/.ssh
|
||||
cat >> /home/users/foo/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
fooPublicKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/foo/.ssh/authorized_keys
|
||||
chown -R foo /home/users/foo
|
||||
exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
|
||||
/etc/init.d/sshd reload||/etc/init.d/ssh reload
|
||||
awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(0)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
|
||||
test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
|
|
@ -0,0 +1,14 @@
|
|||
mkdir -p /home/users/defaultAdminUsername
|
||||
useradd -s /bin/bash -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
publicKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
|
||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||
rm /home/users/defaultAdminUsername/.ssh/id_rsa
|
||||
cat >> /home/users/defaultAdminUsername/.ssh/id_rsa <<'END_OF_FILE'
|
||||
privateKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/defaultAdminUsername/.ssh/id_rsa
|
||||
chown -R defaultAdminUsername /home/users/defaultAdminUsername
|
|
@ -0,0 +1,21 @@
|
|||
rm /etc/sudoers
|
||||
cat >> /etc/sudoers <<'END_OF_FILE'
|
||||
root ALL = (ALL) ALL
|
||||
%wheel ALL = (ALL) NOPASSWD:ALL
|
||||
END_OF_FILE
|
||||
chmod 0440 /etc/sudoers
|
||||
mkdir -p /home/users/defaultAdminUsername
|
||||
groupadd -f wheel
|
||||
useradd -s /bin/bash -g wheel -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE'
|
||||
publicKey
|
||||
END_OF_FILE
|
||||
chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
|
||||
chown -R defaultAdminUsername /home/users/defaultAdminUsername
|
||||
exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
|
||||
/etc/init.d/sshd reload||/etc/init.d/ssh reload
|
||||
awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(1)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
|
||||
test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
|
Loading…
Reference in New Issue