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} */ 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} */ async function mkdirp(path) { await mkdir(path, { recursive: true }); } /** * @param {string} path */ function r(path) { return join(root, path); }