Skip to content

Commit

Permalink
feat(vm): support use ssh key connect vm (#26)
Browse files Browse the repository at this point in the history
Signed-off-by: Black-Hole1 <[email protected]>
  • Loading branch information
BlackHole1 authored Dec 5, 2023
1 parent 9187f48 commit 1d35c24
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function async main() {
socketDir: "/example/socket",
logDir: "/example/log",
targetDir: "/example/target",
sshKeyDir: "/example/ssh",
versions: {
gvproxy: "1.0.0",
vfkit: "1.0.0",
Expand Down Expand Up @@ -84,6 +85,10 @@ The format of the log file name is as follows:

In order to address the issues that may occur when some files are damaged or other malfunctions happen, the program will first copy the files from the `originPath` to this directory. This allows for the restoration of files by calling `.resetPath()` when problems arise. For example, when forcibly shutting down the virtual machine (power off), certain service statuses in rootfs will be affected and cannot be restored. This mechanism is used to resolve this issue.

#### sshKeyDir

Store the SSH key pairs required to connect to the virtual machine.

#### versions

It is used to manage whether a file should be overwritten. For example, when upgrading `gvproxy`, it is necessary to overwrite the original `gvproxy` to ensure the upgrade takes effect.
Expand Down
18 changes: 17 additions & 1 deletion src/darwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
copy,
existsFile,
findUsablePort,
generateSSHKey,
isExecFile,
mkdir,
pRetry,
Expand Down Expand Up @@ -83,6 +84,8 @@ export class DarwinOVM {
private podmanPort: number;
private sshPort: number;

private publicKey: string;

private constructor(private options: OVMDarwinOptions) {}

public static async create(options: OVMDarwinOptions): Promise<DarwinOVM> {
Expand All @@ -91,6 +94,7 @@ export class DarwinOVM {
await ovm.initPath();
await ovm.initSocket();
await ovm.initLogs();
await ovm.initSSHKey();
await ovm.initPort();
await ovm.initDisks();
return ovm;
Expand Down Expand Up @@ -184,6 +188,17 @@ export class DarwinOVM {
this.sshPort = await findUsablePort(2223);
}

private async initSSHKey(): Promise<void> {
await mkdir(this.options.sshKeyDir, 0o700);
const privatePath = path.join(this.options.sshKeyDir, "ovm");

if (!await existsFile(privatePath)) {
await generateSSHKey(privatePath);
}

this.publicKey = await readFile(`${privatePath}.pub`);
}

private async initDisks(): Promise<[void, void]> {
return Promise.all([
this.createDisk("data", 8 * 1024 * 1024 * 1024 * 1024),
Expand Down Expand Up @@ -307,7 +322,8 @@ export class DarwinOVM {

private async ignition(): Promise<void> {
const mount = `echo -e ${Mount.toFSTAB().join("\\\\n")} >> /mnt/overlay/etc/fstab`;
const cmd = [mount].join(";");
const authorizedKeys = `mkdir -p /mnt/overlay/root/.ssh; echo ${this.publicKey} >> /mnt/overlay/root/.ssh/authorized_keys`;
const cmd = [mount, authorizedKeys].join(";");

return new Promise((resolve, reject) => {
const server = net.createServer((conn) => {
Expand Down
1 change: 1 addition & 0 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface OVMDarwinOptions {
targetDir: string;
socketDir: string;
logDir: string;
sshKeyDir: string;
versions: OVMDarwinOptions["originPath"]
}

Expand Down
17 changes: 16 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import net from "node:net";
import http from "node:http";
import unzipper from "unzipper";
import { constants, createReadStream } from "node:fs";
import { exec } from "node:child_process";
import { promisify } from "node:util";

export const isExecFile = (p: string): Promise<void> => {
return fs.access(p, constants.X_OK);
Expand Down Expand Up @@ -43,18 +45,25 @@ export const rename = async (oldPath: string, newName: string): Promise<void> =>
await fs.rename(oldPath, path.join(dir, newName));
};

export const mkdir = async (p: string): Promise<void> => {
export const mkdir = async (p: string, mode?: number): Promise<void> => {
try {
await assertExistsFile(p);
} catch (_error) {
await fs.mkdir(p, { recursive: true });
if (mode) {
await fs.chmod(p, mode);
}
return;
}

const stat = await fs.stat(p);
if (!stat.isDirectory()) {
throw new Error(`${p} is not a directory`);
}

if (mode && (stat.mode & 0o777) !== mode) {
await fs.chmod(p, mode);
}
};

export const readFile = async (p: string): Promise<string> => {
Expand Down Expand Up @@ -206,3 +215,9 @@ export const findUsablePort = async (startPort: number): Promise<number> => {

throw new Error(`no available port from ${startPort} to ${maxPort}, last error: ${lastError}`);
};

export const generateSSHKey = async(privatePath: string): Promise<void> => {
const p = promisify(exec);

await p(`ssh-keygen -t ed25519 -f ${path.join(privatePath)} -N ""`);
};

0 comments on commit 1d35c24

Please sign in to comment.