From 9d99f85807737b18a81ebed2a9f5fdb18d4794b7 Mon Sep 17 00:00:00 2001 From: Jason King Date: Fri, 14 Oct 2011 15:55:24 +0100 Subject: [PATCH] Issue 720: Log the ssh key fingerprint information --- drivers/jsch/pom.xml | 81 +++++++++++++++++++ .../org/jclouds/ssh/jsch/JschSshClient.java | 25 ++++-- .../ssh/jsch/JschSshClientLiveTest.java | 9 ++- .../jclouds/ssh/jsch/JschSshClientTest.java | 57 ++++++++++++- .../jsch/config/JschSshClientModuleTest.java | 3 +- drivers/jsch/src/test/resources/logback.xml | 15 ++++ 6 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 drivers/jsch/src/test/resources/logback.xml diff --git a/drivers/jsch/pom.xml b/drivers/jsch/pom.xml index cfa79c40a2..de7522d121 100644 --- a/drivers/jsch/pom.xml +++ b/drivers/jsch/pom.xml @@ -44,6 +44,14 @@ + + localhost + 22 + + + + + org.jclouds @@ -62,6 +70,17 @@ test-jar test + + org.jclouds.driver + jclouds-slf4j + ${project.version} + + + ch.qos.logback + logback-classic + 0.9.30 + test + com.jcraft jsch @@ -89,4 +108,66 @@ + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + + none + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + file.encoding + UTF-8 + + + test.ssh.host + ${test.ssh.host} + + + test.ssh.port + ${test.ssh.port} + + + test.ssh.username + ${test.ssh.username} + + + test.ssh.keyfile + ${test.ssh.keyfile} + + + test.ssh.password + ${test.ssh.password} + + + + + + + + + + diff --git a/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java b/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java index 426956da02..6c7c56a21b 100644 --- a/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java +++ b/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java @@ -25,6 +25,8 @@ import static com.google.common.base.Predicates.instanceOf; import static com.google.common.base.Predicates.or; import static com.google.common.base.Throwables.getCausalChain; import static com.google.common.collect.Iterables.any; +import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; +import static org.jclouds.crypto.SshKeys.sha1PrivateKey; import java.io.IOException; import java.io.InputStream; @@ -90,6 +92,7 @@ public class JschSshClient implements SshClient { private final int port; private final String username; private final String password; + private final String toString; @Inject(optional = true) @Named("jclouds.ssh.max-retries") @@ -131,6 +134,14 @@ public class JschSshClient implements SshClient { this.timeout = timeout; this.password = password; this.privateKey = privateKey; + if ( privateKey==null ) { + this.toString = String.format("%s:password@%s:%d", username, host, port); + } else { + String fingerPrint = fingerprintPrivateKey(new String(privateKey)); + String sha1 = sha1PrivateKey(new String(privateKey)); + this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host, + port); + } } @Override @@ -182,7 +193,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return String.format("Session(%s)", JschSshClient.this.toString()); + return String.format("Session(timeout=%d)", timeout); } }; @@ -237,7 +248,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return "ChannelSftp(" + JschSshClient.this.toString() + ")"; + return "ChannelSftp()"; } }; @@ -263,7 +274,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return "Payload(" + JschSshClient.this.toString() + ")[" + path + "]"; + return "Payload(path=[" + path + "])"; } }; @@ -301,7 +312,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return "Put(" + JschSshClient.this.toString() + ")[" + path + "]"; + return "Put(path=[" + path + "])"; } }; @@ -354,7 +365,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return String.format("%s@%s:%d", username, host, port); + return toString; } @PreDestroy @@ -389,7 +400,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return "ChannelExec(" + JschSshClient.this.toString() + ")"; + return "ChannelExec()"; } }; @@ -437,7 +448,7 @@ public class JschSshClient implements SshClient { @Override public String toString() { - return "ExecResponse(" + JschSshClient.this.toString() + ")[" + command + "]"; + return "ExecResponse(command=[" + command + "])"; } } diff --git a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientLiveTest.java b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientLiveTest.java index 70a9574274..7e2b57f035 100644 --- a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientLiveTest.java +++ b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientLiveTest.java @@ -24,11 +24,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.net.InetAddress; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.domain.Credentials; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.net.IPSocket; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.jsch.config.JschSshClientModule; @@ -44,7 +46,7 @@ import com.google.inject.Injector; * * @author Adrian Cole */ -@Test(groups = "live") +@Test(groups = "live", testName = "JschSshClientLiveTest" ) public class JschSshClientLiveTest { protected static final String sshHost = System.getProperty("test.ssh.host", "localhost"); protected static final String sshPort = System.getProperty("test.ssh.port", "22"); @@ -106,7 +108,7 @@ public class JschSshClientLiveTest { }; } else { - Injector i = Guice.createInjector(new JschSshClientModule()); + Injector i = Guice.createInjector(new JschSshClientModule(), new SLF4JLoggingModule()); SshClient.Factory factory = i.getInstance(SshClient.Factory.class); SshClient connection; if (sshKeyFile != null && !sshKeyFile.trim().equals("")) { @@ -139,7 +141,8 @@ public class JschSshClientLiveTest { public void testExecHostname() throws IOException { ExecResponse response = setupClient().exec("hostname"); assertEquals(response.getError(), ""); - assertEquals(response.getOutput().trim(), sshHost); + assertEquals(response.getOutput().trim(), "localhost".equals(sshHost) ? InetAddress.getLocalHost().getHostName() + : sshHost); } } \ No newline at end of file diff --git a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientTest.java b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientTest.java index 4ca2408ac4..86d7d1bfcc 100644 --- a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientTest.java +++ b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/JschSshClientTest.java @@ -21,8 +21,11 @@ package org.jclouds.ssh.jsch; import java.io.IOException; import java.net.ConnectException; import java.net.UnknownHostException; +import java.util.Properties; +import com.google.inject.AbstractModule; import org.jclouds.domain.Credentials; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.net.IPSocket; import org.jclouds.rest.AuthorizationException; import org.jclouds.ssh.SshClient; @@ -38,6 +41,8 @@ import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpException; +import static com.google.inject.name.Names.bindProperties; + /** * * @author Adrian Cole @@ -49,11 +54,20 @@ public class JschSshClientTest { @BeforeTest public void setupSsh() throws UnknownHostException { - ssh = createClient(); + ssh = createClient(new Properties()); } protected JschSshClient createClient() throws UnknownHostException { - Injector i = Guice.createInjector(module()); + return createClient(new Properties()); + } + + protected JschSshClient createClient(final Properties props) throws UnknownHostException { + Injector i = Guice.createInjector(module(), new AbstractModule() { + @Override + protected void configure() { + bindProperties(binder(), props); + } + }, new SLF4JLoggingModule()); SshClient.Factory factory = i.getInstance(SshClient.Factory.class); JschSshClient ssh = JschSshClient.class.cast(factory.create(new IPSocket("localhost", 22), new Credentials( "username", "password"))); @@ -83,6 +97,13 @@ public class JschSshClientTest { assert ssh1.shouldRetry(new AuthorizationException("problem", null)); } + public void testOnlyRetryAuthWhenSetViaProperties() throws UnknownHostException { + Properties props = new Properties(); + props.setProperty("jclouds.ssh.retry-auth", "true"); + JschSshClient ssh1 = createClient(props); + assert ssh1.shouldRetry(new AuthorizationException("problem", null)); + } + public void testExceptionMessagesRetry() { assert !ssh.shouldRetry(new NullPointerException("")); assert !ssh.shouldRetry(new NullPointerException((String) null)); @@ -107,12 +128,40 @@ public class JschSshClientTest { assert !ssh.causalChainHasMessageContaining(new NullPointerException()).apply(" End of IO Stream Read"); } + // Seems to be using oauth now instead of bouncycastle and is more strict. + // Commands used to generate this key are: + // openssl dsaparam -outform PEM 2048 > dsaparams + // openssl gendsa -aes256 -out privkey.pem dsaparams + private static final String key = "-----BEGIN DSA PRIVATE KEY-----\n" + + "Proc-Type: 4,ENCRYPTED\n" + + "DEK-Info: AES-256-CBC,8C3A84B8BDA9FE69AAB4A322D7795161\n" + + "\n" + + "B8nuwNIcw5UtV9gGX2LiOq5OZ0uDgWsRon/mmWu+8EFd6X1aautVw8pCZuNusNkS\n" + + "GZlO1JBIgKdX6Qqx0cPsirFB7GTNbBVHOIMqYbmQKW5Ju+n+NkNIomDJDJqBWknE\n" + + "ZIkegznvdLN11r6F4jreusnVepSNYeRwKxA5KAT0S6XsgVFKSJZIyJj8EKZl/25D\n" + + "a7LKoYRlf5QK+Q1/zmMyZcCt0irIMcHxslpVlyATajAADB0hwBl4Xh0H3oHR3PU1\n" + + "xhsliYTARGov6Wn7adDCG9zWDzO7cX3941ub0FPoDdPLxGkmwqEwijF1XWvYbIUC\n" + + "EBjomG3pwjC1kfoqAYJhThi8vmQYtFyCagcZMauHDKuqwUr1o3jS51PBe3bKeg4M\n" + + "dP/JSTiTAUGtwV/MobThQFvCWJm22jIR6Eb0IYPcncUQuZ1QO2piwSvMUZZYCFX/\n" + + "uY7fHkPZyBkZIrxGc91jhSQlo8qsVBIThJpLYI2M2PjKxTzsgZ2mWWK/Zm7pVqqI\n" + + "ldTJ1cpSMb2/9BsXF0CWvzaC4qN5Ymmrp152M4ZEnsRN3ycd9wFCD2cM7w5frj4Z\n" + + "3Q3M9/qNYuPfHddJa1DIkYpZxTTzOo4BBWx2O32D3in+2YZWjBgdxej17hKVkmUR\n" + + "C432CtEqUYAtVv//3TZ47hYsywvvVEX/3ljcCObrHKDha6i3SwXMqe1tL3BYexOn\n" + + "LS8aGQ148oekWaSWYrXCo0gjuJgY3hJZKUHoKdhvyW/FZG3rMjk5NlU9IwjMweqz\n" + + "Bznl7sMxHqtW4BPV9fM4uaiM8LOMkIm6euu/1a2o///TaEgFr/H1ybLdcg8Au1Iy\n" + + "sH68Xn+pmkx1bdVCCpi44EtAEHrpX11AC+cuvu8KG0A+Tpy3WW7YXYkregEQM3kF\n" + + "XzzyJfHuZKvM7qXsMnt/T5VCYX1LSEtXFABFMHDsPC9qs1LVLdSC5U0Ux0Ac0Sqq\n" + + "MRG2Yc8hDOPvPOmiqD9OK9PC6fa+bbMEtlS2O5Cd0l+hoE8OD1EFP1hAGQ0ivjZT\n" + + "zMJXBUVUtYAkrpU6NcY+ub7IyYBR3wOWSAUbolx3K4p2o8k3MGFdLHb4dGvypIv2\n" + + "oHhZLNLYGPrAN2g0gpNmlepDS1aG6422770O/Eh1bDXDyGYJRW3INwWenN8KbuYd\n" + + "-----END DSA PRIVATE KEY-----"; + public void testPrivateKeyWithPassphrase() throws UnknownHostException { - Injector i = Guice.createInjector(module()); + Injector i = Guice.createInjector(module(),new SLF4JLoggingModule()); SshClient.Factory factory = i.getInstance(SshClient.Factory.class); try { JschSshClient ssh = JschSshClient.class.cast(factory.create(new IPSocket("localhost", 22), new Credentials( - "username", "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,123\n\n123"))); + "username", key))); ssh.connect(); assert false; // this code should never be reached. } catch (SshException e) { diff --git a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/config/JschSshClientModuleTest.java b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/config/JschSshClientModuleTest.java index 413dd796d1..fd2b628953 100644 --- a/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/config/JschSshClientModuleTest.java +++ b/drivers/jsch/src/test/java/org/jclouds/ssh/jsch/config/JschSshClientModuleTest.java @@ -21,6 +21,7 @@ package org.jclouds.ssh.jsch.config; import java.net.UnknownHostException; import org.jclouds.domain.Credentials; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.net.IPSocket; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.jsch.JschSshClient; @@ -39,7 +40,7 @@ public class JschSshClientModuleTest { public void testConfigureBindsClient() throws UnknownHostException { - Injector i = Guice.createInjector(new JschSshClientModule()); + Injector i = Guice.createInjector(new JschSshClientModule(), new SLF4JLoggingModule()); SshClient.Factory factory = i.getInstance(SshClient.Factory.class); SshClient connection = factory.create(new IPSocket("localhost", 22), new Credentials("username", "password")); assert connection instanceof JschSshClient; diff --git a/drivers/jsch/src/test/resources/logback.xml b/drivers/jsch/src/test/resources/logback.xml new file mode 100644 index 0000000000..d7a1f1accc --- /dev/null +++ b/drivers/jsch/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + target/test-data/jclouds-ssh.log + true + + %-4relative [%thread] %-5level %logger{35} - %msg%n + + + + + + + +