Files
dkl/src/bootstrap.rs
T

258 lines
6.7 KiB
Rust
Raw Normal View History

2025-07-17 15:42:08 +02:00
use std::collections::BTreeMap as Map;
pub const TAKE_ALL: i16 = -1;
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
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>,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Map::is_empty")]
2025-07-17 15:42:08 +02:00
pub vpns: Map<String, String>,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub networks: Vec<Network>,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub auths: Vec<Auth>,
#[serde(default)]
pub ssh: SSHServer,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub pre_lvm_crypt: Vec<CryptDev>,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub lvm: Vec<LvmVG>,
2025-11-16 16:31:51 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub crypt: Vec<CryptDev>,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer_public_key: Option<String>,
pub bootstrap: Bootstrap,
}
2025-11-14 21:48:35 +01:00
impl Config {
pub fn new(bootstrap_dev: String) -> Self {
Self {
anti_phishing_code: "Direktil<3".into(),
keymap: None,
modules: None,
resolv_conf: None,
vpns: Map::new(),
networks: vec![],
auths: vec![],
ssh: Default::default(),
pre_lvm_crypt: vec![],
lvm: vec![],
crypt: vec![],
signer_public_key: None,
bootstrap: Bootstrap {
dev: bootstrap_dev,
seed: None,
},
}
}
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
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>,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct Network {
pub name: String,
pub interfaces: Vec<NetworkInterface>,
pub script: String,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct NetworkInterface {
pub var: String,
pub n: i16,
2025-11-10 13:55:14 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub regexps: Vec<String>,
2025-11-10 13:55:14 +01:00
#[serde(skip_serializing_if = "Option::is_none")]
pub udev: Option<UdevFilter>,
2025-07-17 15:42:08 +02:00
}
#[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,
}
}
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct LvmVG {
#[serde(alias = "vg")]
pub name: String,
pub pvs: LvmPV,
#[serde(default)]
pub defaults: LvmLVDefaults,
pub lvs: Vec<LvmLV>,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct LvmLVDefaults {
#[serde(default)]
pub fs: Filesystem,
#[serde(default)]
pub raid: Raid,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
#[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
}
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
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,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
#[serde(rename_all = "snake_case")]
pub enum LvSize {
Size(String),
Extents(String),
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct LvmPV {
pub n: i16,
2025-11-10 13:55:14 +01:00
#[serde(default, skip_serializing_if = "Vec::is_empty")]
2025-07-17 15:42:08 +02:00
pub regexps: Vec<String>,
2025-11-10 13:55:14 +01:00
#[serde(skip_serializing_if = "Option::is_none")]
pub udev: Option<UdevFilter>,
2025-07-17 15:42:08 +02:00
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct CryptDev {
pub name: String,
2025-11-10 13:55:14 +01:00
// hit the limit of enum representation here (flatten + enum variant case)
#[serde(skip_serializing_if = "Option::is_none")]
pub dev: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub udev: Option<UdevFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
2025-07-17 15:42:08 +02:00
pub optional: Option<bool>,
}
impl CryptDev {
2025-11-10 13:55:14 +01:00
pub fn filter(&self) -> DevFilter<'_> {
if let Some(dev) = self.dev.as_deref() {
DevFilter::Dev(dev)
} else if let Some(prefix) = self.prefix.as_deref() {
DevFilter::Prefix(prefix)
} else if let Some(udev) = self.udev.as_ref() {
DevFilter::Udev(udev)
} else {
DevFilter::None
}
}
2025-07-17 15:42:08 +02:00
pub fn optional(&self) -> bool {
2025-11-10 13:55:14 +01:00
self.optional.unwrap_or_else(|| match self.filter() {
DevFilter::None => true,
DevFilter::Dev(_) => false,
DevFilter::Prefix(_) => true,
DevFilter::Udev(_) => true,
})
2025-07-17 15:42:08 +02:00
}
}
2025-11-10 13:55:14 +01:00
#[test]
fn test_parse_crypt_dev() {
for s in [
"name: sys0\ndev: /dev/sda\n",
"name: crypt-\nprefix: /dev/sd\n",
"name: crypt-${name}\nudev: !glob [ DEVNAME, /dev/sd* ]\n",
] {
let dev: CryptDev = serde_yaml::from_str(s).unwrap();
dev.filter();
dev.optional();
2025-07-17 15:42:08 +02:00
}
}
2025-11-10 13:55:14 +01:00
pub enum DevFilter<'t> {
None,
Dev(&'t str),
Prefix(&'t str),
Udev(&'t UdevFilter),
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-11-10 13:55:14 +01:00
#[serde(rename_all = "snake_case")]
pub enum UdevFilter {
Has(String),
Eq(String, String),
Glob(String, String),
And(Vec<UdevFilter>),
Or(Vec<UdevFilter>),
Not(Box<UdevFilter>),
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct Raid {
pub mirrors: Option<u8>,
pub stripes: Option<u8>,
}
2025-11-16 10:12:22 +01:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
2025-07-17 15:42:08 +02:00
pub struct Bootstrap {
pub dev: String,
2025-11-16 16:31:51 +01:00
#[serde(skip_serializing_if = "Option::is_none")]
2025-07-17 15:42:08 +02:00
pub seed: Option<String>,
}