fireactions/rootfs/build.js

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);
}