mkalpiimg/src/steps/init_os.rs
Dominic 95f3dc0351
Run apk inside docker
Co-authored-by: Glaeder <niklas@vousten.dev>
2023-04-21 15:20:45 +02:00

195 lines
5.7 KiB
Rust

use super::{download, run, run_chroot, tempfile, Image};
use crate::setup::Setup;
use anyhow::Context as _;
use std::{
fs::{self, File},
io::Write as _,
path::Path
};
pub fn init_os(setup: &Setup, img: &Image) -> anyhow::Result<()> {
eprintln!("Preparing operating system ...");
// write raspberry pi config
fs::write(img.boot.0.join("cmdline.txt"), &setup.os.rpi.cmdline)?;
let mut config = File::create(img.boot.0.join("config.txt"))?;
writeln!(
config,
r#"# Do not modify this file. Create and/or modify usercfg.txt instead.
[pi3]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi3+]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi4]
enable_gic=1
kernel=vmlinuz-rpi4
initramfs initramfs-rpi4
[all]"#
)?;
if setup.os.alpine.arch.is64bit() {
writeln!(config, "arm_64bit=1")?;
}
writeln!(config, "include usercfg.txt")?;
drop(config);
fs::write(img.boot.0.join("usercfg.txt"), &setup.os.rpi.usercfg)?;
// write initial apk config
let etc = img.root.0.join("etc");
let etc_apk = etc.join("apk");
let etc_apk_keys = etc_apk.join("keys");
fs::create_dir_all(&etc_apk_keys).context("Failed to create /etc/apk/keys directory")?;
let mut repositories = File::create(etc_apk.join("repositories"))?;
for repo in ["main", "community"] {
writeln!(repositories, "{}/{}/{repo}", setup.os.alpine.mirror, setup.os.alpine.branch)?;
}
for repo in &setup.os.alpine.extra_repos {
writeln!(repositories, "{repo}")?;
}
drop(repositories);
// download apk keys
let keys = tempfile()?;
download(
keys.path(),
&format!(
"https://gitlab.alpinelinux.org/alpine/aports/-/archive/{branch}/aports-{branch}.tar.bz2?path=main/alpine-keys",
branch = setup.os.alpine.branch.git_branch()
)
)?;
run(&[
"tar",
"xfj",
keys.path().to_str().unwrap(),
"-C",
etc_apk_keys.to_str().unwrap(),
"--strip-components=3",
"--wildcards",
"*/alpine-devel@lists.alpinelinux.org-*.rsa.pub"
])?;
drop(keys);
// copy additional keys
for key in &setup.os.alpine.extra_keys {
let key = Path::new(key);
let name = key.file_name().unwrap();
fs::copy(Path::new("setup").join(key), etc_apk_keys.join(name))?;
}
// bootstrap alpine
run(&[
"docker",
"run",
"--rm",
"-v",
&format!("{root}:{root}", root = &*img.root),
"alpine",
"apk",
"add",
"--root",
&img.root,
"--update-cache",
"--initdb",
"--arch",
setup.os.alpine.arch.to_str(),
"agetty",
"alpine-base",
"alpine-sdk",
"ca-certificates",
"elogind",
"eudev",
"haveged",
"linux-rpi",
"linux-rpi4",
"openssh",
"parted",
"raspberrypi-bootloader",
"sudo",
"tzdata",
"udev-init-scripts",
"util-linux-login",
"zram-init"
])?;
run(&[
"sed",
"-E",
"-e",
"s,getty(.*)$,agetty --noclear\\1 linux,",
"-i",
etc.join("inittab").to_str().unwrap()
])?;
for service in ["udev", "udev-postmount", "udev-settle", "udev-trigger"] {
run_chroot(img, "root", ["rc-update", "add", service, "sysinit"])?;
}
for service in ["modules", "syslog", "swclock"] {
run_chroot(img, "root", ["rc-update", "add", service, "boot"])?;
}
for service in ["elogind", "haveged", "ntpd", "sshd"] {
run_chroot(img, "root", ["rc-update", "add", service, "default"])?;
}
// configure fstab
fs::write(
etc.join("fstab"),
r#"# <device> <dir> <type> <options> <dump> <fsck>
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime 0 1
"#
)?;
// something is broken with abuild's functions.sh, so let's set CBUILD ourselves
let profile_d = etc.join("profile.d");
fs::create_dir_all(&profile_d)?;
fs::write(
profile_d.join("cbuild.sh"),
&format!("export CBUILD={}", setup.os.alpine.arch.cbuild())
)?;
// set up the locale
run_chroot(img, "root", ["setup-hostname", &setup.os.host.hostname])?;
run_chroot(img, "root", ["setup-timezone", "-z", &setup.os.host.timezone])?;
if let Some(variant) = setup.os.host.keymap_variant.as_deref() {
run_chroot(img, "root", ["setup-keymap", &setup.os.host.keymap, variant])?;
} else {
run_chroot(img, "root", ["setup-keymap", &setup.os.host.keymap])?;
}
// set up a user that we can use to install packages
run_chroot(img, "root", ["adduser", "-D", "mkalpiimg"])?;
run_chroot(img, "root", ["adduser", "mkalpiimg", "abuild"])?;
run_chroot(img, "root", "echo mkalpiimg:mkalpiimg | chpasswd")?;
fs::write(etc.join("sudoers.d").join("mkalpiimg"), "%abuild ALL=(ALL) NOPASSWD: ALL")?;
fs::create_dir_all(img.root.0.join("var").join("cache").join("distfiles"))?;
run_chroot(img, "root", "chgrp abuild /var/cache/distfiles; chmod 775 /var/cache/distfiles")?;
run_chroot(img, "mkalpiimg", ["abuild-keygen", "-a", "-n", "-b", "4096"])?;
run_chroot(img, "root", "cp /home/mkalpiimg/.abuild/*.rsa.pub /etc/apk/keys/")?;
// avoid a bug with cargo in qemu: https://github.com/rust-lang/cargo/issues/10583
let cargo = img.root.0.join("home").join("mkalpiimg").join(".cargo");
fs::create_dir_all(&cargo)?;
fs::write(cargo.join("config.toml"), "[net]\ngit-fetch-with-cli = true\n")?;
// set up the other users as requested
fs::write(etc.join("sudoers.d").join("wheel"), "%wheel ALL=(ALL:ALL) ALL")?;
for user in &setup.os.user {
run_chroot(img, "root", ["adduser", "-s", "/bin/ash", "-D", &user.name])?;
if user.sudo {
run_chroot(img, "root", ["adduser", &user.name, "wheel"])?;
}
for grp in &user.groups {
run_chroot(img, "root", ["adduser", &user.name, grp])?;
}
let ssh = img.root.0.join("home").join(&user.name).join(".ssh");
fs::create_dir_all(&ssh)?;
let mut keys = File::create(ssh.join("authorized_keys"))?;
for key in &user.authorized_keys {
writeln!(keys, "{key}")?;
}
drop(keys);
run_chroot(img, "root", ["chown", "-R", &user.name, &format!("/home/{}/.ssh", user.name)])?;
run_chroot(img, &user.name, "chmod 700 ~/.ssh; chmod 600 ~/.ssh/*")?;
}
Ok(())
}