Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee03452591 | |||
| 7fef9eaf6c | |||
| 6a9875fad5 | |||
| 6a8805346f | |||
| acb8f28ab7 | |||
| d4ec6380f8 | |||
| 3036b2f417 | |||
| e72e6a0b3b | |||
| 2924263cb6 | |||
| 5ce212b3ef | |||
| ea49b99dbe | |||
| c5f3f220c9 | |||
| fec3cfcb57 | |||
| c8437c655c | |||
| fe3752baf9 | |||
| f29dc650b4 | |||
| 5ebf1331bb | |||
| be0e10723a | |||
| 01b211d0c5 | |||
| 0ef1ae769e | |||
| afac751118 |
Generated
+182
-667
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "init"
|
||||
version = "2.6.0"
|
||||
version = "2.6.9"
|
||||
edition = "2024"
|
||||
|
||||
[profile.release]
|
||||
@@ -28,6 +28,8 @@ unix_mode = "0.1.4"
|
||||
sys-info = "0.9.1"
|
||||
dkl = { git = "https://novit.tech/direktil/dkl", version = "1.0.0" }
|
||||
openssl = "0.10.73"
|
||||
reqwest = { version = "0.13.1", features = ["native-tls"] }
|
||||
#reqwest = { version = "0.13.1", features = ["native-tls", "system-proxy"], default-features = false }
|
||||
reqwest = { git = "https://github.com/mcluseau/rs-reqwest", version = "0.13.1", features = ["native-tls", "system-proxy", "socks"], default-features = false }
|
||||
glob = "0.3.3"
|
||||
hex = "0.4.3"
|
||||
tar = { version = "0.4.46", default-features = false }
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
from rust:1.95.0-alpine as rust
|
||||
from rust:1.96.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
|
||||
|
||||
@@ -17,7 +17,7 @@ run . /etc/os-release \
|
||||
&& 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 \
|
||||
ethtool iproute2 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/*
|
||||
|
||||
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#! /bin/bash
|
||||
|
||||
base_initrd=dist/initrd
|
||||
flavor=dhcp
|
||||
dir=tmp/$flavor-initrd
|
||||
|
||||
dist=dist/$flavor
|
||||
uki=$dist/bootx64.efi
|
||||
|
||||
linux_v=6.18.34
|
||||
|
||||
set -ex
|
||||
|
||||
mkdir -p tmp/dl $dist
|
||||
|
||||
linux=tmp/dl/linux-$linux_v
|
||||
modules=tmp/dl/modules-$linux_v
|
||||
|
||||
[ -e $linux ] || curl -o $linux https://dkl.novit.io/dist/kernels/6.18.34
|
||||
[ -e $modules ] || curl -o $modules https://dkl.novit.io/dist/layers/modules/6.18.34 # TODO .erofs
|
||||
|
||||
rm -fr $dir
|
||||
mkdir $dir
|
||||
|
||||
cpio --quiet --extract --file $base_initrd --directory $dir
|
||||
|
||||
cp -rv $flavor/. $dir
|
||||
cp $modules $dir/modules.fs
|
||||
|
||||
(cd $dir && find * |cpio --create -H newc -R 0:0) >$dir.cpio
|
||||
|
||||
if cpio -tF $dir.cpio 2>&1 |grep bytes.of.junk; then echo "bad cpio archive"; exit 1; fi
|
||||
|
||||
zstd -12 -T0 -vf $dir.cpio &&
|
||||
mv $dir.cpio.zst $dist/initrd
|
||||
cp $linux $dist/vmlinuz
|
||||
|
||||
ukify build --output $uki --os-release "Direktil DHCP" \
|
||||
--linux $linux --initrd $dist/initrd --cmdline "console=tty0 console=ttyS0,115200"
|
||||
|
||||
MB=$(( 2**20 ))
|
||||
sz=$(( ( $(stat -c %s $uki) + MB ) / MB + 2 ))
|
||||
|
||||
efi=$dist/efi.img
|
||||
|
||||
if [ -e $efi ]; then rm $efi; fi
|
||||
|
||||
truncate -s ${sz}M $efi
|
||||
sgdisk -n 1:2048:0 -t 1:ef00 -c 1:"EFI System" $efi
|
||||
|
||||
offset=$(( 2048 * 512 ))
|
||||
|
||||
export MTOOLSRC=$(mktemp)
|
||||
trap "rm -f $MTOOLSRC" exit
|
||||
echo "drive e: file=\"$efi\" offset=$offset" >$MTOOLSRC
|
||||
|
||||
args="-i $efi@@$offset"
|
||||
|
||||
mformat -F e:
|
||||
mmd e:EFI e:EFI/BOOT
|
||||
mcopy $uki e:EFI/BOOT/BOOTX64.EFI
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
anti_phishing_code: "direktil<3"
|
||||
|
||||
modules: /modules.fs
|
||||
|
||||
auths:
|
||||
- name: adm1@novit
|
||||
sshKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICkpbU6sf4t0f6XAv9DuW3XH5iLM0AI5rc8PT2jwea1N
|
||||
- name: adm2@novit
|
||||
sshKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIomzqVAIqb7BedauhAo2VgbLqme5Jx/vjGUqZLoJqF
|
||||
|
||||
ssh:
|
||||
listen: "[::]:22"
|
||||
|
||||
networks:
|
||||
- name: loopback
|
||||
interfaces: [ { var: iface, n: 1, udev: !eq [INTERFACE, lo] } ]
|
||||
script: |
|
||||
ip a add 127.0.0.1/8 dev lo
|
||||
ip a add ::1/128 dev lo
|
||||
ip li set lo up
|
||||
- name: main
|
||||
interfaces:
|
||||
- var: iface
|
||||
n: -1
|
||||
udev: !has ID_NET_NAME_MAC
|
||||
script: |
|
||||
ip link add main type bond mode active-backup
|
||||
for l in $ifaces; do
|
||||
ip link set $l master main
|
||||
ip link set $l up
|
||||
done
|
||||
ip link set main up
|
||||
udhcpc -b -i main
|
||||
|
||||
bootstrap:
|
||||
dev: /dev/storage/bootstrap
|
||||
@@ -166,6 +166,13 @@ async fn mount_modules(modules: &str, kernel_version: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
symlink(modules_path, format!("/lib/modules/{kernel_version}"))?;
|
||||
|
||||
let firmware_path = &format!("/modules/lib/firmware/{kernel_version}");
|
||||
if std::fs::exists(firmware_path)? {
|
||||
fs::create_dir_all("/lib/firmware").await?;
|
||||
symlink(firmware_path, format!("/lib/firmware/{kernel_version}"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+95
-21
@@ -1,5 +1,5 @@
|
||||
use eyre::{format_err, Result};
|
||||
use log::{info, warn};
|
||||
use log::{debug, info, warn};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::{
|
||||
fs,
|
||||
@@ -49,7 +49,7 @@ pub async fn bootstrap(cfg: Config) {
|
||||
.await;
|
||||
|
||||
let sys_cfg: dkl::Config = retry(async || {
|
||||
let sys_cfg_bytes = seed_config(base_dir, &bs.seed, &verifier).await?;
|
||||
let sys_cfg_bytes = seed_config(base_dir, &bs, &verifier).await?;
|
||||
Ok(serde_yaml::from_slice(&sys_cfg_bytes)?)
|
||||
})
|
||||
.await;
|
||||
@@ -79,7 +79,30 @@ pub async fn bootstrap(cfg: Config) {
|
||||
})
|
||||
.await;
|
||||
|
||||
exec("chroot", &["/system", "update-ca-certificates"]).await
|
||||
exec("chroot", &["/system", "update-ca-certificates"]).await;
|
||||
|
||||
// activate ttyS* consoles as needed
|
||||
retry_or_ignore(async || {
|
||||
const PATH: &str = "/system/etc/inittab";
|
||||
let mut inittab = fs::read_to_string(PATH).await?;
|
||||
let mut changed = false;
|
||||
for opt in utils::cmdline().filter_map(|s| s.strip_prefix("console=ttyS")) {
|
||||
info!("inittab: adding entry for ttyS{opt}");
|
||||
changed = true;
|
||||
|
||||
let mut params = opt.split(',');
|
||||
let num = params.next().unwrap();
|
||||
let speed = params.next().unwrap_or("115200");
|
||||
inittab.push_str(&format!(
|
||||
"S{num}:12345:respawn:/sbin/agetty --noclear {speed} ttyS{num} linux\n"
|
||||
));
|
||||
}
|
||||
if changed {
|
||||
fs::write(PATH, inittab.as_bytes()).await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
struct Verifier {
|
||||
@@ -129,45 +152,85 @@ impl Verifier {
|
||||
}
|
||||
|
||||
async fn seed_config(
|
||||
base_dir: &str,
|
||||
seed_url: &Option<String>,
|
||||
base_dir: impl Into<PathBuf>,
|
||||
bs: &dkl::bootstrap::Bootstrap,
|
||||
verifier: &Verifier,
|
||||
) -> Result<Vec<u8>> {
|
||||
let cfg_path = &format!("{base_dir}/config.yaml");
|
||||
let base_dir = base_dir.into();
|
||||
|
||||
if fs::try_exists(cfg_path).await? {
|
||||
return Ok(fs::read(cfg_path).await?);
|
||||
let cfg_path = base_dir.join("config.yaml");
|
||||
|
||||
if fs::try_exists(&cfg_path).await? {
|
||||
return verifier.verify_path(cfg_path).await;
|
||||
}
|
||||
|
||||
let bs_tar = "/bootstrap.tar";
|
||||
if !fs::try_exists(bs_tar).await? {
|
||||
if let Some(seed_url) = seed_url.as_ref() {
|
||||
fetch_bootstrap(seed_url, bs_tar).await?;
|
||||
} else {
|
||||
if !fs::try_exists(&bs_tar).await? {
|
||||
if bs.seed.is_none() {
|
||||
return Err(format_err!(
|
||||
"no {cfg_path}, no {bs_tar} and no seed, can't bootstrap"
|
||||
"no {}, no {bs_tar} and no seed URL, can't bootstrap",
|
||||
cfg_path.display()
|
||||
));
|
||||
}
|
||||
fetch_bootstrap(bs, bs_tar).await?;
|
||||
}
|
||||
|
||||
try_exec("tar", &["xf", bs_tar, "-C", base_dir]).await?;
|
||||
let tmp_dir = base_dir.with_added_extension("new");
|
||||
fs::create_dir_all(&tmp_dir).await?;
|
||||
|
||||
if !fs::try_exists(cfg_path).await? {
|
||||
return Err(format_err!("{cfg_path} does not exist after seeding"));
|
||||
untar(bs_tar, &tmp_dir)
|
||||
.await
|
||||
.map_err(|e| format_err!("untar failed: {e}"))?;
|
||||
|
||||
let cfg_path = tmp_dir.join("config.yaml");
|
||||
if !fs::try_exists(&cfg_path).await? {
|
||||
return Err(format_err!(
|
||||
"{} does not exist after seeding",
|
||||
cfg_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
verifier.verify_path(&cfg_path).await
|
||||
let cfg_bytes = verifier.verify_path(&cfg_path).await?;
|
||||
|
||||
fs::rename(tmp_dir, base_dir).await?;
|
||||
Ok(cfg_bytes)
|
||||
}
|
||||
|
||||
async fn fetch_bootstrap(seed_url: &str, output_file: &str) -> Result<()> {
|
||||
let seed_url: reqwest::Url = seed_url.parse()?;
|
||||
async fn fetch_bootstrap(bs: &dkl::bootstrap::Bootstrap, output_file: &str) -> Result<()> {
|
||||
let seed_url: reqwest::Url = (bs.seed.as_ref())
|
||||
.ok_or(format_err!("no seed URL"))?
|
||||
.parse()
|
||||
.map_err(|e| format_err!("invalid seed URL: {e}"))?;
|
||||
|
||||
info!(
|
||||
"fetching {output_file} from {}",
|
||||
seed_url.host_str().unwrap_or("<no host>")
|
||||
);
|
||||
|
||||
let resp = reqwest::get(seed_url).await?;
|
||||
let mut builder = reqwest::Client::builder();
|
||||
|
||||
if let Some(ref proxy) = bs.seed_proxy {
|
||||
debug!("using proxy {proxy}");
|
||||
let proxy = reqwest::Proxy::all(proxy) //
|
||||
.map_err(|e| format_err!("seed proxy setup failed: {e}"))?;
|
||||
builder = builder.proxy(proxy);
|
||||
}
|
||||
|
||||
if let Some(ref ca) = bs.seed_ca {
|
||||
debug!("using custom CA certificate");
|
||||
let ca = base64_decode(ca).map_err(|e| format_err!("invalid seed CA: decode: {e}"))?;
|
||||
let ca = reqwest::Certificate::from_der(&ca)
|
||||
.map_err(|e| format_err!("invalid seed CA: parse: {e}"))?;
|
||||
builder = builder.tls_certs_only([ca]);
|
||||
}
|
||||
|
||||
if let Some(ref sn) = bs.seed_servername {
|
||||
debug!("tls server name: {sn}");
|
||||
builder = builder.tls_server_name(bs.seed_servername.clone());
|
||||
}
|
||||
|
||||
let req = builder.build()?.get(seed_url);
|
||||
let resp = req.send().await?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(format_err!("HTTP request failed: {}", resp.status()));
|
||||
@@ -181,6 +244,18 @@ async fn fetch_bootstrap(seed_url: &str, output_file: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn untar(arch: impl Into<PathBuf>, target: impl Into<PathBuf>) -> Result<()> {
|
||||
let arch = arch.into();
|
||||
let target = target.into();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let tar = std::fs::File::open(arch)?;
|
||||
let mut tar = tar::Archive::new(tar);
|
||||
tar.unpack(target)
|
||||
})
|
||||
.await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_root_tmpfs_opts() -> Option<String> {
|
||||
let mem = sys_info::mem_info()
|
||||
.inspect_err(|e| warn!("failed to get system memory info, using default tmpfs size: {e}"))
|
||||
@@ -315,7 +390,6 @@ async fn mount_system(cfg: &dkl::Config, bs_cfg: &Config, bs_dir: &str, verifier
|
||||
if layer == "modules" && bs_cfg.modules.is_some() {
|
||||
continue; // take modules from initrd
|
||||
}
|
||||
|
||||
mounter.mount(layer).await;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ static CMDLINE: LazyLock<String> = LazyLock::new(|| {
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
fn cmdline() -> impl Iterator<Item = &'static str> {
|
||||
pub fn cmdline() -> impl Iterator<Item = &'static str> {
|
||||
CMDLINE.split_ascii_whitespace()
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ lvm:
|
||||
#- dev: /dev/storage/bootstrap
|
||||
#- dev: /dev/storage/dls
|
||||
|
||||
signer_public_key: 'MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBe6Y3zGQUIHvVXoS5GI8irY8yoB0ozFpzn/cUykA46TkHdJ8xCEaaM1MpqMrfWgDtP/rA2KeE9HjVerLnEFD01uUAUh4/OYgCBDYJPhridVDoC78KOJpkWBj7Shl0Rp0AtETvatNPa1RRe15V7nDF/Nm75Y6O3IL29lYPQ6jqEGhR810='
|
||||
signer_public_key: 'MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAd5sR4NqLtjSt8ESNlYWvuufYj7v+aYGDlgxQThcKbzDPVe639IfH94hHE0l9TAfyU94qtN/GpFyKJ68F/u2pu70A/umT1m24ELFDqXlQXqhTsH91r+nYUZ7due3EqSrvru/yjchNNRkpoCCu3QkDF25KnrYfWWHqj9ZIRlBTCJE9SwM='
|
||||
bootstrap:
|
||||
dev: /dev/storage/bootstrap
|
||||
seed: http://192.168.12.254:7606/public/download-set/host/m1/bootstrap.tar?set=ICIXKJJWA6U4RQESD3KQMWO3IBW6THG4FJUM2HUNFPTIODVSXGDPXTCHSFT6IOUZO6LBAG65QIGYUMIZA3TEHTPB6BXKUFONNUWKUWAJAQRE2GDEOC4RWAAAQA3DSZJXMNSDGN34NA5G2MJ2MJXW65DTORZGC4BOORQXEAAAAAACMICVFM
|
||||
seed: http://192.168.12.254:7606/public/download-set/host/m1/bootstrap.tar?set=ICM5KUZDRAMJPMO5OWW6PSIFYF4AHMYLAQSBZVFUDNG4DQDEW6UFQQJQKMGIXPI4CFOZFVA4CXULRXCAHKX3WELVAYS246FM6SGSGHIOAQRE2GDEOC4RUAAAQA3GEZDFMUZDOMD4NA5CUOTCN5XXI43UOJQXALTUMFZAAAAAACHHUMRU
|
||||
|
||||
|
||||
Reference in New Issue
Block a user