Общая идея: используйте пользовательские данные экземпляра AWS, чтобы записать новый открытый ключ ssh-rsa в /root/.ssh/authorized_keys. Пакет cloud-init, установленный на экземпляре linux, должен поддерживать директиву bootcmd. У меня это работало с Ubuntu 16.04 и 18.04.
Пример пользовательских данных:
#cloud-config
bootcmd:
- echo 'ssh-rsa AAAAB3NzaC1... key-comment' > /root/.ssh/authorized_keys
Это можно сделать вручную, например, сгенерируйте новый ключ с помощью PuTTYgen и задайте пользовательские данные для экземпляра EC2 через консоль AWS.
Или автоматизировано, например, с Java, используя AWS EC2 Java SDK и Надувной замок :
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 + ")");
}
}
/** /2408562/kak-sozdat-ssh-sovmestimyi-idrsa-pub-iz-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: