fix: use CreateKeyPair instead of ImportKeyPair for simpler SSH key management

AWS CreateKeyPair generates the RSA key pair server-side and returns the private key,
avoiding the need to manually format RSA public keys in OpenSSH wire format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 00:23:59 +02:00
parent b975f8e9b9
commit 4a1dc78714
2 changed files with 8 additions and 34 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ import { getAdminSettings } from "../lib/adminSettings";
import { env } from "../lib/env";
import {
makeEc2Client,
generateAndImportKeyPair,
createKeyPairAndGetPrivateKey,
createPreviewSecurityGroup,
launchInstance,
waitForInstanceRunning,
@@ -200,7 +200,7 @@ async function firstDeploy(
checkAbort(previewId);
const keyName = `pp-preview-${previewId}`;
const { privateKey } = await generateAndImportKeyPair(ec2, keyName);
const { privateKey } = await createKeyPairAndGetPrivateKey(ec2, keyName);
const encPrivateKey = encrypt(privateKey);
await prisma.preview.update({ where: { id: previewId }, data: { sshPrivateKey: encPrivateKey, sshKeyName: keyName } });
+6 -32
View File
@@ -7,12 +7,11 @@ import {
DeleteSecurityGroupCommand,
AuthorizeSecurityGroupIngressCommand,
DescribeSecurityGroupsCommand,
ImportKeyPairCommand,
CreateKeyPairCommand,
DeleteKeyPairCommand,
CreateTagsCommand,
} from "@aws-sdk/client-ec2";
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
import { generateKeyPairSync, createPublicKey } from "crypto";
import { createLogger } from "../lib/logger";
import { decrypt } from "../lib/encryption";
import type { User } from "@prisma/client";
@@ -68,38 +67,13 @@ export async function validateAwsCredentials(user: User): Promise<{ success: boo
}
}
export function generateSshKeyPair(): { privateKey: string; publicKeyOpenssh: string } {
const { privateKey: privKeyPem, publicKey: pubKeyPem } = generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs1", format: "pem" },
});
const pubKeyObj = createPublicKey(pubKeyPem);
const pubKeyDer = pubKeyObj.export({ type: "spki", format: "der" });
function sshEncodeBuffer(buf: Buffer): Buffer {
const lenBuf = Buffer.allocUnsafe(4);
lenBuf.writeUInt32BE(buf.length, 0);
return Buffer.concat([lenBuf, buf]);
}
const keyTypeStr = Buffer.from("ssh-rsa");
const keyTypeEncoded = sshEncodeBuffer(keyTypeStr);
const base64Encoded = pubKeyDer.toString("base64");
const openSshKey = `ssh-rsa ${Buffer.concat([keyTypeEncoded]).toString("base64")} pp-generated`;
return { privateKey: privKeyPem, publicKeyOpenssh: `ssh-rsa ${base64Encoded} pp-generated` };
}
export async function generateAndImportKeyPair(ec2: EC2Client, keyName: string): Promise<{ privateKey: string }> {
const { privateKey, publicKeyOpenssh } = generateSshKeyPair();
await ec2.send(new ImportKeyPairCommand({
export async function createKeyPairAndGetPrivateKey(ec2: EC2Client, keyName: string): Promise<{ privateKey: string }> {
const res = await ec2.send(new CreateKeyPairCommand({
KeyName: keyName,
PublicKeyMaterial: Buffer.from(publicKeyOpenssh),
KeyType: "rsa",
KeyFormat: "pem",
}));
return { privateKey };
return { privateKey: res.KeyMaterial! };
}
export async function createPreviewSecurityGroup(ec2: EC2Client, groupName: string, port: number): Promise<string> {