Issue 543, 473: new AdminAccess statement, which locks down compute nodes and creates a default admin account

This commit is contained in:
Adrian Cole 2011-05-06 00:35:50 -07:00
parent 3a690186f4
commit a925d704f4
36 changed files with 1903 additions and 295 deletions

View File

@ -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) {

View File

@ -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() {

View File

@ -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();

View File

@ -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));
}
}

View File

@ -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());
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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
* &lt;drepper@redhat.com&gt;. A discussion of the rationale and development of this algorithm is at
* &lt;drepper@redhat.com&gt;. 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$&lt;rounds=n&gt;$&lt;salt&gt;$&lt;hashed mess&gt;
* The resulting string will be in the form
* '$6$&lt;rounds=n&gt;$&lt;salt&gt;$&lt;hashed mess&gt;
* </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();

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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