Java tutorial
/* * Copyright 2008-2010 Xebia and the original author or authors. * * 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 fr.xebia.demo.amazon.aws; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigInteger; import java.net.URL; import java.nio.ByteBuffer; import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.Security; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.List; import java.util.Properties; import java.util.Set; import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.security.auth.x500.X500Principal; import org.apache.commons.lang.RandomStringUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.x509.X509V1CertificateGenerator; import org.jclouds.crypto.Pems; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.CreateKeyPairRequest; import com.amazonaws.services.ec2.model.CreateKeyPairResult; import com.amazonaws.services.ec2.model.DeleteKeyPairRequest; import com.amazonaws.services.ec2.model.DescribeKeyPairsRequest; import com.amazonaws.services.ec2.model.DescribeKeyPairsResult; import com.amazonaws.services.ec2.model.KeyPair; import com.amazonaws.services.identitymanagement.AmazonIdentityManagement; import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; import com.amazonaws.services.identitymanagement.model.AccessKey; import com.amazonaws.services.identitymanagement.model.AddUserToGroupRequest; import com.amazonaws.services.identitymanagement.model.CreateAccessKeyRequest; import com.amazonaws.services.identitymanagement.model.CreateAccessKeyResult; import com.amazonaws.services.identitymanagement.model.CreateLoginProfileRequest; import com.amazonaws.services.identitymanagement.model.CreateUserRequest; import com.amazonaws.services.identitymanagement.model.CreateUserResult; import com.amazonaws.services.identitymanagement.model.SigningCertificate; import com.amazonaws.services.identitymanagement.model.UploadSigningCertificateRequest; import com.amazonaws.services.identitymanagement.model.UploadSigningCertificateResult; import com.amazonaws.services.identitymanagement.model.User; import com.amazonaws.services.simpleemail.AmazonSimpleEmailService; import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient; import com.amazonaws.services.simpleemail.model.Body; import com.amazonaws.services.simpleemail.model.Content; import com.amazonaws.services.simpleemail.model.Destination; import com.amazonaws.services.simpleemail.model.Message; import com.amazonaws.services.simpleemail.model.RawMessage; import com.amazonaws.services.simpleemail.model.SendEmailRequest; import com.amazonaws.services.simpleemail.model.SendEmailResult; import com.amazonaws.services.simpleemail.model.SendRawEmailRequest; import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.io.Resources; /** * Create Amazon IAM accounts. * * @author <a href="mailto:cyrille@cyrilleleclerc.com">Cyrille Le Clerc</a> */ @SuppressWarnings("deprecation") public class AmazonAwsIamAccountCreatorV2 { static { // adds the Bouncy castle provider to java security Security.addProvider(new BouncyCastleProvider()); } public static void main(String[] args) throws Exception { try { AmazonAwsIamAccountCreatorV2 amazonAwsIamAccountCreator = new AmazonAwsIamAccountCreatorV2(); amazonAwsIamAccountCreator.createUsers(); } catch (Exception e) { e.printStackTrace(); } } protected final KeyPairGenerator keyPairGenerator; protected final AmazonEC2 ec2; protected final AmazonIdentityManagement iam; protected final Logger logger = LoggerFactory.getLogger(getClass()); protected final AmazonSimpleEmailService ses; public AmazonAwsIamAccountCreatorV2() { try { InputStream credentialsAsStream = Thread.currentThread().getContextClassLoader() .getResourceAsStream("AwsCredentials.properties"); Preconditions.checkNotNull(credentialsAsStream, "File 'AwsCredentials.properties' NOT found in the classpath"); AWSCredentials awsCredentials = new PropertiesCredentials(credentialsAsStream); iam = new AmazonIdentityManagementClient(awsCredentials); ses = new AmazonSimpleEmailServiceClient(awsCredentials); ec2 = new AmazonEC2Client(awsCredentials); ec2.setEndpoint("ec2.eu-west-1.amazonaws.com"); keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC"); keyPairGenerator.initialize(1024, new SecureRandom()); } catch (Exception e) { throw Throwables.propagate(e); } } /** * Builds difference between list of emails provided in * "accounts-to-create.txt" and the already created users (obtained via * {@link AmazonIdentityManagement#listUsers()}). */ public Set<String> buildUserNamesToCreate() { List<String> existingUserNames = Lists.transform(iam.listUsers().getUsers(), new Function<User, String>() { @Override public String apply(User user) { return user.getUserName(); } }); URL emailsToVerifyURL = Thread.currentThread().getContextClassLoader() .getResource("accounts-to-create.txt"); Preconditions.checkNotNull(emailsToVerifyURL, "File 'accounts-to-create.txt' NOT found in the classpath"); List<String> userNamesToCreate; try { userNamesToCreate = Resources.readLines(emailsToVerifyURL, Charsets.ISO_8859_1); } catch (IOException e) { throw Throwables.propagate(e); } return Sets.difference(Sets.newHashSet(userNamesToCreate), Sets.newHashSet(existingUserNames)); } public void createUsers() { Set<String> userNames = buildUserNamesToCreate(); System.out.println("Create accounts for: " + userNames); for (String userName : userNames) { createUsers(userName); // sleep 10 seconds to prevent "Throttling exception" try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { throw Throwables.propagate(e); } } } public java.security.KeyPair createRsaKeyPair() { return keyPairGenerator.generateKeyPair(); } public X509Certificate createX509Certificate(String userName, java.security.KeyPair keyPair) { try { DateTime startDate = new DateTime().minusDays(1); DateTime expiryDate = new DateTime().plusYears(2); X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); X500Principal dnName = new X500Principal("CN=" + userName); certGen.setSubjectDN(dnName); // same as subject : self signed certificate certGen.setIssuerDN(dnName); certGen.setNotBefore(startDate.toDate()); certGen.setNotAfter(expiryDate.toDate()); certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); certGen.setPublicKey(keyPair.getPublic()); certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); return certGen.generate(keyPair.getPrivate(), "BC"); } catch (Exception e) { throw Throwables.propagate(e); } } /** * Create an Amazon IAM account with a password, a secret key and member of * "Admins". The password, access key and secret key are sent by email. * * @param userName * valid email used as userName of the created account. */ public void createUsers(String userName) { CreateUserRequest createUserRequest = new CreateUserRequest(userName); CreateUserResult createUserResult = iam.createUser(createUserRequest); User user = createUserResult.getUser(); String password = RandomStringUtils.randomAlphanumeric(8); iam.createLoginProfile(new CreateLoginProfileRequest(user.getUserName(), password)); iam.addUserToGroup(new AddUserToGroupRequest("Admins", user.getUserName())); CreateAccessKeyResult createAccessKeyResult = iam .createAccessKey(new CreateAccessKeyRequest().withUserName(user.getUserName())); AccessKey accessKey = createAccessKeyResult.getAccessKey(); // SSH KeyPair sshKeyPair = createOrOverWriteSshKeyPair(userName); // X509 java.security.KeyPair x509KeyPair = createRsaKeyPair(); X509Certificate x509Certificate = createX509Certificate(userName, x509KeyPair); SigningCertificate signingCertificate; try { UploadSigningCertificateResult uploadSigningCertificateResult = iam .uploadSigningCertificate(new UploadSigningCertificateRequest(Pems.pem(x509Certificate)) .withUserName(user.getUserName())); signingCertificate = uploadSigningCertificateResult.getCertificate(); } catch (CertificateEncodingException e) { throw Throwables.propagate(e); } System.out.println("CREATED userName=" + user.getUserName() + "\tpassword=" + password + "\taccessKeyId=" + accessKey.getAccessKeyId() + "\tsecretAccessKey=" + accessKey.getSecretAccessKey() + "\tsshKeyPair=" + sshKeyPair.getKeyName() + "\tx509Certificate=" + signingCertificate.getCertificateId()); String subject = "Xebia France Amazon EC2 Credentials"; String body = "Hello,\n"; body += "\n"; body += "Here are the credentials to connect to Xebia Amazon AWS/EC2 training infrastructure:\n"; body += "\n"; body += "User Name: " + user.getUserName() + "\n"; body += "Password: " + password + "\n"; body += "\n"; body += "Access Key Id: " + accessKey.getAccessKeyId() + "\n"; body += "Secret Access Key: " + accessKey.getSecretAccessKey() + "\n"; body += "\n"; body += "SSH private key pair '" + sshKeyPair.getKeyName() + "' attached, rename it as '" + sshKeyPair.getKeyName() + ".pem" + "'n"; body += "\n"; body += "The authentication page is https://xebia-france.signin.aws.amazon.com/console"; body += "\n"; body += "Don't hesitate to connect to Amazon AWS, to play with it but please DO NOT FORGET TO STOP INSTANCES OR IF POSSIBLE TERMINATE THEM AFTER USING THEM.\n"; body += "Letting instances started would cost unnecessary money to Xebia.\n"; body += "\n"; body += "\n"; body += "Thanks,\n"; body += "\n"; body += "Cyrille"; try { sendEmail(subject, body, accessKey, sshKeyPair, x509KeyPair, x509Certificate, signingCertificate, "cyrille@cyrilleleclerc.com", user.getUserName()); } catch (Exception e) { e.printStackTrace(); } } KeyPair createOrOverWriteSshKeyPair(String userName) { String keyPairName; if (userName.endsWith("@xebia.fr") || userName.endsWith("@xebia.com")) { keyPairName = userName.substring(0, userName.indexOf("@xebia.")); } else { keyPairName = userName.replace("@", "_at_").replace(".", "_dot_").replace("+", "_plus_"); } try { DescribeKeyPairsResult describeKeyPairsResult = ec2 .describeKeyPairs(new DescribeKeyPairsRequest().withKeyNames(keyPairName)); if (!describeKeyPairsResult.getKeyPairs().isEmpty()) { // unexpected, should be an "InvalidKeyPair.NotFound" // AmazonServiceException ec2.deleteKeyPair(new DeleteKeyPairRequest(keyPairName)); } } catch (AmazonServiceException e) { if ("InvalidKeyPair.NotFound".equals(e.getErrorCode())) { // key does not exist } else { throw e; } } CreateKeyPairResult createKeyPairResult = ec2.createKeyPair(new CreateKeyPairRequest(keyPairName)); KeyPair keyPair = createKeyPairResult.getKeyPair(); return keyPair; } /** * Send email with Amazon Simple Email Service. * <p/> * * Please note that the sender (ie 'from') must be a verified address (see * {@link AmazonSimpleEmailService#verifyEmailAddress(com.amazonaws.services.simpleemail.model.VerifyEmailAddressRequest)} * ). * <p/> * * Please note that the sender is a CC of the meail to ease support. * <p/> * * @param subject * @param body * @param from * @param toAddresses */ public void sendEmail(String subject, String body, String from, String... toAddresses) { SendEmailRequest sendEmailRequest = new SendEmailRequest( // from, // new Destination().withToAddresses(toAddresses).withCcAddresses(from), // new Message(new Content(subject), // new Body(new Content(body)))); SendEmailResult sendEmailResult = ses.sendEmail(sendEmailRequest); System.out.println(sendEmailResult); } /** * Send email with Amazon Simple Email Service. * <p/> * * Please note that the sender (ie 'from') must be a verified address (see * {@link AmazonSimpleEmailService#verifyEmailAddress(com.amazonaws.services.simpleemail.model.VerifyEmailAddressRequest)} * ). * <p/> * * Please note that the sender is a CC of the meail to ease support. * <p/> * * @param subject * @param body * @param from * @param toAddresses * @throws MessagingException * @throws AddressException */ public void sendEmail(String subject, String body, AccessKey accessKey, KeyPair sshKeyPair, java.security.KeyPair x509KeyPair, X509Certificate x509Certificate, SigningCertificate signingCertificate, String from, String toAddress) { try { Session s = Session.getInstance(new Properties(), null); MimeMessage msg = new MimeMessage(s); msg.setFrom(new InternetAddress(from)); msg.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(toAddress)); msg.addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(from)); msg.setSubject(subject); MimeMultipart mimeMultiPart = new MimeMultipart(); msg.setContent(mimeMultiPart); // body BodyPart plainTextBodyPart = new MimeBodyPart(); mimeMultiPart.addBodyPart(plainTextBodyPart); plainTextBodyPart.setContent(body, "text/plain"); // aws-credentials.txt / accessKey { BodyPart awsCredentialsBodyPart = new MimeBodyPart(); awsCredentialsBodyPart.setFileName("aws-credentials.txt"); StringWriter awsCredentialsStringWriter = new StringWriter(); PrintWriter awsCredentials = new PrintWriter(awsCredentialsStringWriter); awsCredentials .println("#Insert your AWS Credentials from http://aws.amazon.com/security-credentials"); awsCredentials.println("#" + new DateTime()); awsCredentials.println(); awsCredentials.println("# ec2, rds & elb tools use accessKey and secretKey"); awsCredentials.println("accessKey=" + accessKey.getAccessKeyId()); awsCredentials.println("secretKey=" + accessKey.getSecretAccessKey()); awsCredentials.println(); awsCredentials.println("# iam tools use AWSAccessKeyId and AWSSecretKey"); awsCredentials.println("AWSAccessKeyId=" + accessKey.getAccessKeyId()); awsCredentials.println("AWSSecretKey=" + accessKey.getSecretAccessKey()); awsCredentialsBodyPart.setContent(awsCredentialsStringWriter.toString(), "text/plain"); mimeMultiPart.addBodyPart(awsCredentialsBodyPart); } // private ssh key { BodyPart keyPairBodyPart = new MimeBodyPart(); keyPairBodyPart.setFileName(sshKeyPair.getKeyName() + ".pem.txt"); keyPairBodyPart.setContent(sshKeyPair.getKeyMaterial(), "application/octet-stream"); mimeMultiPart.addBodyPart(keyPairBodyPart); } // x509 private key { BodyPart x509PrivateKeyBodyPart = new MimeBodyPart(); x509PrivateKeyBodyPart.setFileName("pk-" + signingCertificate.getCertificateId() + ".pem.txt"); String x509privateKeyPem = Pems.pem(x509KeyPair.getPrivate()); x509PrivateKeyBodyPart.setContent(x509privateKeyPem, "application/octet-stream"); mimeMultiPart.addBodyPart(x509PrivateKeyBodyPart); } // x509 private key { BodyPart x509CertificateBodyPart = new MimeBodyPart(); x509CertificateBodyPart.setFileName("cert-" + signingCertificate.getCertificateId() + ".pem.txt"); String x509CertificatePem = Pems.pem(x509Certificate); x509CertificateBodyPart.setContent(x509CertificatePem, "application/octet-stream"); mimeMultiPart.addBodyPart(x509CertificateBodyPart); } // Convert to raw message ByteArrayOutputStream out = new ByteArrayOutputStream(); msg.writeTo(out); RawMessage rawMessage = new RawMessage(); rawMessage.setData(ByteBuffer.wrap(out.toString().getBytes())); ses.sendRawEmail(new SendRawEmailRequest().withRawMessage(rawMessage)); } catch (Exception e) { throw Throwables.propagate(e); } } }