brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From drigod...@apache.org
Subject [1/2] brooklyn-server git commit: Adds authSudo support (for sudo’ing with password)
Date Mon, 10 Apr 2017 10:22:04 GMT
Repository: brooklyn-server
Updated Branches:
  refs/heads/master 2575bc466 -> 5b34d0a14


Adds authSudo support (for sudo’ing with password)

Required for some clouds (e.g. azure-arm), when using
`useJcloudsSshInit: false`


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/2e3e82f5
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/2e3e82f5
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/2e3e82f5

Branch: refs/heads/master
Commit: 2e3e82f57bb621b247a2849c26c7d87b73249846
Parents: 2575bc4
Author: Aled Sage <aled.sage@gmail.com>
Authored: Fri Apr 7 18:07:32 2017 +0100
Committer: Aled Sage <aled.sage@gmail.com>
Committed: Fri Apr 7 20:27:50 2017 +0100

----------------------------------------------------------------------
 .../core/internal/ssh/ShellAbstractTool.java    | 37 ++++++++++++++------
 .../util/core/internal/ssh/ShellTool.java       |  4 ++-
 .../util/core/internal/ssh/SshTool.java         |  1 -
 .../ssh/sshj/SshjToolIntegrationTest.java       | 24 +++++++++++++
 .../location/jclouds/JcloudsLocation.java       | 17 +++++----
 .../apache/brooklyn/util/ssh/BashCommands.java  | 11 ++++++
 6 files changed, 75 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellAbstractTool.java
b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellAbstractTool.java
index d641478..befef2e 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellAbstractTool.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellAbstractTool.java
@@ -219,11 +219,13 @@ public abstract class ShellAbstractTool implements ShellTool {
         protected final OutputStream out;
         protected final OutputStream err;
         protected final String scriptDir;
-        protected final Boolean runAsRoot;
-        protected final Boolean noExtraOutput;
-        protected final Boolean noDeleteAfterExec;
+        protected final boolean runAsRoot;
+        protected final boolean authSudo;
+        protected final boolean noExtraOutput;
+        protected final boolean noDeleteAfterExec;
         protected final String scriptNameWithoutExtension;
         protected final String scriptPath;
+        protected final String password;
         protected final Duration execTimeout;
 
         public ToolAbstractExecScript(Map<String,?> props) {
@@ -233,9 +235,11 @@ public abstract class ShellAbstractTool implements ShellTool {
             this.err = getOptionalVal(props, PROP_ERR_STREAM);
             
             this.scriptDir = getOptionalVal(props, PROP_SCRIPT_DIR);
-            this.runAsRoot = getOptionalVal(props, PROP_RUN_AS_ROOT);
-            this.noExtraOutput = getOptionalVal(props, PROP_NO_EXTRA_OUTPUT);
-            this.noDeleteAfterExec = getOptionalVal(props, PROP_NO_DELETE_SCRIPT);
+            this.runAsRoot = Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT));
+            this.authSudo = Boolean.TRUE.equals(getOptionalVal(props, PROP_AUTH_SUDO));
+            this.noExtraOutput = Boolean.TRUE.equals(getOptionalVal(props, PROP_NO_EXTRA_OUTPUT));
+            this.noDeleteAfterExec = Boolean.TRUE.equals(getOptionalVal(props, PROP_NO_DELETE_SCRIPT));
+            this.password = getOptionalVal(props, PROP_PASSWORD);
             this.execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT);
             
             String summary = getOptionalVal(props, PROP_SUMMARY);
@@ -253,12 +257,23 @@ public abstract class ShellAbstractTool implements ShellTool {
         /** builds the command to run the given script;
          * note that some modes require \$RESULT passed in order to access a variable, whereas
most just need $ */
         protected List<String> buildRunScriptCommand() {
+            String scriptInvocationCmd;
+            if (runAsRoot) {
+                if (authSudo) {
+                    scriptInvocationCmd = BashCommands.authSudo(scriptPath, password);
+                } else {
+                    scriptInvocationCmd = BashCommands.sudo(scriptPath) + " < /dev/null";
+                }
+            } else {
+                scriptInvocationCmd = scriptPath + " < /dev/null";
+            }
+            
             MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add((runAsRoot ? BashCommands.sudo(scriptPath) : scriptPath) + " <
/dev/null")
+                    .add(scriptInvocationCmd)
                     .add("RESULT=$?");
-            if (noExtraOutput==null || !noExtraOutput)
+            if (!noExtraOutput)
                 cmds.add("echo Executed "+scriptPath+", result $RESULT"); 
-            if (noDeleteAfterExec!=Boolean.TRUE) {
+            if (!noDeleteAfterExec) {
                 // use "-f" because some systems have "rm" aliased to "rm -i"
                 // use "< /dev/null" to guarantee doesn't hang
                 cmds.add("rm -f "+scriptPath+" < /dev/null");
@@ -304,7 +319,7 @@ public abstract class ShellAbstractTool implements ShellTool {
                     .add(runAsRoot ? BashCommands.sudo(cmd) : cmd)
                     .add("echo $! > "+pidPath)
                     .add("RESULT=$?");
-            if (noExtraOutput==null || !noExtraOutput) {
+            if (!noExtraOutput) {
                 cmds.add("echo Executing async "+scriptPath);
             }
             cmds.add("exit $RESULT");
@@ -416,7 +431,7 @@ public abstract class ShellAbstractTool implements ShellTool {
         protected List<String> deleteTemporaryFilesCommand() {
             ImmutableList.Builder<String> cmdParts = ImmutableList.builder();
             
-            if (!Boolean.TRUE.equals(noDeleteAfterExec)) {
+            if (!noDeleteAfterExec) {
                 // use "-f" because some systems have "rm" aliased to "rm -i"
                 // use "< /dev/null" to guarantee doesn't hang
                 cmdParts.add(

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellTool.java
b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellTool.java
index d2d42b4..b135e6b 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellTool.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/ShellTool.java
@@ -43,7 +43,9 @@ public interface ShellTool {
     // config which applies to calls:
     
     public static final ConfigKey<Boolean> PROP_RUN_AS_ROOT = newConfigKey("runAsRoot",
"When running a script, whether to run as root", Boolean.FALSE);
-    
+    public static final ConfigKey<Boolean> PROP_AUTH_SUDO = newConfigKey("authSudo",
"When to run `sudo` commands with password authentication", Boolean.FALSE);
+    public static final ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password",
"Password to use (e.g. for authSudo, or for ssh to connect)", null);
+
     public static final ConfigKey<OutputStream> PROP_OUT_STREAM = newConfigKey(OutputStream.class,
"out", "Stream to which to capture stdout");
     public static final ConfigKey<OutputStream> PROP_ERR_STREAM = newConfigKey(OutputStream.class,
"err", "Stream to which to capture stderr");
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
index 3ebc130..8906f1b 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
@@ -66,7 +66,6 @@ public interface SshTool extends ShellTool {
     public static final ConfigKey<String> PROP_HOST = newStringConfigKey("host", "Host
to connect to (required)", null);
     public static final ConfigKey<Integer> PROP_PORT = newConfigKey("port", "Port on
host to connect to", 22);
     public static final ConfigKey<String> PROP_USER = newConfigKey("user", "User to
connect as", System.getProperty("user.name"));
-    public static final ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password",
"Password to use to connect", null);
     
     public static final ConfigKey<String> PROP_PRIVATE_KEY_FILE = newStringConfigKey("privateKeyFile",
"the path of an ssh private key file; leave blank to use defaults (i.e. ~/.ssh/id_rsa and
id_dsa)", null);
     public static final ConfigKey<String> PROP_PRIVATE_KEY_DATA = newStringConfigKey("privateKeyData",
"the private ssh key (e.g. contents of an id_rsa or id_dsa file)", null);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjToolIntegrationTest.java
b/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjToolIntegrationTest.java
index 76f45b9..2d7bfce 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjToolIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjToolIntegrationTest.java
@@ -27,6 +27,7 @@ import static org.testng.Assert.fail;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
@@ -36,6 +37,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.internal.ssh.ShellTool;
 import org.apache.brooklyn.util.core.internal.ssh.SshException;
 import org.apache.brooklyn.util.core.internal.ssh.SshTool;
 import org.apache.brooklyn.util.core.internal.ssh.SshToolAbstractIntegrationTest;
@@ -168,6 +170,28 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest
{
         assertEquals(localtool3.getLocalTempDir(), new File(Os.tidyPath(customRelativeTempDir)));
     }
 
+    // This is just a sanity check - we actually expect passwordless sudo to work, and the
+    // "dummypa55w0rd" to not be right. It will supply that password to sudo's stdin, which
+    // doesn't require it and ignores it. To really integration-test this, we'd probably
have  
+    // to create a new user with sudo-with-password rights, and delete it in tearDown which

+    // seems overkill for testing!
+    @Test(groups = {"Integration"})
+    public void testRunAsRootWithAuthSudo() {
+        final ShellTool localtool = newTool();
+        connect(localtool);
+        Map<String,Object> props = new LinkedHashMap<String, Object>();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        props.put("out", out);
+        props.put("err", err);
+        props.put(SshTool.PROP_RUN_AS_ROOT.getName(), true);
+        props.put(SshTool.PROP_AUTH_SUDO.getName(), true);
+        props.put(SshTool.PROP_PASSWORD.getName(), "dummypa55w0rd");
+        int exitcode = localtool.execScript(props, Arrays.asList("whoami"), null);
+        assertTrue(out.toString().contains("root"), "not running as root; whoami is: "+out+"
(err is '"+err+"')");
+        assertEquals(0, exitcode);
+    }
+
     @Test(groups = {"Integration"})
     public void testAsyncExecStdoutAndStderr() throws Exception {
         boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index 121d5dd..1c2003d 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -1702,14 +1702,19 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation
im
                 }
 
                 String initialUser = initialCredentials.getUser();
-
+                boolean authSudo = initialCredentials.shouldAuthenticateSudo();
+                Optional<String> password = initialCredentials.getOptionalPassword();
+                
                 // TODO Retrying lots of times as workaround for vcloud-director. There the
guest customizations
                 // can cause the VM to reboot shortly after it was ssh'able.
-                Map<String,Object> execProps = Maps.newLinkedHashMap();
-                execProps.put(ShellTool.PROP_RUN_AS_ROOT.getName(), true);
-                execProps.put(SshTool.PROP_ALLOCATE_PTY.getName(), true);
-                execProps.put(SshTool.PROP_SSH_TRIES.getName(), 50);
-                execProps.put(SshTool.PROP_SSH_TRIES_TIMEOUT.getName(), 10*60*1000);
+                Map<String,Object> execProps = MutableMap.<String, Object>builder()
+                        .put(ShellTool.PROP_RUN_AS_ROOT.getName(), true)
+                        .put(SshTool.PROP_AUTH_SUDO.getName(), authSudo)
+                        .put(SshTool.PROP_ALLOCATE_PTY.getName(), true)
+                        .putIfNotNull(SshTool.PROP_PASSWORD.getName(), authSudo ? password.orNull()
: null)
+                        .put(SshTool.PROP_SSH_TRIES.getName(), 50)
+                .put(SshTool.PROP_SSH_TRIES_TIMEOUT.getName(), 10*60*1000)
+                .build();
 
                 if (LOG.isDebugEnabled()) {
                     LOG.debug("VM {}: executing user creation/setup via {}@{}; commands:
{}", new Object[] {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e3e82f5/utils/common/src/main/java/org/apache/brooklyn/util/ssh/BashCommands.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/ssh/BashCommands.java b/utils/common/src/main/java/org/apache/brooklyn/util/ssh/BashCommands.java
index 193a0d0..4d0fd81 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/ssh/BashCommands.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/ssh/BashCommands.java
@@ -91,12 +91,23 @@ public class BashCommands {
      * If null is supplied, it is returned (sometimes used to indicate no command desired).
      */
     public static String sudo(String command) {
+        if (command==null) return null;
         if (command.startsWith("( ") || command.endsWith(" &"))
             return sudoNew(command);
         else
             return sudoOld(command);
     }
 
+    public static String authSudo(String command, String password) {
+        checkNotNull(password, "password must not be null");
+        if (command==null) return null;
+        if (command.startsWith("( ") || command.endsWith(" &")) {
+            throw new UnsupportedOperationException("authSudo supports only simple commands,
not those wrapped in parentheses or backgrounded: cmd="+command);
+        }
+        // some OS's (which?) fail if you try running sudo when you're already root (dumb
but true)
+        return format("( if test \"$UID\" -eq 0; then ( %s ); else echo -e '%s\\n' | sudo
-E -S -- %s; fi )", command, password, command);
+    }
+
     // TODO would like to move away from sudoOld -- but needs extensive testing!
     
     private static String sudoOld(String command) {


Mime
View raw message