丢失密钥对后访问EC2实例

时间:2011-10-31 03:48:23

标签: amazon-ec2

我几个月前启动了一个EC2实例,当时我的.pem密钥存储在我的电脑上。 PC崩溃了,我不得不重新安装Windows,我没有.pem文件的备份

对于我来说,有什么方法可以获得EC2实例的提示 - 无论是ssh还是以其他方式访问实例?

4 个答案:

答案 0 :(得分:8)

不容易。

论坛指南表明,您可以通过生成新密钥对然后调出新实例并从原始实例安装卷并在该卷中安装密钥对来实现。之后,原始实例应该能够使用新的密钥对。

根据this post on AWS Developer Forums,可以通过以下方式完成:

  

创建新密钥对会将私钥下载到您的计算机,公钥将存储在您的AWS账户中。当您启动一个新的(linux)实例时,公钥被放入/root/.ssh/authorized_keys文件(或/home/ubuntu/.ssh/authorized_keys用于Canonical Ubuntu AMI),允许您的私钥访问该实例。

     

如果已经设置了私钥,可以通过另一个Linux帐户登录实例来修复丢失私钥。

     

或者,如果您正在运行EBS支持的实例,则可以将其停止,将根EBS卷附加到另一个实例,并将新公钥放入authorized_keys文件,然后将卷返回到原始卷(已停止) )实例并再次启动实例。

     

如果这些都不起作用,那么 - 抱歉 - 你运气不好。它被称为一把钥匙,因为没有它你就无法进入。

答案 1 :(得分:3)

您必须创建当前实例的图像,然后使用该图像启动新实例。所有文件和数据都将从该实例复制,并在启动实例时使用不同的密钥,然后使用相同的密钥

访问它

答案 2 :(得分:2)

当我们松开私钥时,您无法登录该计算机。但是,还有另一种通过生成新密钥对来访问该计算机的方法

请按照以下步骤恢复密钥。

步骤1)使用AWS控制台从计算机中分离根卷 步骤2)启动一个新的EC2实例(不是来自您的旧机器AMI)
步骤3)将旧卷连接到新的EC2机器上 步骤4)现在登录新的ec2机器并安装旧的EBS卷
步骤5)现在转到该分区然后访问该机器内的主目录并转到.ssh文件夹 步骤6)现在生成一个新的私钥和公钥。然后将公钥粘贴到authorized_keys文件中 步骤7)完成上述步骤后,从该ec2机器上拆下该卷 步骤8)现在将此卷作为根卷附加到旧机器上 步骤9)现在尝试使用新生成的密钥登录旧计算机。

希望有所帮助!!

答案 3 :(得分:0)

一般思想:使用AWS实例用户数据将新的ssh-rsa公钥写入/root/.ssh/authorized_keys。安装在Linux实例上的cloud-init软件包需要支持bootcmd指令。它适用于Ubuntu 16.04和18.04。

用户数据示例:

#cloud-config
bootcmd:
 - echo 'ssh-rsa AAAAB3NzaC1... key-comment' > /root/.ssh/authorized_keys

这可以手动完成,例如使用PuTTYgen生成一个新密钥,并通过AWS控制台在EC2实例上设置用户数据。

或自动执行,例如Java,并使用AWS EC2 Java SDKBouncy Castle

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPublicKey;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.InstanceStateName;
import com.amazonaws.services.ec2.model.ModifyInstanceAttributeRequest;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StopInstancesRequest;

public class RecoverAwsEc2RootSshAccess {
  public static void main(String[] args) throws Exception {
    // use the AWS console to create an AWS IAM user with ec2 permissions for your region, and generate security credentials
    String awsAccessKey = "...";
    String awsSecretKey = "...";
    Regions region = Regions.US_EAST_1;
    String instanceId = "i-...";

    File pemKeyFile = new File("private.key.pem");
    String keyComment = "key-generated-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());

    KeyPair keyPair;
    if(pemKeyFile.exists()) {
      System.out.println("reusing existing RSA private key: " + pemKeyFile.getAbsolutePath());
      try(PEMParser pemParser = new PEMParser(new FileReader(pemKeyFile))) {
        keyPair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) pemParser.readObject());
      }
    }
    else {
      System.out.println("generating new RSA private key: " + pemKeyFile.getAbsolutePath());
      KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
      keyGen.initialize(2048);
      keyPair = keyGen.generateKeyPair();
      try(JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter(pemKeyFile))) {
        pemWriter.writeObject(keyPair.getPrivate());
      }
    }

    String authorized_keys = to_authorized_keys_line(keyPair, keyComment);
    String userdata = "#cloud-config";
    userdata += "\n" + "bootcmd:";
    userdata += "\n" + " - echo '" + authorized_keys + "' > /root/.ssh/authorized_keys";
    String userdataBase64 = Base64.getEncoder().encodeToString(userdata.getBytes(StandardCharsets.UTF_8));
    System.out.println("AWS instance user-data (can also be set manually via the AWS console):");
    System.out.println(userdata);

    AmazonEC2 ec2 = AmazonEC2ClientBuilder.standard().withRegion(region) //
        .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey))).build();

    // stop, set userdata, start
    ec2.stopInstances(new StopInstancesRequest().withInstanceIds(instanceId));
    waitForInstanceState(ec2, instanceId, InstanceStateName.Stopped);
    ec2.modifyInstanceAttribute(new ModifyInstanceAttributeRequest().withInstanceId(instanceId).withUserData(userdataBase64));
    ec2.startInstances(new StartInstancesRequest().withInstanceIds(instanceId));
    waitForInstanceState(ec2, instanceId, InstanceStateName.Running);

    // optional: stop, clear userdata, start

    System.out.println("new IP: " + ec2.describeInstances(new DescribeInstancesRequest().withInstanceIds(instanceId)).getReservations()
        .get(0).getInstances().get(0).getPublicIpAddress());

    // next step: automate DNS update
  }

  private static void waitForInstanceState(AmazonEC2 ec2, String instanceId, InstanceStateName desiredState) throws Exception {
    String currentState = "?";
    while(!currentState.equals(desiredState.toString())) {
      Thread.sleep(3_000);
      currentState = ec2.describeInstances(new DescribeInstancesRequest().withInstanceIds(instanceId)).getReservations().get(0)
          .getInstances().get(0).getState().getName();
      System.out.println("instance state: " + currentState + " (waiting for " + desiredState + ")");
    }
  }

  /** https://stackoverflow.com/questions/3706177/how-to-generate-ssh-compatible-id-rsa-pub-from-java */
  private static String to_authorized_keys_line(KeyPair key, String keyComment) throws IOException {
    byte[] exponent = ((RSAPublicKey) key.getPublic()).getPublicExponent().toByteArray();
    byte[] modulus = ((RSAPublicKey) key.getPublic()).getModulus().toByteArray();
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write(new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' });
    out.write(toUInt32(exponent.length));
    out.write(exponent);
    out.write(toUInt32(modulus.length));
    out.write(modulus);
    return "ssh-rsa " + Base64.getEncoder().encodeToString(out.toByteArray()) + " " + keyComment;
  }

  private static byte[] toUInt32(int value) {
    return new byte[] { (byte) (value >>> 24 & 0xff), (byte) (value >>> 16 & 0xff), (byte) (value >>> 8 & 0xff), (byte) (value & 0xff) };
  }
}

最后测试连接,例如使用FileZilla:

enter image description here