From 4b879402fd307af4bdfd974729c205173ffbd79c Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 30 Apr 2011 20:58:50 -0700 Subject: [PATCH] Issue 473: created new function to add a user, and supporting sudoers and shadow logic --- .../main/java/org/jclouds/crypto/Crypto.java | 2 + .../encryption/internal/JCECrypto.java | 14 +- .../test/java/org/jclouds/io/CryptoTest.java | 13 +- scriptbuilder/pom.xml | 13 +- .../domain/AuthorizeRSAPublicKey.java | 28 +- .../domain/InstallRSAPrivateKey.java | 32 +- .../statements/login/SudoStatements.java | 36 +++ .../statements/login/Sudoers.java | 58 ++++ .../statements/login/UserAdd.java | 205 ++++++++++++ .../ssh/AuthorizeRSAPublicKeys.java | 71 +++++ .../statements/ssh/InstallRSAPrivateKey.java | 70 +++++ .../statements/ssh/SshStatements.java | 44 +++ .../statements/ssh/SshdConfig.java | 60 ++++ .../scriptbuilder/util/Sha512Crypt.java | 295 ++++++++++++++++++ .../login/SudoStatementsTest.java} | 18 +- .../statements/login/UserAddTest.java | 77 +++++ .../ssh/AuthorizeRSAPublicKeyTest.java | 63 ++++ .../ssh}/InstallRSAPrivateKeyTest.java | 20 +- .../statements/ssh/SshStatementsTest.java | 52 +++ .../scriptbuilder/util/Sha512CryptTest.java | 78 +++++ 20 files changed, 1172 insertions(+), 77 deletions(-) create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/SudoStatements.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKey.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshStatements.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java create mode 100644 scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Sha512Crypt.java rename scriptbuilder/src/test/java/org/jclouds/scriptbuilder/{domain/AuthorizeRSAPublicKeyTest.java => statements/login/SudoStatementsTest.java} (65%) create mode 100644 scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java create mode 100644 scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java rename scriptbuilder/src/test/java/org/jclouds/scriptbuilder/{domain => statements/ssh}/InstallRSAPrivateKeyTest.java (58%) create mode 100644 scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java create mode 100644 scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/Sha512CryptTest.java diff --git a/core/src/main/java/org/jclouds/crypto/Crypto.java b/core/src/main/java/org/jclouds/crypto/Crypto.java index a218a98b28..187470f7f0 100644 --- a/core/src/main/java/org/jclouds/crypto/Crypto.java +++ b/core/src/main/java/org/jclouds/crypto/Crypto.java @@ -57,4 +57,6 @@ public interface Crypto { MessageDigest sha256(); + MessageDigest sha512(); + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java index 914cd7974e..3afbc2dd5f 100755 --- a/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java +++ b/core/src/main/java/org/jclouds/encryption/internal/JCECrypto.java @@ -73,7 +73,8 @@ public class JCECrypto implements Crypto { public final static String MD5 = "MD5"; public final static String SHA1 = "SHA1"; - public final static String SHA256 = "SHA256"; + public final static String SHA256 = "SHA-256"; + public final static String SHA512 = "SHA-512"; @Override public MessageDigest md5() { @@ -98,7 +99,16 @@ public class JCECrypto implements Crypto { try { return digest(SHA256); } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA256 must be supported", e); + throw new IllegalStateException(SHA256 + " must be supported", e); + } + } + + @Override + public MessageDigest sha512() { + try { + return digest(SHA512); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(SHA512 + " must be supported", e); } } diff --git a/core/src/test/java/org/jclouds/io/CryptoTest.java b/core/src/test/java/org/jclouds/io/CryptoTest.java index e14345dd03..bdaed52388 100644 --- a/core/src/test/java/org/jclouds/io/CryptoTest.java +++ b/core/src/test/java/org/jclouds/io/CryptoTest.java @@ -45,7 +45,7 @@ import com.google.inject.Injector; * * @author Adrian Cole */ -//NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire @Test(groups = "performance", sequential = true, timeOut = 2 * 60 * 1000, testName = "CryptoTest") public class CryptoTest extends PerformanceTest { @@ -122,4 +122,15 @@ public class CryptoTest extends PerformanceTest { assertEquals(hexMD5Digest, b64); } + public void testSHA1() { + crypto.sha1(); + } + + public void testSHA256() { + crypto.sha256(); + } + + public void testSHA512() { + crypto.sha512(); + } } diff --git a/scriptbuilder/pom.xml b/scriptbuilder/pom.xml index 11110c8835..42dd34c6ab 100644 --- a/scriptbuilder/pom.xml +++ b/scriptbuilder/pom.xml @@ -36,15 +36,10 @@ - - com.google.guava - guava - r09 - - - com.google.code.findbugs - jsr305 - 1.3.9 + + ${project.groupId} + jclouds-core + ${project.version} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java index 25863b5214..00b8b0016e 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java @@ -19,37 +19,21 @@ package org.jclouds.scriptbuilder.domain; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.scriptbuilder.domain.Statements.appendFile; -import static org.jclouds.scriptbuilder.domain.Statements.exec; -import java.util.Collections; +import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; /** * * @author Adrian Cole + * @see AuthorizeRSAPublicKeys */ -public class AuthorizeRSAPublicKey implements Statement { - - private final String publicKey; +@Deprecated +public class AuthorizeRSAPublicKey extends AuthorizeRSAPublicKeys { public AuthorizeRSAPublicKey(String publicKey) { - this.publicKey = checkNotNull(publicKey, "publicKey"); + super(ImmutableSet.of(checkNotNull(publicKey, "publicKey"))); } - @Override - public Iterable functionDependecies(OsFamily family) { - return Collections.emptyList(); - } - - @Override - public String render(OsFamily family) { - checkNotNull(family, "family"); - if (family == OsFamily.WINDOWS) - throw new UnsupportedOperationException("windows not yet implemented"); - return new StatementList(ImmutableList.of(exec("{md} ~/.ssh"), appendFile("~/.ssh/authorized_keys", Splitter.on('\n') - .split(publicKey)), exec("chmod 600 ~/.ssh/authorized_keys"))).render(family); - } } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java index 1427475c9a..84a2a002e0 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java @@ -18,39 +18,15 @@ */ package org.jclouds.scriptbuilder.domain; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.scriptbuilder.domain.Statements.appendFile; -import static org.jclouds.scriptbuilder.domain.Statements.exec; -import static org.jclouds.scriptbuilder.domain.Statements.rm; - -import java.util.Collections; - -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; - /** - * + * @see org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey * @author Adrian Cole */ -public class InstallRSAPrivateKey implements Statement { - - private final String privateKey; +@Deprecated +public class InstallRSAPrivateKey extends org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey { public InstallRSAPrivateKey(String privateKey) { - this.privateKey = checkNotNull(privateKey, "privateKey"); + super(privateKey); } - @Override - public Iterable functionDependecies(OsFamily family) { - return Collections.emptyList(); - } - - @Override - public String render(OsFamily family) { - checkNotNull(family, "family"); - if (family == OsFamily.WINDOWS) - throw new UnsupportedOperationException("windows not yet implemented"); - return new StatementList(ImmutableList.of(exec("{md} ~/.ssh"), rm("~/.ssh/id_rsa"), appendFile("~/.ssh/id_rsa", Splitter.on('\n') - .split(privateKey)), exec("chmod 600 ~/.ssh/id_rsa"))).render(family); - } } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/SudoStatements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/SudoStatements.java new file mode 100644 index 0000000000..089e493229 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/SudoStatements.java @@ -0,0 +1,36 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.scriptbuilder.domain.Statement; + +/** + * Statements used in sudo control + * + * @author Adrian Cole + */ +public class SudoStatements { + + /** + * create new sudoers file with the wheel group authorized to all commands without a password + */ + public static Statement createWheel() { + return new Sudoers(); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java new file mode 100644 index 0000000000..91fd3e718c --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/Sudoers.java @@ -0,0 +1,58 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.exec; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList.Builder; + +/** + * Control /etc/sudoers + * + * @author Adrian Cole + */ +@Beta +public class Sudoers implements Statement { + private static final String sudoers = "/etc/sudoers"; + + public String render(OsFamily family) { + checkNotNull(family, "family"); + if (family == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + Builder statements = ImmutableList. builder(); + statements.add(exec("{rm} " + sudoers)); + statements.add(appendFile(sudoers, ImmutableSet.of("root ALL = (ALL) ALL", "%wheel ALL = (ALL) NOPASSWD:ALL"))); + statements.add(exec("chmod 0440 " + sudoers)); + return new StatementList(statements.build()).render(family); + } + + @Override + public Iterable functionDependecies(OsFamily family) { + return ImmutableList.of(); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..2d7a10cecb --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java @@ -0,0 +1,205 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.Preconditions.checkNotNull; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.jclouds.encryption.internal.JCECrypto; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; +import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; +import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey; +import org.jclouds.scriptbuilder.util.Sha512Crypt; + +import com.google.common.base.Joiner; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +/** + * + * @author Adrian Cole + */ +public class UserAdd implements Statement { + public static UserAdd.Builder builder() { + return new Builder(); + } + + public static class Builder { + private String defaultHome = "/home/users"; + private String login; + private String password; + private String RSAPrivateKey; + private List groups = Lists.newArrayList(); + private List authorizeRSAPublicKeys = Lists.newArrayList(); + private String shell = "/bin/bash"; + + public UserAdd.Builder defaultHome(String defaultHome) { + this.defaultHome = defaultHome; + return this; + } + + public UserAdd.Builder login(String login) { + this.login = login; + return this; + } + + public UserAdd.Builder password(String password) { + this.password = password; + return this; + } + + public UserAdd.Builder group(String group) { + this.groups.add(checkNotNull(group, "group")); + return this; + } + + public UserAdd.Builder groups(Iterable groups) { + this.groups = ImmutableList. copyOf(checkNotNull(groups, "groups")); + return this; + } + + public UserAdd.Builder installRSAPrivateKey(String RSAPrivateKey) { + this.RSAPrivateKey = RSAPrivateKey; + return this; + } + + public UserAdd.Builder authorizeRSAPublicKey(String RSAPublicKey) { + this.authorizeRSAPublicKeys.add(checkNotNull(RSAPublicKey, "RSAPublicKey")); + return this; + } + + public UserAdd.Builder authorizeRSAPublicKeys(Iterable RSAPublicKeys) { + this.authorizeRSAPublicKeys = ImmutableList. copyOf(checkNotNull(RSAPublicKeys, "RSAPublicKeys")); + return this; + } + + public UserAdd.Builder shell(String shell) { + this.shell = shell; + return this; + } + + public UserAdd build() { + return new UserAdd(login, groups, password, RSAPrivateKey, authorizeRSAPublicKeys, defaultHome, shell); + } + } + + public UserAdd(String login, List groups, @Nullable String password, @Nullable String installRSAPrivateKey, + List authorizeRSAPublicKeys, String defaultHome, String shell) { + this.login = checkNotNull(login, "login"); + this.password = password; + this.groups = ImmutableList.copyOf(checkNotNull(groups, "groups")); + this.installRSAPrivateKey = installRSAPrivateKey; + this.authorizeRSAPublicKeys = ImmutableList + .copyOf(checkNotNull(authorizeRSAPublicKeys, "authorizeRSAPublicKeys")); + this.defaultHome = checkNotNull(defaultHome, "defaultHome"); + this.shell = checkNotNull(shell, "shell"); + } + + private final String defaultHome; + private final String login; + private final List groups; + private final String password; + private final String installRSAPrivateKey; + private final List authorizeRSAPublicKeys; + private final String shell; + + @Override + public Iterable functionDependecies(OsFamily family) { + return ImmutableList.of(); + } + + @Override + public String render(OsFamily family) { + checkNotNull(family, "family"); + if (family == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + String homeDir = defaultHome + "{fs}" + login; + ImmutableList.Builder statements = ImmutableList. builder(); + statements.add(Statements.exec("{md} " + homeDir)); + + ImmutableMap.Builder userAddOptions = ImmutableMap. builder(); + userAddOptions.put("-s", shell); + if (groups.size() > 0) { + for (String group : groups) + statements.add(Statements.exec("groupadd -f " + group)); + + List groups = Lists.newArrayList(this.groups); + String primaryGroup = groups.remove(0); + userAddOptions.put("-g", primaryGroup); + if (groups.size() > 0) + userAddOptions.put("-G", Joiner.on(',').join(groups)); + + } + userAddOptions.put("-d", homeDir); + if (password != null) { + try { + userAddOptions.put("-p", "'" + Sha512Crypt.makeShadowLine(password, null, new JCECrypto()) + "'"); + } catch (Exception e) { + Throwables.propagate(e); + } + } + + String options = Joiner.on(' ').withKeyValueSeparator(" ").join(userAddOptions.build()); + + statements.add(Statements.exec(String.format("useradd %s %s", options, login))); + + if (authorizeRSAPublicKeys.size() > 0 || installRSAPrivateKey != null) { + String sshDir = homeDir + "{fs}.ssh"; + if (authorizeRSAPublicKeys.size() > 0) + statements.add(new AuthorizeRSAPublicKeys(sshDir, authorizeRSAPublicKeys)); + if (installRSAPrivateKey != null) + statements.add(new InstallRSAPrivateKey(sshDir, installRSAPrivateKey)); + } + statements.add(Statements.exec(String.format("chown -R %s %s", login, homeDir))); + return new StatementList(statements.build()).render(family); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((login == null) ? 0 : login.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; + UserAdd other = (UserAdd) obj; + if (login == null) { + if (other.login != null) + return false; + } else if (!login.equals(other.login)) + return false; + return true; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..50b25bf453 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java @@ -0,0 +1,71 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.exec; + +import java.util.Collections; +import java.util.List; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +/** + * + * @author Adrian Cole + */ +public class AuthorizeRSAPublicKeys implements Statement { + private final String sshDir; + private final List publicKeys; + + public AuthorizeRSAPublicKeys(Iterable publicKeys) { + this("~/.ssh", publicKeys); + } + + public AuthorizeRSAPublicKeys(String sshDir, Iterable publicKeys) { + this.sshDir = checkNotNull(sshDir, "sshDir"); + this.publicKeys = ImmutableList.copyOf(checkNotNull(publicKeys, "publicKeys")); + } + + @Override + public Iterable functionDependecies(OsFamily family) { + return Collections.emptyList(); + } + + @Override + public String render(OsFamily family) { + checkNotNull(family, "family"); + if (family == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + Builder statements = ImmutableList. builder(); + statements.add(exec("mkdir -p " + sshDir)); + 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)); + return new StatementList(statements.build()).render(family); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKey.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKey.java new file mode 100644 index 0000000000..061f014519 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKey.java @@ -0,0 +1,70 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.exec; + +import java.util.Collections; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +/** + * + * @author Adrian Cole + */ +public class InstallRSAPrivateKey implements Statement { + private final String sshDir; + private final String privateKey; + + public InstallRSAPrivateKey(String privateKey) { + this("~/.ssh", privateKey); + } + + public InstallRSAPrivateKey(String sshDir, String privateKey) { + this.sshDir = checkNotNull(sshDir, "sshDir"); + this.privateKey = checkNotNull(privateKey, "privateKey"); + } + + @Override + public Iterable functionDependecies(OsFamily family) { + return Collections.emptyList(); + } + + @Override + public String render(OsFamily family) { + checkNotNull(family, "family"); + if (family == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + Builder statements = ImmutableList. builder(); + statements.add(exec("{md} " + sshDir)); + String idRsa = sshDir + "{fs}id_rsa"; + statements.add(exec("{rm} " + idRsa)); + statements.add(appendFile(idRsa, Splitter.on('\n').split(privateKey))); + statements.add(exec("chmod 600 " + idRsa)); + return new StatementList(statements.build()).render(family); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshStatements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshStatements.java new file mode 100644 index 0000000000..13d2b9336d --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshStatements.java @@ -0,0 +1,44 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import java.util.Map; + +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.collect.ImmutableMap; + +/** + * Statements used in ssh control + * + * @author Adrian Cole + */ +public class SshStatements { + + /** + * lock sshd down so root cannot login, and password auth is disabled, + */ + public static Statement lockSshd() { + return sshdConfig(ImmutableMap.of("PasswordAuthentication","no", "PermitRootLogin","no")); + } + + public static Statement sshdConfig(Map params) { + return new SshdConfig(params); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java new file mode 100644 index 0000000000..f7a809c7ea --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java @@ -0,0 +1,60 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; + +import java.util.Map; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; + +/** + * Control sshd_config + * + * @author Adrian Cole + */ +public class SshdConfig implements Statement { + private static final String sshdConfig = "/etc/ssh/sshd_config"; + + private final Map params; + + public SshdConfig(Map params) { + this.params = checkNotNull(params, "params"); + } + + public String render(OsFamily family) { + String linesToPrepend = Joiner.on('\n').withKeyValueSeparator(" ").join(params); + Statement prependSshdConfig = exec(String.format( + "exec 3<> %1$s && awk -v TEXT=\"%2$s\n\" 'BEGIN {print TEXT}{print}' %1$s >&3", sshdConfig, + linesToPrepend)); + Statement reloadSshdConfig = exec("/etc/init.d/sshd reload||/etc/init.d/ssh reload"); + return newStatementList(prependSshdConfig, reloadSshdConfig).render(family); + } + + @Override + public Iterable functionDependecies(OsFamily family) { + return ImmutableList.of(); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Sha512Crypt.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Sha512Crypt.java new file mode 100644 index 0000000000..b879ad38a8 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Sha512Crypt.java @@ -0,0 +1,295 @@ +/* + Sha512Crypt.java + + Created: 18 December 2007 + Last Changed By: $Author: broccol $ + Version: $Revision: 7692 $ + Last Mod Date: $Date: 2007-12-30 01:55:31 -0600 (Sun, 30 Dec 2007) $ + + Java Port By: James Ratcliff, falazar@arlut.utexas.edu + + This class implements the new generation, scalable, SHA512-based + Unix 'crypt' algorithm developed by a group of engineers from Red + Hat, Sun, IBM, and HP for common use in the Unix and Linux + /etc/shadow files. + + The Linux glibc library (starting at version 2.7) includes support + for validating passwords hashed using this algorithm. + + The algorithm itself was released into the Public Domain by Ulrich + Drepper . A discussion of the rationale and + development of this algorithm is at + + http://people.redhat.com/drepper/sha-crypt.html + + and the specification and a sample C language implementation is at + + http://people.redhat.com/drepper/SHA-crypt.txt + + This Java Port is + + Copyright (c) 2008 The University of Texas at Austin. + + All rights reserved. + + Redistribution and use in source and binary form are permitted + provided that distributions retain this entire copyright notice + and comment. Neither the name of the University nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. + + */ + +package org.jclouds.scriptbuilder.util; + +import java.security.MessageDigest; + +import javax.annotation.Nullable; + +import org.jclouds.crypto.Crypto; + +/** + * This class defines a method, + * {@link Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int) Sha512_crypt()}, which + * takes a password and a salt string and generates a Sha512 encrypted password entry. + * + * This class implements the new generation, scalable, SHA512-based Unix 'crypt' algorithm developed + * by a group of engineers from Red Hat, Sun, IBM, and HP for common use in the Unix and Linux + * /etc/shadow files. + * + * The Linux glibc library (starting at version 2.7) includes support for validating passwords + * hashed using this algorithm. + * + * The 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 + * + * http://people.redhat.com/drepper/sha-crypt.html + * + * and the specification and a sample C language implementation is at + * + * http://people.redhat.com/drepper/SHA-crypt.txt + */ +public class Sha512Crypt { + static private final String sha512_salt_prefix = "$6$"; + static private final String sha512_rounds_prefix = "rounds="; + static private final int SALT_LEN_MAX = 16; + static private final int ROUNDS_DEFAULT = 5000; + static private final int ROUNDS_MIN = 1000; + static private final int ROUNDS_MAX = 999999999; + static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + /** + * This method actually generates an Sha512 crypted password hash from a plaintext password and a + * salt. + * + *

+ * The resulting string will be in the form '$6$<rounds=n>$<salt>$<hashed mess> + *

+ * + * @param password + * Plaintext password + * + * @param shadowPrefix + * An encoded salt/rounds which will be consulted to determine the salt and round + * count, if not null + * + * @return The Sha512 Unix Crypt hash text for the password + */ + public static String makeShadowLine(String password, @Nullable String shadowPrefix, Crypto crypto) { + MessageDigest ctx = crypto.sha512(); + MessageDigest alt_ctx = crypto.sha512(); + + byte[] alt_result; + byte[] temp_result; + byte[] p_bytes = null; + byte[] s_bytes = null; + int cnt, cnt2; + int rounds = ROUNDS_DEFAULT; // Default number of rounds. + StringBuffer buffer; + + /* -- */ + + if (shadowPrefix != null) { + if (shadowPrefix.startsWith(sha512_salt_prefix)) { + shadowPrefix = shadowPrefix.substring(sha512_salt_prefix.length()); + } + + if (shadowPrefix.startsWith(sha512_rounds_prefix)) { + String num = shadowPrefix.substring(sha512_rounds_prefix.length(), shadowPrefix.indexOf('$')); + int srounds = Integer.valueOf(num).intValue(); + shadowPrefix = shadowPrefix.substring(shadowPrefix.indexOf('$') + 1); + rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX)); + } + + if (shadowPrefix.length() > SALT_LEN_MAX) { + shadowPrefix = shadowPrefix.substring(0, SALT_LEN_MAX); + } + } else { + java.util.Random randgen = new java.util.Random(); + StringBuffer saltBuf = new StringBuffer(); + + while (saltBuf.length() < 16) { + int index = (int) (randgen.nextFloat() * SALTCHARS.length()); + saltBuf.append(SALTCHARS.substring(index, index + 1)); + } + + shadowPrefix = saltBuf.toString(); + } + + byte[] key = password.getBytes(); + byte[] salts = shadowPrefix.getBytes(); + + ctx.reset(); + ctx.update(key, 0, key.length); + ctx.update(salts, 0, salts.length); + + alt_ctx.reset(); + alt_ctx.update(key, 0, key.length); + alt_ctx.update(salts, 0, salts.length); + alt_ctx.update(key, 0, key.length); + + alt_result = alt_ctx.digest(); + + for (cnt = key.length; cnt > 64; cnt -= 64) { + ctx.update(alt_result, 0, 64); + } + + ctx.update(alt_result, 0, cnt); + + for (cnt = key.length; cnt > 0; cnt >>= 1) { + if ((cnt & 1) != 0) { + ctx.update(alt_result, 0, 64); + } else { + ctx.update(key, 0, key.length); + } + } + + alt_result = ctx.digest(); + + alt_ctx.reset(); + + for (cnt = 0; cnt < key.length; ++cnt) { + alt_ctx.update(key, 0, key.length); + } + + temp_result = alt_ctx.digest(); + + p_bytes = new byte[key.length]; + + for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64) { + System.arraycopy(temp_result, 0, p_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt); + + alt_ctx.reset(); + + for (cnt = 0; cnt < 16 + (alt_result[0] & 0xFF); ++cnt) { + alt_ctx.update(salts, 0, salts.length); + } + + temp_result = alt_ctx.digest(); + + s_bytes = new byte[salts.length]; + + for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64) { + System.arraycopy(temp_result, 0, s_bytes, cnt2, 64); + cnt2 += 64; + } + + System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt); + + /* + * Repeatedly run the collected hash value through SHA512 to burn CPU cycles. + */ + + for (cnt = 0; cnt < rounds; ++cnt) { + ctx.reset(); + + if ((cnt & 1) != 0) { + ctx.update(p_bytes, 0, key.length); + } else { + ctx.update(alt_result, 0, 64); + } + + if (cnt % 3 != 0) { + ctx.update(s_bytes, 0, salts.length); + } + + if (cnt % 7 != 0) { + ctx.update(p_bytes, 0, key.length); + } + + if ((cnt & 1) != 0) { + ctx.update(alt_result, 0, 64); + } else { + ctx.update(p_bytes, 0, key.length); + } + + alt_result = ctx.digest(); + } + + buffer = new StringBuffer(sha512_salt_prefix); + + if (rounds != 5000) { + buffer.append(sha512_rounds_prefix); + buffer.append(rounds); + buffer.append("$"); + } + + buffer.append(shadowPrefix); + buffer.append("$"); + + buffer.append(b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4)); + buffer.append(b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4)); + buffer.append(b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4)); + buffer.append(b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4)); + buffer.append(b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4)); + buffer.append(b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4)); + buffer.append(b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4)); + buffer.append(b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4)); + buffer.append(b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4)); + buffer.append(b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4)); + buffer.append(b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4)); + buffer.append(b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4)); + buffer.append(b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4)); + buffer.append(b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4)); + buffer.append(b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4)); + buffer.append(b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4)); + buffer.append(b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4)); + buffer.append(b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4)); + buffer.append(b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4)); + buffer.append(b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4)); + buffer.append(b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4)); + buffer.append(b64_from_24bit((byte) 0x00, (byte) 0x00, alt_result[63], 2)); + + /* + * Clear the buffer for the intermediate result so that people attaching to processes or + * reading core dumps cannot get any information. + */ + + ctx.reset(); + + return buffer.toString(); + } + + private static final String b64_from_24bit(byte B2, byte B1, byte B0, int size) { + int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int) B0 & 0xff); + + StringBuffer result = new StringBuffer(); + + while (--size >= 0) { + result.append(itoa64.charAt((int) (v & 0x3f))); + v >>>= 6; + } + + return result.toString(); + } + +} \ No newline at end of file diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java similarity index 65% rename from scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKeyTest.java rename to scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java index a253f5c81c..3873ff2b0d 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKeyTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/SudoStatementsTest.java @@ -16,27 +16,27 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.scriptbuilder.domain; +package org.jclouds.scriptbuilder.statements.login; import static org.testng.Assert.assertEquals; +import org.jclouds.scriptbuilder.domain.OsFamily; import org.testng.annotations.Test; /** * @author Adrian Cole */ @Test(groups = "unit") -public class AuthorizeRSAPublicKeyTest { +public class SudoStatementsTest { - AuthorizeRSAPublicKey auth = new AuthorizeRSAPublicKey("ssh-dss AAAAB"); - - public void testAuthorizeRSAPublicKeyUNIX() { + public void testCreateWheelUNIX() { assertEquals( - auth.render(OsFamily.UNIX), - "mkdir -p ~/.ssh\ncat >> ~/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\nEND_OF_FILE\nchmod 600 ~/.ssh/authorized_keys\n"); } + SudoStatements.createWheel().render(OsFamily.UNIX), + "rm /etc/sudoers\ncat >> /etc/sudoers <<'END_OF_FILE'\nroot ALL = (ALL) ALL\n%wheel ALL = (ALL) NOPASSWD:ALL\nEND_OF_FILE\nchmod 0440 /etc/sudoers\n"); + } @Test(expectedExceptions = UnsupportedOperationException.class) - public void testAuthorizeRSAPublicKeyWINDOWS() { - auth.render(OsFamily.WINDOWS); + public void testCreateWheelWindowsNotSupported() { + SudoStatements.createWheel().render(OsFamily.WINDOWS); } } 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 new file mode 100644 index 0000000000..477d8d2588 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java @@ -0,0 +1,77 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.scriptbuilder.domain.OsFamily; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class UserAddTest { + + public void testUNIX() { + assertEquals(UserAdd.builder().login("me").build().render(OsFamily.UNIX), + "mkdir -p /home/users/me\nuseradd -s /bin/bash -d /home/users/me me\nchown -R me /home/users/me\n"); + } + + public void testWithBaseUNIX() { + assertEquals(UserAdd.builder().login("me").defaultHome("/export/home").build().render(OsFamily.UNIX), + "mkdir -p /export/home/me\nuseradd -s /bin/bash -d /export/home/me me\nchown -R me /export/home/me\n"); + } + + public void testWithGroupUNIX() { + assertEquals(UserAdd.builder().login("me").group("wheel").build().render(OsFamily.UNIX), + "mkdir -p /home/users/me\ngroupadd -f wheel\nuseradd -s /bin/bash -g wheel -d /home/users/me me\nchown -R me /home/users/me\n"); + } + + public void testWithGroupsUNIX() { + assertEquals(UserAdd.builder().login("me").groups(ImmutableList.of("wheel", "candy")).build().render( + OsFamily.UNIX), + "mkdir -p /home/users/me\ngroupadd -f wheel\ngroupadd -f candy\nuseradd -s /bin/bash -g wheel -G candy -d /home/users/me me\nchown -R me /home/users/me\n"); + } + + public void testWithPasswordUNIX() { + String userAdd = UserAdd.builder().login("me").password("foo").group("wheel").build().render(OsFamily.UNIX); + assert userAdd.startsWith("mkdir -p /home/users/me\ngroupadd -f wheel\nuseradd -s /bin/bash -g wheel -d /home/users/me -p '$6$") : userAdd; + assert userAdd.endsWith("' me\nchown -R me /home/users/me\n") : userAdd; + } + + public void testWithSshAuthorizedKeyUNIX() { + assertEquals( + UserAdd.builder().login("me").authorizeRSAPublicKey("rsapublickey").build().render(OsFamily.UNIX), + "mkdir -p /home/users/me\nuseradd -s /bin/bash -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<'END_OF_FILE'\nrsapublickey\nEND_OF_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me\n"); + } + + public void testWithSshInstalledKeyUNIX() { + assertEquals( + UserAdd.builder().login("me").installRSAPrivateKey("rsaprivate").build().render(OsFamily.UNIX), + "mkdir -p /home/users/me\nuseradd -s /bin/bash -d /home/users/me me\nmkdir -p /home/users/me/.ssh\nrm /home/users/me/.ssh/id_rsa\ncat >> /home/users/me/.ssh/id_rsa <<'END_OF_FILE'\nrsaprivate\nEND_OF_FILE\nchmod 600 /home/users/me/.ssh/id_rsa\nchown -R me /home/users/me\n"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testAddUserWindowsNotSupported() { + UserAdd.builder().login("me").build().render(OsFamily.WINDOWS); + } +} 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 new file mode 100644 index 0000000000..53351c6c17 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java @@ -0,0 +1,63 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class AuthorizeRSAPublicKeyTest { + + public void testAuthorizeRSAPublicKeyUNIXCurrentUser() { + assertEquals( + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), + "mkdir -p ~/.ssh\ncat >> ~/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\nEND_OF_FILE\nchmod 600 ~/.ssh/authorized_keys\n"); + } + + public void testAuthorizeRSAPublicKeyUNIXCurrentUserWith2Keys() { + assertEquals( + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")).render(OsFamily.UNIX), + "mkdir -p ~/.ssh\ncat >> ~/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\n\nssh-dss CCCCD\nEND_OF_FILE\nchmod 600 ~/.ssh/authorized_keys\n"); + } + + public void testAuthorizeRSAPublicKeyUNIXSpecifiedDir() { + assertEquals( + new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX), + "mkdir -p /home/me/.ssh\ncat >> /home/me/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\nEND_OF_FILE\nchmod 600 /home/me/.ssh/authorized_keys\n"); + } + + public void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() { + assertEquals( + new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")) + .render(OsFamily.UNIX), + "mkdir -p /home/me/.ssh\ncat >> /home/me/.ssh/authorized_keys <<'END_OF_FILE'\nssh-dss AAAAB\n\nssh-dss CCCCD\nEND_OF_FILE\nchmod 600 /home/me/.ssh/authorized_keys\n"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testAuthorizeRSAPublicKeyWINDOWS() { + new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.WINDOWS); + } +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java similarity index 58% rename from scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKeyTest.java rename to scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java index f51b503374..2c4b8f2d20 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKeyTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/InstallRSAPrivateKeyTest.java @@ -16,10 +16,11 @@ * limitations under the License. * ==================================================================== */ -package org.jclouds.scriptbuilder.domain; +package org.jclouds.scriptbuilder.statements.ssh; import static org.testng.Assert.assertEquals; +import org.jclouds.scriptbuilder.domain.OsFamily; import org.testng.annotations.Test; /** @@ -28,16 +29,23 @@ import org.testng.annotations.Test; @Test(groups = "unit") public class InstallRSAPrivateKeyTest { - InstallRSAPrivateKey key = new InstallRSAPrivateKey("-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n"); - - public void testInstallRSAPrivateKeyUNIX() { + public void testInstallRSAPrivateKeyUNIXCurrentUser() { assertEquals( - key.render(OsFamily.UNIX), + new InstallRSAPrivateKey("-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n") + .render(OsFamily.UNIX), "mkdir -p ~/.ssh\nrm ~/.ssh/id_rsa\ncat >> ~/.ssh/id_rsa <<'END_OF_FILE'\n-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n\nEND_OF_FILE\nchmod 600 ~/.ssh/id_rsa\n"); } + public void testInstallRSAPrivateKeyUNIXSpecifiedHome() { + assertEquals( + new InstallRSAPrivateKey("/home/me/.ssh", "-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n") + .render(OsFamily.UNIX), + "mkdir -p /home/me/.ssh\nrm /home/me/.ssh/id_rsa\ncat >> /home/me/.ssh/id_rsa <<'END_OF_FILE'\n-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n\nEND_OF_FILE\nchmod 600 /home/me/.ssh/id_rsa\n"); + } + @Test(expectedExceptions = UnsupportedOperationException.class) public void testInstallRSAPrivateKeyWINDOWS() { - key.render(OsFamily.WINDOWS); + new InstallRSAPrivateKey("-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n") + .render(OsFamily.WINDOWS); } } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java new file mode 100644 index 0000000000..cc2c1976cc --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java @@ -0,0 +1,52 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.ssh; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.statements.ssh.SshStatements; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class SshStatementsTest { + + public void testLockSshdUNIX() { + assertEquals(SshStatements.lockSshd().render(OsFamily.UNIX), new StringBuilder().append( + "exec 3<> /etc/ssh/sshd_config && awk -v TEXT=\"")// + .append("PasswordAuthentication no").append("\n")// + .append("PermitRootLogin no").append("\n")// + .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")// + .append("/etc/init.d/sshd reload||/etc/init.d/ssh reload").append("\n").toString()); + } + + public void testSshdConfigUNIX() { + assertEquals(SshStatements.sshdConfig(ImmutableMap.of("AddressFamily", "inet6")).render(OsFamily.UNIX), + new StringBuilder().append("exec 3<> /etc/ssh/sshd_config && awk -v TEXT=\"")// + .append("AddressFamily inet6").append("\n")// + .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")// + .append("/etc/init.d/sshd reload||/etc/init.d/ssh reload").append("\n").toString()); + } + +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/Sha512CryptTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/Sha512CryptTest.java new file mode 100644 index 0000000000..1b7d52e4d2 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/Sha512CryptTest.java @@ -0,0 +1,78 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * 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.util; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.crypto.Crypto; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class Sha512CryptTest { + + protected Crypto crypto; + + @BeforeTest + protected void createCrypto() { + Injector i = Guice.createInjector(); + crypto = i.getInstance(Crypto.class); + } + + public final static Object[][] TEST_DATA = { + { "Hello world!", "$6$saltstring", + "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1" }, + { + "Hello world!", + "$6$rounds=10000$saltstringsaltstring", + "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." }, + { "This is just a test", "$6$rounds=5000$toolongsaltstring", + "$6$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" }, + { + "a very much longer text to encrypt. This one even stretches over morethan one line.", + "$6$rounds=1400$anotherlongsaltstring", + "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" }, + + { + "a short string", + "$6$rounds=123456$asaltof16chars..", + "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" }, + { "the minimum number is still observed", "$6$rounds=10$roundstoolow", + "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." } }; + + @DataProvider(name = "data") + public Object[][] createData1() { + return TEST_DATA; + } + + /** + * Validate our implementation using test data from Ulrich Drepper's C implementation. + */ + @Test(dataProvider = "data") + public void testMakeCryptedPasswordHash(String password, String salt, String expected) { + assertEquals(Sha512Crypt.makeShadowLine(password, salt, crypto), expected); + } +}