From 179ed3b2f6688ca51e579ac3451945b2d015b279 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Thu, 16 Jun 2016 23:32:07 +0200 Subject: [PATCH] Properly set the permissions in the ~/.ssh folder When writing the ~/.ssh/authorized_keys file in a script that is being run as sudo, the file is created with the root owner, instead of the user defined by the node credentials. File ownership should be enforced to make sure the right owner is alwaays set. --- .../BaseComputeServiceContextModule.java | 3 -- ...ment.java => InstallKeysAndRunScript.java} | 12 ++++-- .../NodeAndTemplateOptionsToStatement.java | 40 +++++++++++++++++++ ...ateOptionsToStatementWithoutPublicKey.java | 17 ++++---- ...dAddToGoodMapOrPutExceptionIntoBadMap.java | 14 ++++--- ...ptionsToStatementWithoutPublicKeyTest.java | 21 +++++----- ...ToGoodMapOrPutExceptionIntoBadMapTest.java | 11 ++--- .../test/resources/initscript_with_java.sh | 1 + .../test/resources/initscript_with_jetty.sh | 1 + .../test/resources/runscript_adminUpdate.sh | 1 + ...italOcean2ComputeServiceContextModule.java | 6 +-- .../statements/login/UserAdd.java | 2 +- .../ssh/AuthorizeRSAPublicKeys.java | 16 ++++++-- .../statements/login/UserAddTest.java | 2 +- .../ssh/AuthorizeRSAPublicKeyTest.java | 28 +++++++++---- .../resources/test_adminaccess_flipped.sh | 1 + .../test/resources/test_adminaccess_params.sh | 1 + .../test_adminaccess_params_and_fullname.sh | 1 + .../resources/test_adminaccess_plainuser.sh | 1 + .../resources/test_adminaccess_standard.sh | 1 + 20 files changed, 128 insertions(+), 52 deletions(-) rename compute/src/main/java/org/jclouds/compute/functions/{TemplateOptionsToStatement.java => InstallKeysAndRunScript.java} (83%) create mode 100644 compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java rename providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java => compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java (76%) rename providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java => compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java (75%) diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java index 4acaef9fe18..8d9aa0ee327 100644 --- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java +++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java @@ -47,7 +47,6 @@ import org.jclouds.compute.extensions.SecurityGroupExtension; import org.jclouds.compute.functions.CreateSshClientOncePortIsListeningOnNode; import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials; -import org.jclouds.compute.functions.TemplateOptionsToStatement; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; @@ -89,8 +88,6 @@ protected void configure() { install(new ComputeServiceTimeoutsModule()); bind(new TypeLiteral>() { }).to(CreateSshClientOncePortIsListeningOnNode.class); - bind(new TypeLiteral>() { - }).to(TemplateOptionsToStatement.class); bind(LoginCredentials.class).annotatedWith(Names.named("image")).toProvider( GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull.class); diff --git a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java similarity index 83% rename from compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java rename to compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java index 87d299da3a1..594c4247d1b 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java +++ b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java @@ -22,6 +22,7 @@ import javax.inject.Singleton; +import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statement; @@ -29,17 +30,20 @@ import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey; -import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; @Singleton -public class TemplateOptionsToStatement implements Function { +public class InstallKeysAndRunScript implements NodeAndTemplateOptionsToStatement { @Override - public Statement apply(TemplateOptions options) { + public Statement apply(NodeMetadata node, TemplateOptions options) { + String user = options.getLoginUser(); + if (user == null && node.getCredentials() != null) { + user = node.getCredentials().getUser(); + } List bootstrap = newArrayList(); if (options.getPublicKey() != null) - bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey()))); + bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey()), user)); if (options.getRunScript() != null) bootstrap.add(options.getRunScript()); if (options.getPrivateKey() != null) diff --git a/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java new file mode 100644 index 00000000000..90c74b60593 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.compute.functions; + +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.inject.ImplementedBy; + +/** + * Returns the statement to be executed on the node. + */ +@ImplementedBy(InstallKeysAndRunScript.class) +public interface NodeAndTemplateOptionsToStatement { + + /** + * Returns the script that has to be executed in the given node. + * + * @return The script to be executed or null if no script needs + * to be run. + */ + @Nullable + Statement apply(NodeMetadata node, TemplateOptions options); +} diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java similarity index 76% rename from providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java rename to compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java index 52dcb0ef590..c4e26dd0b2a 100644 --- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java +++ b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java @@ -14,29 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.digitalocean2.compute.functions; +package org.jclouds.compute.functions; import javax.inject.Singleton; -import org.jclouds.compute.functions.TemplateOptionsToStatement; +import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.scriptbuilder.InitScript; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey; + import com.google.common.collect.ImmutableList; /** - * Convert the template options into a statement, but ignoring the public key. + * Convert the node and template options into a statement, but ignoring the + * public key. *

- * The {@link org.jclouds.DigitalOcean2ComputeServiceAdapter.compute.strategy.DigitalOceanComputeServiceAdapter} already takes care of - * installing it using the {@link org.jclouds.digitalocean.features.KeyPairApi}. + * Providers that can install the public key using their API should bind this + * strategy to avoid an unnecessary SSH connection to manually upload it. */ @Singleton -public class TemplateOptionsToStatementWithoutPublicKey extends TemplateOptionsToStatement { +public class NodeAndTemplateOptionsToStatementWithoutPublicKey implements NodeAndTemplateOptionsToStatement { @Override - public Statement apply(TemplateOptions options) { + public Statement apply(NodeMetadata node, TemplateOptions options) { ImmutableList.Builder builder = ImmutableList.builder(); if (options.getRunScript() != null) { builder.add(options.getRunScript()); @@ -55,5 +57,4 @@ public Statement apply(TemplateOptions options) { return null; } - } diff --git a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java index 98419d1b8b2..2bb383c41be 100644 --- a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java +++ b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java @@ -34,6 +34,7 @@ import org.jclouds.compute.config.CustomizationResponse; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.util.OpenSocketFinder; @@ -66,7 +67,7 @@ Function, Void> create(TemplateOptions options, Se private final OpenSocketFinder openSocketFinder; @Nullable - private final Statement statement; + private final NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement; private final TemplateOptions options; private AtomicReference node; private final Set goodNodes; @@ -78,13 +79,13 @@ Function, Void> create(TemplateOptions options, Se @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named(TIMEOUT_NODE_RUNNING) Function, AtomicReference> pollNodeRunning, - OpenSocketFinder openSocketFinder, Function templateOptionsToStatement, + OpenSocketFinder openSocketFinder, NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted AtomicReference node, @Assisted Set goodNodes, @Assisted Map badNodes, @Assisted Multimap customizationResponses) { - this.statement = checkNotNull(templateOptionsToStatement, "templateOptionsToStatement").apply( - checkNotNull(options, "options")); + this.nodeAndTemplateOptionsToStatement = checkNotNull(nodeAndTemplateOptionsToStatement, + "nodeAndTemplateOptionsToStatement"); this.pollNodeRunning = checkNotNull(pollNodeRunning, "pollNodeRunning"); this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory"); this.openSocketFinder = checkNotNull(openSocketFinder, "openSocketFinder"); @@ -99,11 +100,11 @@ public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named(TIMEOUT_NODE_RUNNING) Function, AtomicReference> pollNodeRunning, GetNodeMetadataStrategy getNode, OpenSocketFinder openSocketFinder, - Function templateOptionsToStatement, + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted Set goodNodes, @Assisted Map badNodes, @Assisted Multimap customizationResponses) { - this(pollNodeRunning, openSocketFinder, templateOptionsToStatement, initScriptRunnerFactory, options, + this(pollNodeRunning, openSocketFinder, nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, new AtomicReference(null), goodNodes, badNodes, customizationResponses); } @@ -115,6 +116,7 @@ public Void call() { try { if (options.shouldBlockUntilRunning()) { pollNodeRunning.apply(node); + Statement statement = nodeAndTemplateOptionsToStatement.apply(node.get(), options); if (statement != null) { RunScriptOnNode runner = initScriptRunnerFactory.create(node.get(), statement, options, badNodes).call(); if (runner != null) { diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java similarity index 75% rename from providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java rename to compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java index c3a6cd26247..d85dc750e33 100644 --- a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java +++ b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.digitalocean2.compute.functions; +package org.jclouds.compute.functions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; @@ -31,18 +31,17 @@ import org.testng.annotations.Test; /** - * Unit tests for the {@link TemplateOptionsToStatementWithoutPublicKey} class. + * Unit tests for the {@link NodeAndTemplateOptionsToStatementWithoutPublicKey} class. */ -@Test(groups = "unit", testName = "TemplateOptionsToStatementWithoutPublicKeyTest") -public class TemplateOptionsToStatementWithoutPublicKeyTest { +@Test(groups = "unit", testName = "NodeAndTemplateOptionsToStatementWithoutPublicKeyTest") +public class NodeAndTemplateOptionsToStatementWithoutPublicKeyTest { @Test public void testPublicKeyDoesNotGenerateAuthorizePublicKeyStatementIfOnlyPublicKeyOptionsConfigured() { Map keys = SshKeys.generate(); TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")); - - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - assertNull(function.apply(options)); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + assertNull(function.apply(null, options)); } @Test @@ -50,8 +49,8 @@ public void testPublicAndRunScriptKeyDoesNotGenerateAuthorizePublicKeyStatementI Map keys = SshKeys.generate(); TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")).runScript("uptime"); - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - Statement statement = function.apply(options); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + Statement statement = function.apply(null, options); assertEquals(statement.render(OsFamily.UNIX), "uptime\n"); } @@ -62,8 +61,8 @@ public void testPublicAndPrivateKeyAndRunScriptDoesNotGenerateAuthorizePublicKey TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")) .installPrivateKey(keys.get("private")).runScript("uptime"); - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - Statement statement = function.apply(options); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + Statement statement = function.apply(null, options); assertTrue(statement instanceof StatementList); StatementList statements = (StatementList) statement; diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java index 822bd848968..857c0063155 100644 --- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java +++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java @@ -32,7 +32,8 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.domain.NodeMetadataBuilder; -import org.jclouds.compute.functions.TemplateOptionsToStatement; +import org.jclouds.compute.functions.InstallKeysAndRunScript; +import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.util.OpenSocketFinder; import org.jclouds.scriptbuilder.domain.Statement; @@ -53,7 +54,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { public void testBreakOnIllegalStateExceptionDuringPollNode() { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); - Function templateOptionsToStatement = new TemplateOptionsToStatement(); + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript(); @SuppressWarnings("unused") Statement statement = null; TemplateOptions options = new TemplateOptions(); @@ -79,7 +80,7 @@ public AtomicReference apply(AtomicReference node) { // run AtomicReference atomicNode = Atomics.newReference(pendingNode); new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(pollNodeRunning, openSocketFinder, - templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, + nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, customizationResponses).apply(atomicNode); assertEquals(goodNodes.size(), 0); @@ -95,7 +96,7 @@ public void testBreakGraceWhenNodeSocketFailsToOpen() { int portTimeoutSecs = 2; InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); - Function templateOptionsToStatement = new TemplateOptionsToStatement(); + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript(); TemplateOptions options = new TemplateOptions().blockOnPort(22, portTimeoutSecs); Set goodNodes = Sets.newLinkedHashSet(); Map badNodes = Maps.newLinkedHashMap(); @@ -124,7 +125,7 @@ public AtomicReference apply(AtomicReference node) { // run AtomicReference atomicNode = Atomics.newReference(pendingNode); new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(pollNodeRunning, openSocketFinder, - templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, + nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, customizationResponses).apply(atomicNode); assertEquals(goodNodes.size(), 0); diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh index 6c2d077cd7a..bea054a86fc 100644 --- a/compute/src/test/resources/initscript_with_java.sh +++ b/compute/src/test/resources/initscript_with_java.sh @@ -219,6 +219,7 @@ END_OF_JCLOUDS_SCRIPT publicKey END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys + chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh chown -R defaultAdminUsername /home/users/defaultAdminUsername exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/compute/src/test/resources/initscript_with_jetty.sh b/compute/src/test/resources/initscript_with_jetty.sh index 1e662427f1f..3b399cd5280 100644 --- a/compute/src/test/resources/initscript_with_jetty.sh +++ b/compute/src/test/resources/initscript_with_jetty.sh @@ -219,6 +219,7 @@ END_OF_JCLOUDS_SCRIPT publicKey END_OF_JCLOUDS_FILE chmod 600 /home/users/web/.ssh/authorized_keys + chown -R web /home/users/web/.ssh chown -R web /home/users/web exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/compute/src/test/resources/runscript_adminUpdate.sh b/compute/src/test/resources/runscript_adminUpdate.sh index eca129a7844..9d2ade99a01 100644 --- a/compute/src/test/resources/runscript_adminUpdate.sh +++ b/compute/src/test/resources/runscript_adminUpdate.sh @@ -100,6 +100,7 @@ END_OF_JCLOUDS_SCRIPT publicKey END_OF_JCLOUDS_FILE chmod 600 /over/ridden/foo/.ssh/authorized_keys + chown -R foo /over/ridden/foo/.ssh chown -R foo /over/ridden/foo exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java index 03caf856873..e5b59ba91ef 100644 --- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java +++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java @@ -32,7 +32,8 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.extensions.ImageExtension; -import org.jclouds.compute.functions.TemplateOptionsToStatement; +import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement; +import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatementWithoutPublicKey; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; @@ -45,7 +46,6 @@ import org.jclouds.digitalocean2.compute.functions.ImageInRegionToImage; import org.jclouds.digitalocean2.compute.functions.RegionToLocation; import org.jclouds.digitalocean2.compute.functions.SizeToHardware; -import org.jclouds.digitalocean2.compute.functions.TemplateOptionsToStatementWithoutPublicKey; import org.jclouds.digitalocean2.compute.internal.ImageInRegion; import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions; import org.jclouds.digitalocean2.compute.strategy.CreateKeyPairsThenCreateNodes; @@ -91,7 +91,7 @@ protected void configure() { bind(CreateNodesInGroupThenAddToSet.class).to(CreateKeyPairsThenCreateNodes.class); bind(TemplateOptions.class).to(DigitalOcean2TemplateOptions.class); - bind(TemplateOptionsToStatement.class).to(TemplateOptionsToStatementWithoutPublicKey.class); + bind(NodeAndTemplateOptionsToStatement.class).to(NodeAndTemplateOptionsToStatementWithoutPublicKey.class); bind(new TypeLiteral() { }).to(DigitalOcean2ImageExtension.class); diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java index fe48d66f9bd..4d12b867745 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java @@ -231,7 +231,7 @@ public String render(OsFamily family) { if (!authorizeRSAPublicKeys.isEmpty() || installRSAPrivateKey != null) { String sshDir = homeDir + "{fs}.ssh"; if (!authorizeRSAPublicKeys.isEmpty()) - statements.add(new AuthorizeRSAPublicKeys(sshDir, authorizeRSAPublicKeys)); + statements.add(new AuthorizeRSAPublicKeys(sshDir, authorizeRSAPublicKeys, login)); if (installRSAPrivateKey != null) statements.add(new InstallRSAPrivateKey(sshDir, installRSAPrivateKey)); } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java index 384a59ed74d..13ec2ddf50d 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java @@ -22,6 +22,7 @@ import java.util.List; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; @@ -34,14 +35,20 @@ public class AuthorizeRSAPublicKeys implements Statement { private final String sshDir; private final List publicKeys; - + private final String owner; + public AuthorizeRSAPublicKeys(Iterable publicKeys) { - this("~/.ssh", publicKeys); + this("~/.ssh", publicKeys, null); + } + + public AuthorizeRSAPublicKeys(Iterable publicKeys, @Nullable String owner) { + this("~/.ssh", publicKeys, owner); } - public AuthorizeRSAPublicKeys(String sshDir, Iterable publicKeys) { + public AuthorizeRSAPublicKeys(String sshDir, Iterable publicKeys, @Nullable String owner) { this.sshDir = checkNotNull(sshDir, "sshDir"); this.publicKeys = ImmutableList.copyOf(checkNotNull(publicKeys, "publicKeys")); + this.owner = owner; } @Override @@ -59,6 +66,9 @@ public String render(OsFamily family) { String authorizedKeys = sshDir + "{fs}authorized_keys"; statements.add(appendFile(authorizedKeys, Splitter.on('\n').split(Joiner.on("\n\n").join(publicKeys)))); statements.add(exec("chmod 600 " + authorizedKeys)); + if (owner != null) { + statements.add(exec(String.format("chown -R %s %s", owner, sshDir))); + } return new StatementList(statements.build()).render(family); } } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java index 76d64e6dacc..f24022dbffc 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java @@ -70,7 +70,7 @@ public void testWithPasswordUNIX() { public void testWithSshAuthorizedKeyUNIX() { assertEquals( UserAdd.builder().login("me").authorizeRSAPublicKey("rsapublickey").build().render(OsFamily.UNIX), - "mkdir -p /home/users\nchmod 0755 /home/users\nuseradd -c me -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n\trsapublickey\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me\n"); + "mkdir -p /home/users\nchmod 0755 /home/users\nuseradd -c me -s /bin/bash -m -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n\trsapublickey\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me/.ssh\nchown -R me /home/users/me\n"); } public void testWithSshInstalledKeyUNIX() { diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java index acc76a42266..ba11e982160 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java @@ -27,6 +27,17 @@ public class AuthorizeRSAPublicKeyTest { public void testAuthorizeRSAPublicKeyUNIXCurrentUser() { + assertEquals( + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.UNIX), + "mkdir -p ~/.ssh\n" + + "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" + + "\tssh-dss AAAAB\n" + + "END_OF_JCLOUDS_FILE\n" + + "chmod 600 ~/.ssh/authorized_keys\n" + + "chown -R jclouds ~/.ssh\n"); + } + + public void testAuthorizeRSAPublicKeyUNIXNoOwner() { assertEquals( new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), "mkdir -p ~/.ssh\n" + @@ -38,29 +49,31 @@ public void testAuthorizeRSAPublicKeyUNIXCurrentUser() { public void testAuthorizeRSAPublicKeyUNIXCurrentUserWith2Keys() { assertEquals( - new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")).render(OsFamily.UNIX), + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD"), "jclouds").render(OsFamily.UNIX), "mkdir -p ~/.ssh\n" + "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" + "\tssh-dss AAAAB\n" + "\t\n" + "\tssh-dss CCCCD\n" + "END_OF_JCLOUDS_FILE\n" + - "chmod 600 ~/.ssh/authorized_keys\n"); + "chmod 600 ~/.ssh/authorized_keys\n" + + "chown -R jclouds ~/.ssh\n"); } public void testAuthorizeRSAPublicKeyUNIXSpecifiedDir() { assertEquals( - new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), + new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.UNIX), "mkdir -p /home/me/.ssh\n" + "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" + "\tssh-dss AAAAB\n" + "END_OF_JCLOUDS_FILE\n" + - "chmod 600 /home/me/.ssh/authorized_keys\n"); + "chmod 600 /home/me/.ssh/authorized_keys\n" + + "chown -R jclouds /home/me/.ssh\n"); } public void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() { assertEquals( - new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")) + new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD"), "jclouds") .render(OsFamily.UNIX), "mkdir -p /home/me/.ssh\n" + "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" + @@ -68,11 +81,12 @@ public void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() { "\t\n" + "\tssh-dss CCCCD\n" + "END_OF_JCLOUDS_FILE\n" + - "chmod 600 /home/me/.ssh/authorized_keys\n"); + "chmod 600 /home/me/.ssh/authorized_keys\n" + + "chown -R jclouds /home/me/.ssh\n"); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testAuthorizeRSAPublicKeyWINDOWS() { - new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.WINDOWS); + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.WINDOWS); } } diff --git a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh index cd2695fdcf3..f2c6e044599 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh @@ -15,6 +15,7 @@ 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/.ssh chown -R defaultAdminUsername /home/users/defaultAdminUsername exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params.sh b/scriptbuilder/src/test/resources/test_adminaccess_params.sh index 42d4852ca66..ee61aa6c222 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_params.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_params.sh @@ -14,6 +14,7 @@ cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' fooPublicKey END_OF_JCLOUDS_FILE chmod 600 /over/ridden/foo/.ssh/authorized_keys +chown -R foo /over/ridden/foo/.ssh chown -R foo /over/ridden/foo exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh b/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh index 20b1ee63b98..2ec9b27e623 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh @@ -14,6 +14,7 @@ cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE' fooPublicKey END_OF_JCLOUDS_FILE chmod 600 /over/ridden/foo/.ssh/authorized_keys +chown -R foo /over/ridden/foo/.ssh chown -R foo /over/ridden/foo exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no diff --git a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh index 5583ab4ff44..1f5164ca8be 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh @@ -6,6 +6,7 @@ cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_ publicKey END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys +chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh mkdir -p /home/users/defaultAdminUsername/.ssh rm /home/users/defaultAdminUsername/.ssh/id_rsa cat >> /home/users/defaultAdminUsername/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE' diff --git a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh index db10e19cdfb..5e0c79dcb2a 100644 --- a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh +++ b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh @@ -14,6 +14,7 @@ cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_ publicKey END_OF_JCLOUDS_FILE chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys +chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh chown -R defaultAdminUsername /home/users/defaultAdminUsername exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no PermitRootLogin no