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