1 Commits

Author SHA1 Message Date
3e78707d81 allow device matching by udev properties 2025-11-10 19:15:22 +01:00
9 changed files with 222 additions and 745 deletions

829
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "init"
version = "2.5.2"
version = "2.4.3"
edition = "2024"
[profile.release]

View File

@ -1,4 +1,4 @@
from rust:1.93.0-alpine as rust
from rust:1.91.0-alpine as rust
run apk add --no-cache git musl-dev libudev-zero-dev openssl-dev cryptsetup-dev lvm2-dev clang-libs clang-dev
@ -14,15 +14,13 @@ run apk add zstd lz4
workdir /system
env VERSION=3.23.2
run . /etc/os-release \
&& wget -O- https://dl-cdn.alpinelinux.org/alpine/v${VERSION%.*}/releases/x86_64/alpine-minirootfs-${VERSION}-x86_64.tar.gz |tar zxv
&& wget -O- https://dl-cdn.alpinelinux.org/alpine/v${VERSION_ID%.*}/releases/x86_64/alpine-minirootfs-${VERSION_ID}-x86_64.tar.gz |tar zxv
run apk add --no-cache --update -p . musl libgcc coreutils \
iproute2 lvm2 lvm2-extra lvm2-dmeventd udev cryptsetup \
lvm2 lvm2-extra lvm2-dmeventd udev cryptsetup \
e2fsprogs lsblk openssl openssh-server wireguard-tools-wg-quick \
&& rm -rf usr/share/apk var/cache/apk etc/motd dev/*
&& rm -rf usr/share/apk var/cache/apk etc/motd
copy etc/sshd_config etc/ssh/sshd_config
@ -36,6 +34,6 @@ run chroot . init-version
run find * |cpio -H newc -oF /initrd
# ------------------------------------------------------------------------
from alpine:3.23.0
from alpine:3.22.2
copy --from=initrd /initrd /
entrypoint ["base64","/initrd"]

View File

@ -1,4 +1,5 @@
pub mod bootstrap;
pub mod connect_boot;
pub mod init;
pub mod init_input;
pub mod version;

1
src/cmd/connect_boot.rs Normal file
View File

@ -0,0 +1 @@

View File

@ -1,7 +1,6 @@
use eyre::{format_err, Result};
use log::{error, info, warn};
use std::collections::BTreeSet as Set;
use std::convert::Infallible;
use std::os::unix::fs::symlink;
use tokio::sync::Mutex;
use tokio::{fs, process::Command};
@ -324,7 +323,13 @@ async fn child_reaper() {
}
}
async fn switch_root(root: &str) -> Result<Infallible> {
macro_rules! cstr {
($s:expr) => {
std::ffi::CString::new($s)?.as_c_str()
};
}
async fn switch_root(root: &str) -> Result<()> {
info!("killing all processes and switching root");
dklog::LOG.close().await;
@ -335,13 +340,7 @@ async fn switch_root(root: &str) -> Result<Infallible> {
eprintln!("failed to kill processes: {e}");
}
macro_rules! cstr {
($s:expr) => {
std::ffi::CString::new($s)?.as_c_str()
};
}
Ok(nix::unistd::execv(
nix::unistd::execv(
cstr!("/sbin/switch_root"),
&[
cstr!("switch_root"),
@ -350,5 +349,8 @@ async fn switch_root(root: &str) -> Result<Infallible> {
cstr!(root),
cstr!("/sbin/init"),
],
)?)
)
.unwrap();
unreachable!();
}

View File

@ -33,7 +33,6 @@ pub async fn bootstrap(cfg: Config) {
.await;
}
// prepare system
let boot_version = utils::param("version").unwrap_or("current");
let base_dir = &format!("/bootstrap/{boot_version}");

View File

@ -56,72 +56,27 @@ pub async fn setup(devs: &[CryptDev]) {
.await;
}
struct PrevPw {
pw: String,
reuse: bool,
}
impl PrevPw {
fn is_set(&self) -> bool {
!self.pw.is_empty()
}
fn can_reuse(&self) -> bool {
self.reuse && self.is_set()
}
fn invalidate(&mut self) {
self.pw = String::new();
self.reuse = false;
}
async fn input(&mut self, prompt: impl std::fmt::Display) -> String {
if self.can_reuse() {
info!("reusing password");
self.pw.clone()
} else if self.is_set() {
let pw =
input::read_password(format!("{prompt} (\"\" reuse, \"*\" auto-reuse)? ")).await;
match pw.as_str() {
"" => self.pw.clone(),
"*" => {
self.reuse = true;
self.pw.clone()
}
_ => {
self.pw = pw.clone();
pw
}
}
} else {
let pw = loop {
let pw = input::read_password(format!("{prompt}? ")).await;
if pw.is_empty() {
error!("empty password provided!");
continue;
}
break pw;
};
self.pw = pw.clone();
pw
}
}
}
static PREV_PW: Mutex<PrevPw> = Mutex::const_new(PrevPw {
pw: String::new(),
reuse: false,
});
static PREV_PW: Mutex<String> = Mutex::const_new(String::new());
async fn crypt_open(crypt_dev: &str, dev_path: &str) -> Result<()> {
'open_loop: loop {
let mut prev_pw = PREV_PW.lock().await;
let prompt = if prev_pw.is_empty() {
format!("crypt password for {crypt_dev}? ")
} else {
format!("crypt password for {crypt_dev} (enter = reuse previous)? ")
};
let pw = prev_pw
.input(format!("crypt password for {crypt_dev}"))
.await;
let mut pw = input::read_password(prompt).await;
if pw.is_empty() {
pw = prev_pw.clone();
}
if pw.is_empty() {
error!("empty password provided!");
continue;
}
*prev_pw = pw.clone();
if cryptsetup(&pw, ["open", dev_path, crypt_dev]).await? {
return Ok(());
@ -152,17 +107,15 @@ async fn crypt_open(crypt_dev: &str, dev_path: &str) -> Result<()> {
}
_ => unreachable!(),
}
}
} else {
// device looks initialized, don't allow format
warn!("{dev_path} looks initialized, formatting not allowed from init");
// device looks initialized, don't allow format
warn!("{dev_path} looks initialized, formatting not allowed from init");
prev_pw.invalidate();
match input::read_choice(["[r]etry", "[i]gnore"]).await {
'r' => continue 'open_loop,
'i' => return Ok(()),
_ => unreachable!(),
match input::read_choice(["[r]etry", "[i]gnore"]).await {
'r' => continue 'open_loop,
'i' => return Ok(()),
_ => unreachable!(),
}
}
}
}

View File

@ -2,10 +2,6 @@ use crate::input;
pub async fn run() {
tokio::spawn(async {
// give a bit of time for stdout
use tokio::time::{sleep, Duration};
sleep(Duration::from_millis(200)).await;
if let Err(e) = input::forward_requests_from_socket().await {
eprintln!("failed to forwards requests from socket: {e}");
std::process::exit(1);