132 lines
3.2 KiB
JavaScript
132 lines
3.2 KiB
JavaScript
import { init } from "@nulo/apkit";
|
|
import { execFile as _execFile } from "node:child_process";
|
|
import {
|
|
chmod,
|
|
mkdir,
|
|
mkdtemp,
|
|
readFile,
|
|
readdir,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
const execFile = promisify(_execFile);
|
|
|
|
const root = await mkdtemp(join(tmpdir(), "fireactions-rootfs."));
|
|
const alpine = await init(root);
|
|
await alpine.install(["dropbear", "util-linux", "dropbear-dbclient", "dhcpcd"]);
|
|
|
|
// gotta go fast
|
|
await append(r("/etc/rc.conf"), 'rc_parallel="YES"');
|
|
|
|
await mkdirp(r("/usr/local/sbin"));
|
|
console.debug(
|
|
await execFile("go", [
|
|
"build",
|
|
"-tags=netgo",
|
|
"-o",
|
|
r("/usr/local/sbin/fireactions-agent"),
|
|
"./agent",
|
|
])
|
|
);
|
|
// https://github.com/OpenRC/openrc/blob/master/service-script-guide.md
|
|
await writeFile(
|
|
r("/etc/init.d/fireactions-agent"),
|
|
`#!/sbin/openrc-run
|
|
pidfile="/run/\${RC_SVCNAME}.pid"
|
|
command_background=true
|
|
command=/usr/local/sbin/fireactions-agent
|
|
output_log=/dev/stdout
|
|
error_log=/dev/stdout
|
|
`
|
|
);
|
|
await chmod(r("/etc/init.d/fireactions-agent"), 0o700);
|
|
await alpine.rcUpdate("default", "fireactions-agent");
|
|
|
|
await alpine.rcUpdate("boot", "devfs");
|
|
await alpine.rcUpdate("boot", "procfs");
|
|
await alpine.rcUpdate("boot", "sysfs");
|
|
// alpine.rcUpdate('default', 'dhcpcd')
|
|
await alpine.rcUpdate("default", "dropbear");
|
|
|
|
await writeFile(r("/etc/securetty"), "ttyS0");
|
|
await symlink("agetty", r("/etc/init.d/agetty.ttyS0"));
|
|
await alpine.rcUpdate("default", "agetty.ttyS0");
|
|
|
|
await mkdirp(r("/etc/dropbear"));
|
|
for (const t of ["rsa", "dss", "ed25519", "ecdsa"]) {
|
|
await execFile("dropbearkey", [
|
|
"-t",
|
|
t,
|
|
"-f",
|
|
r(`/etc/dropbear/dropbear_${t}_host_key`),
|
|
]);
|
|
}
|
|
|
|
await execFile("sudo", ["mkdir", "-p", r("/root/.ssh")]);
|
|
await writeFile(
|
|
"authorized_keys",
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEhrcTPMrhrOdwpgFkRFjTt8rdxt3gg16LJvBCZENRWa user@personal"
|
|
);
|
|
await execFile("sudo", [
|
|
"mv",
|
|
"authorized_keys",
|
|
r("/root/.ssh/authorized_keys"),
|
|
]);
|
|
|
|
await alpine.rcUpdate("default", "networking");
|
|
await writeFile(
|
|
r("/etc/network/interfaces"),
|
|
`auto lo
|
|
iface lo inet loopback`
|
|
);
|
|
|
|
const ext4 = "rootfs.ext4";
|
|
await rm(ext4);
|
|
await execFile("fallocate", ["--length", "1G", ext4]);
|
|
await execFile("mkfs.ext4", [ext4]);
|
|
|
|
const mountpoint = await mkdtemp(join(tmpdir(), "tmp.fireactions-mountpoint."));
|
|
try {
|
|
await mkdirp(mountpoint);
|
|
await execFile("sudo", ["mount", ext4, mountpoint]);
|
|
try {
|
|
for (const dir of await readdir(root)) {
|
|
await execFile("sudo", ["cp", "-r", r(dir), mountpoint]);
|
|
}
|
|
} finally {
|
|
await execFile("sudo", ["umount", mountpoint]);
|
|
}
|
|
} finally {
|
|
await rm(mountpoint, { recursive: true });
|
|
}
|
|
|
|
// # sudo rm -rf "$dir"
|
|
|
|
// helpers
|
|
/**
|
|
* @param {import("node:fs").PathLike} path
|
|
* @param {string} content
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function append(path, content) {
|
|
const file = await readFile(path, "utf-8");
|
|
await writeFile(path, file + "\n" + content);
|
|
}
|
|
/**
|
|
* @param {import("node:fs").PathLike} path
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function mkdirp(path) {
|
|
await mkdir(path, { recursive: true });
|
|
}
|
|
/**
|
|
* @param {string} path
|
|
*/
|
|
function r(path) {
|
|
return join(root, path);
|
|
}
|