move configs to dkl crate

This commit is contained in:
Mikaël Cluseau
2025-07-17 16:15:36 +02:00
parent e30a46d62b
commit ea9861efe1
12 changed files with 1397 additions and 333 deletions

1409
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -30,3 +30,4 @@ cpio = "0.4.1"
lz4 = "1.28.1"
base64 = "0.22.1"
sys-info = "0.9.1"
dkl = { git = "https://novit.tech/direktil/dkl", version = "0.1.0" }

View File

@ -1 +0,0 @@
pub mod config;

View File

@ -1,192 +0,0 @@
use std::collections::BTreeMap as Map;
pub const TAKE_ALL: i16 = -1;
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Config {
pub anti_phishing_code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub keymap: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modules: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resolv_conf: Option<String>,
#[serde(default)]
pub vpns: Map<String, String>,
pub networks: Vec<Network>,
pub auths: Vec<Auth>,
#[serde(default)]
pub ssh: SSHServer,
#[serde(default)]
pub pre_lvm_crypt: Vec<CryptDev>,
#[serde(default)]
pub lvm: Vec<LvmVG>,
#[serde(default)]
pub crypt: Vec<CryptDev>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer_public_key: Option<String>,
pub bootstrap: Bootstrap,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Auth {
pub name: String,
#[serde(alias = "sshKey")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ssh_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Network {
pub name: String,
pub interfaces: Vec<NetworkInterface>,
pub script: String,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct NetworkInterface {
pub var: String,
pub n: i16,
pub regexps: Vec<String>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SSHServer {
pub listen: String,
pub user_ca: Option<String>,
}
impl Default for SSHServer {
fn default() -> Self {
Self {
listen: "[::]:22".to_string(),
user_ca: None,
}
}
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LvmVG {
#[serde(alias = "vg")]
pub name: String,
pub pvs: LvmPV,
#[serde(default)]
pub defaults: LvmLVDefaults,
pub lvs: Vec<LvmLV>,
}
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct LvmLVDefaults {
#[serde(default)]
pub fs: Filesystem,
#[serde(default)]
pub raid: Raid,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Filesystem {
Ext4,
Xfs,
Btrfs,
Other(String),
}
impl Filesystem {
pub fn fstype(&self) -> &str {
use Filesystem as F;
match self {
F::Ext4 => "ext4",
F::Xfs => "xfs",
F::Btrfs => "btrfs",
F::Other(t) => t,
}
}
}
impl Default for Filesystem {
fn default() -> Self {
Filesystem::Ext4
}
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LvmLV {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub fs: Option<Filesystem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub raid: Option<Raid>,
#[serde(flatten)]
pub size: LvSize,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum LvSize {
Size(String),
Extents(String),
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LvmPV {
pub n: i16,
pub regexps: Vec<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct CryptDev {
pub name: String,
#[serde(flatten)]
pub filter: DevFilter,
pub optional: Option<bool>,
}
impl CryptDev {
pub fn optional(&self) -> bool {
self.optional.unwrap_or_else(|| self.filter.is_prefix())
}
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DevFilter {
Dev(String),
Prefix(String),
}
impl DevFilter {
pub fn is_dev(&self) -> bool {
match self {
Self::Dev(_) => true,
_ => false,
}
}
pub fn is_prefix(&self) -> bool {
match self {
Self::Prefix(_) => true,
_ => false,
}
}
}
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
pub struct Raid {
pub mirrors: Option<u8>,
pub stripes: Option<u8>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Bootstrap {
pub dev: String,
pub seed: Option<String>,
}

View File

@ -5,7 +5,8 @@ use std::os::unix::fs::symlink;
use tokio::sync::Mutex;
use tokio::{fs, process::Command};
use crate::{bootstrap::config::Config, cmd::version::version_string, dklog, input, utils};
use crate::{cmd::version::version_string, dklog, input, utils};
use dkl::bootstrap::Config;
mod bootstrap;
mod dmcrypt;

View File

@ -1,14 +1,14 @@
use eyre::{format_err, Result};
use log::{info, warn};
use std::path::Path;
use tokio::{
fs,
io::{AsyncBufReadExt, BufReader},
};
use dkl::{self, apply::{self, chroot, set_perms}, bootstrap::Config};
use super::{exec, mount, retry, retry_or_ignore, try_exec};
use crate::bootstrap::config::Config;
use crate::{dkl, utils};
use crate::utils;
pub async fn bootstrap(cfg: Config) {
let verifier = retry(async || Verifier::from_config(&cfg)).await;
@ -50,7 +50,7 @@ pub async fn bootstrap(cfg: Config) {
})
.await;
retry_or_ignore(async || apply_files(&sys_cfg.files, "/system").await).await;
retry_or_ignore(async || apply::files(&sys_cfg.files, "/system").await).await;
apply_groups(&sys_cfg.groups, "/system").await;
apply_users(&sys_cfg.users, "/system").await;
@ -250,47 +250,6 @@ async fn mount_system(cfg: &dkl::Config, bs_dir: &str, verifier: &Verifier) {
.await;
}
fn chroot(root: &str, path: &str) -> String {
format!("{root}/{}", path.trim_start_matches(|c| c == '/'))
}
async fn apply_files(files: &[dkl::File], root: &str) -> Result<()> {
for file in files {
let path = chroot(root, &file.path);
let path = Path::new(&path);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).await?;
}
use crate::dkl::FileKind as K;
match &file.kind {
K::Content(content) => fs::write(path, content.as_bytes()).await?,
K::Dir(true) => fs::create_dir(path).await?,
K::Dir(false) => {} // shouldn't happen, but semantic is to ignore
K::Symlink(tgt) => fs::symlink(tgt, path).await?,
}
match file.kind {
K::Symlink(_) => {}
_ => set_perms(path, file.mode).await?,
}
info!("created {}", file.path);
}
Ok(())
}
async fn set_perms(path: impl AsRef<Path>, mode: Option<u32>) -> std::io::Result<()> {
if let Some(mode) = mode.filter(|m| *m != 0) {
use std::os::unix::fs::PermissionsExt;
let mode = std::fs::Permissions::from_mode(mode);
fs::set_permissions(path, mode).await?;
}
Ok(())
}
async fn apply_groups(groups: &[dkl::Group], root: &str) {
for group in groups {
let mut args = vec![root, "groupadd", "-r"];

View File

@ -8,7 +8,7 @@ use tokio::sync::Mutex;
use super::{retry_or_ignore, USED_DEVS};
use crate::blockdev::{is_uninitialized, uninitialize};
use crate::bootstrap::config::{CryptDev, DevFilter};
use dkl::bootstrap::{CryptDev, DevFilter};
use crate::fs::walk_dir;
use crate::input;

View File

@ -3,7 +3,7 @@ use log::{error, info, warn};
use tokio::process::Command;
use super::{exec, retry, retry_or_ignore, USED_DEVS};
use crate::bootstrap::config::{Config, Filesystem, LvSize, LvmLV, LvmVG, TAKE_ALL};
use dkl::bootstrap::{Config, Filesystem, LvSize, LvmLV, LvmVG, TAKE_ALL};
use crate::fs::walk_dir;
use crate::{blockdev, lvm};

View File

@ -3,9 +3,9 @@ use log::{info, warn};
use std::collections::BTreeSet as Set;
use tokio::process::Command;
use super::{format_err, retry_or_ignore, Config, Result};
use super::{format_err, retry_or_ignore, Result};
use dkl::bootstrap::{Config,Network};
use crate::{
bootstrap::config,
udev,
utils::{select_n_by_regex, NameAliases},
};
@ -23,7 +23,7 @@ pub async fn setup(cfg: &Config) {
}
}
async fn setup_network(net: &config::Network, assigned: &mut Set<String>) -> Result<()> {
async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()> {
info!("setting up network {}", net.name);
let netdevs = get_interfaces()?

View File

@ -7,7 +7,7 @@ use tokio::net;
use tokio::process::Command;
use super::retry_or_ignore;
use crate::bootstrap::config::{Config, SSHServer};
use dkl::bootstrap::{Config, SSHServer};
pub async fn start(cfg: &Config) {
retry_or_ignore(async || {

View File

@ -1,61 +0,0 @@
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Config {
pub layers: Vec<String>,
pub root_user: RootUser,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mounts: Vec<Mount>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub files: Vec<File>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub groups: Vec<Group>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub users: Vec<User>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct RootUser {
#[serde(skip_serializing_if = "Option::is_none")]
pub password_hash: Option<String>,
pub authorized_keys: Vec<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Mount {
pub r#type: Option<String>,
pub dev: String,
pub path: String,
pub options: Option<String>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Group {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub gid: Option<u32>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct User {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub uid: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gid: Option<u32>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct File {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<u32>,
#[serde(flatten)]
pub kind: FileKind,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FileKind {
Content(String),
Symlink(String),
Dir(bool),
}

View File

@ -1,4 +1,3 @@
pub mod bootstrap;
pub mod cmd;
pub mod lsblk;
pub mod lvm;
@ -7,5 +6,4 @@ pub mod utils;
pub mod input;
pub mod blockdev;
pub mod fs;
pub mod dkl;
pub mod dklog;