Compare commits

...

5 commits

Author SHA1 Message Date
f235867600
update
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2025-01-10 09:45:21 +01:00
05cf6cf9da
wheel group 2025-01-10 09:28:03 +01:00
33a8fed192
refactor 2025-01-09 23:18:33 +01:00
12b2cf4a96
add bluetooth support 2025-01-09 23:04:35 +01:00
227b97b27f
refactor 2025-01-09 23:00:22 +01:00
17 changed files with 250 additions and 90 deletions

View file

@ -33,6 +33,12 @@ hostname = "navos"
# Root password # Root password
root_password = "root" root_password = "root"
# Enable Bluetooth
bluetooth = true
# GPU Video Drivers
gpu_driver = "NVIDIA"
[pkg] [pkg]
# Additional packages # Additional packages
pkg = [ pkg = [
@ -49,5 +55,5 @@ name = "u"
# User password # User password
password = "pass" password = "pass"
# Allow user to use `doas` as root # Add user to wheel group
doas_root= true wheel = true

View file

@ -33,6 +33,12 @@ hostname = "navos"
# Root password # Root password
root_password = "root" root_password = "root"
# Enable Bluetooth
bluetooth = true
# GPU Video Drivers
gpu_driver = "NVIDIA"
[pkg] [pkg]
# Additional packages # Additional packages
pkg = [ pkg = [
@ -66,8 +72,8 @@ home_dir = "/home/u"
# Set the shell of the user # Set the shell of the user
shell = "/bin/bash" shell = "/bin/bash"
# Allow user to use `doas` as root # Add user to wheel group
doas_root= true wheel = true
# Add user to Docker group # Add user to Docker group
docker = true docker = true

View file

@ -52,8 +52,8 @@ name = "u"
# User password # User password
password = "pass" password = "pass"
# Allow user to use `doas` as root # Add user to wheel group
doas_root= true wheel = true
# Add user to Docker group # Add user to Docker group
docker = true docker = true

View file

@ -44,7 +44,7 @@ pub struct UserConfig {
pub uid: Option<u32>, pub uid: Option<u32>,
pub home_dir: Option<String>, pub home_dir: Option<String>,
pub shell: Option<String>, pub shell: Option<String>,
pub doas_root: Option<bool>, pub wheel: Option<bool>,
pub docker: Option<bool>, pub docker: Option<bool>,
pub virtualization: Option<bool>, pub virtualization: Option<bool>,
} }
@ -85,6 +85,17 @@ pub struct GeneralConfig {
pub hostname: String, pub hostname: String,
// Root password // Root password
pub root_password: Option<String>, pub root_password: Option<String>,
// Enable Bluetooth
pub bluetooth: Option<bool>,
/// Install Video Driver
pub gpu_driver: Option<GPUVendor>
}
#[derive(Debug, Clone, Deserialize)]
pub enum GPUVendor {
AMD,
NVIDIA,
INTEL
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]

8
src/install/bluetooth.rs Normal file
View file

@ -0,0 +1,8 @@
use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status};
/// Enable Bluetooth
pub fn setup_bluetooth() {
print_status("Setting up Bluetooth");
install_pkgs(&["bluez", "bluez-utils"]);
systemd_service_enable("bluetooth.service");
}

29
src/install/desktop.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::{
config::InstallConfig,
linux::install_file,
pkg::{self, install_pkgs},
print_status,
};
use super::navos::setup_navos;
pub fn setup_desktop(conf: &InstallConfig) {
setup_navos();
install_pkgs(&pkg::DESKTOP_PKG);
print_status("Enable SDDM");
print_status("Set keyboard layout for SDDM");
install_file(
"/mnt/usr/share/sddm/scripts/Xsetup",
&format!(
"#!/bin/sh\n# Xsetup\nsetxkbmap {},us\n",
conf.general.keyboard_layout
),
0o644,
);
std::os::unix::fs::symlink(
"/usr/lib/systemd/system/sddm.service",
"/mnt/etc/systemd/system/display-manager.service",
)
.unwrap();
}

24
src/install/firmware.rs Normal file
View file

@ -0,0 +1,24 @@
use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status};
pub fn setup_fwupd() {
print_status("Enabling firmware updates");
install_pkgs(&["fwupd"]);
systemd_service_enable("fwupd-refresh.timer");
}
pub fn setup_microcode() {
print_status("Installing CPU Microcode");
let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").unwrap();
for line in cpuinfo.lines() {
if line.starts_with("vendor_id") {
if line.contains("GenuineIntel") {
install_pkgs(&["intel-ucode"]);
}
if line.contains("AuthenticAMD") {
install_pkgs(&["amd-ucode"]);
}
}
}
}

View file

@ -2,7 +2,7 @@
use crate::{ use crate::{
config::GeneralConfig, config::GeneralConfig,
linux::{arch_chroot, run_command, systemd_service_enable}, linux::{arch_chroot, install_file, run_command, systemd_service_enable},
print_status, print_status,
}; };
@ -45,6 +45,8 @@ pub fn first_boot_values(conf: &GeneralConfig) {
print_status("Writing /etc/hostname"); print_status("Writing /etc/hostname");
std::fs::write("/mnt/etc/hostname", format!("{}\n", conf.hostname)).unwrap(); std::fs::write("/mnt/etc/hostname", format!("{}\n", conf.hostname)).unwrap();
install_file("/mnt/etc/hosts", &format!("127.0.0.1 localhost\n::1 localhost\n127.0.1.1 {}\n", conf.hostname), 0o644);
// LOCALE // LOCALE
print_status("Setting locale"); print_status("Setting locale");
uncomment_first_value_of(&conf.locale, "/mnt/etc/locale.gen"); uncomment_first_value_of(&conf.locale, "/mnt/etc/locale.gen");

15
src/install/gpu.rs Normal file
View file

@ -0,0 +1,15 @@
use crate::{config::GPUVendor, pkg::install_pkgs};
pub fn setup_video_drivers(vendor: &GPUVendor) {
match vendor {
GPUVendor::AMD => {
install_pkgs(&["xf86-video-amdgpu", "mesa", "lib32-mesa", "vulkan-radeon", "lib32-vulkan-radeon"]);
},
GPUVendor::NVIDIA => {
install_pkgs(&["nvidia", "nvidia-utils", "lib32-nvidia-utils"]);
},
GPUVendor::INTEL => {
install_pkgs(&["xf86-video-intel2", "mesa", "lib32-mesa", "vulkan-intel", "lib32-vulkan-intel"]);
},
}
}

View file

@ -1,12 +1,17 @@
// TODO : Autojoin docker swarm // TODO : Autojoin docker swarm
// TODO : Autojoin teleport // TODO : Autojoin teleport
// TODO : Firewall
// DRIVE SELECTION // DRIVE SELECTION
use bluetooth::setup_bluetooth;
use boot::setup_bootloader; use boot::setup_bootloader;
use desktop::setup_desktop;
use docker::setup_docker; use docker::setup_docker;
use drives::{format_drives, mount_drives}; use drives::{format_drives, mount_drives};
use firmware::{setup_fwupd, setup_microcode};
use first_boot::{first_boot_values, genfstab}; use first_boot::{first_boot_values, genfstab};
use gpu::setup_video_drivers;
use kernel::setup_mkinitcpio; use kernel::setup_mkinitcpio;
use navos::setup_navos; use navos::setup_navos;
use ollama::setup_ollama; use ollama::setup_ollama;
@ -14,10 +19,13 @@ use security::{setup_secure_boot, setup_tpm_unlock};
use skel::setup_skel; use skel::setup_skel;
use ssh::setup_ssh; use ssh::setup_ssh;
use user::setup_users; use user::setup_users;
use virt::{setup_virtualization, setup_vm};
use yansi::{Color, Paint}; use yansi::{Color, Paint};
use zram::setup_zram; use zram::setup_zram;
pub mod bluetooth;
pub mod boot; pub mod boot;
pub mod desktop;
pub mod docker; pub mod docker;
pub mod drives; pub mod drives;
pub mod first_boot; pub mod first_boot;
@ -28,13 +36,14 @@ pub mod security;
pub mod skel; pub mod skel;
pub mod ssh; pub mod ssh;
pub mod user; pub mod user;
pub mod virt;
pub mod zram; pub mod zram;
pub mod firmware;
pub mod gpu;
use crate::{ use crate::{
config::{InstallConfig, InstallMode}, config::InstallConfig,
linux::{arch_chroot, install_file, systemd_service_enable},
pkg::{self, install_pkgs, pacstrap}, pkg::{self, install_pkgs, pacstrap},
print_status,
}; };
pub fn uncomment_first_value_of(value: &str, file: &str) { pub fn uncomment_first_value_of(value: &str, file: &str) {
@ -91,31 +100,14 @@ pub fn install(conf: InstallConfig) {
setup_skel(&conf.general); setup_skel(&conf.general);
setup_users(&conf.user.as_ref().unwrap_or(&Vec::new())); setup_users(&conf.user.as_ref().unwrap_or(&Vec::new()));
setup_ssh(conf.ssh); setup_ssh(&conf.ssh);
setup_bootloader(); setup_bootloader();
match conf.general.mode { match conf.general.mode {
crate::config::InstallMode::Base => {} crate::config::InstallMode::Base => {}
crate::config::InstallMode::Desktop => { crate::config::InstallMode::Desktop => {
setup_navos(); setup_desktop(&conf);
install_pkgs(&pkg::DESKTOP_PKG);
print_status("Enable SDDM");
print_status("Set keyboard layout for SDDM");
install_file(
"/mnt/usr/share/sddm/scripts/Xsetup",
&format!(
"#!/bin/sh\n# Xsetup\nsetxkbmap {},us\n",
conf.general.keyboard_layout
),
0o644,
);
std::os::unix::fs::symlink(
"/usr/lib/systemd/system/sddm.service",
"/mnt/etc/systemd/system/display-manager.service",
)
.unwrap();
} }
crate::config::InstallMode::Server => { crate::config::InstallMode::Server => {
setup_navos(); setup_navos();
@ -127,29 +119,7 @@ pub fn install(conf: InstallConfig) {
} }
if conf.pkg.virtualization.unwrap_or_default() { if conf.pkg.virtualization.unwrap_or_default() {
let user_conf = if let Some(user_conf) = &conf.user { setup_virtualization(&conf);
user_conf.clone()
} else {
Vec::new()
};
install_pkgs(&["libvirt"]);
if matches!(conf.general.mode, InstallMode::Desktop) {
install_pkgs(&["virt-manager"]);
}
systemd_service_enable("libvirtd.service");
for user in user_conf {
if user.virtualization.unwrap_or_default() {
arch_chroot(
&vec!["usermod", "-a", "-G", "libvirt", user.name.as_str()],
None,
false,
);
}
}
} }
if conf.pkg.docker.unwrap_or_default() { if conf.pkg.docker.unwrap_or_default() {
@ -167,6 +137,17 @@ pub fn install(conf: InstallConfig) {
} }
setup_zram(); setup_zram();
if conf.general.bluetooth.unwrap_or_default() {
setup_bluetooth();
}
setup_vm();
if let Some(gpu_vendor) = &conf.general.gpu_driver {
setup_video_drivers(gpu_vendor);
}
setup_fwupd();
setup_microcode();
setup_mkinitcpio(&conf.drive); setup_mkinitcpio(&conf.drive);
setup_secure_boot(); setup_secure_boot();

View file

@ -4,7 +4,7 @@ use std::io::Write;
/// Setup SSH on the system /// Setup SSH on the system
/// ///
/// This should be done after `setup_users()` to ensure that the users directories exist. /// This should be done after `setup_users()` to ensure that the users directories exist.
pub fn setup_ssh(conf: Option<SSHConfig>) { pub fn setup_ssh(conf: &Option<SSHConfig>) {
if let Some(conf) = conf { if let Some(conf) = conf {
install_pkgs(&["openssh"]); install_pkgs(&["openssh"]);
@ -14,7 +14,7 @@ pub fn setup_ssh(conf: Option<SSHConfig>) {
install_file("/mnt/etc/ssh/sshd_config", &content, 0o644); install_file("/mnt/etc/ssh/sshd_config", &content, 0o644);
} }
for key in &conf.key.unwrap_or_default() { for key in conf.key.as_ref().unwrap_or(&Vec::new()) {
for user in &key.users { for user in &key.users {
let path = if user == "root" { let path = if user == "root" {
std::fs::create_dir_all("/root/.ssh").unwrap(); std::fs::create_dir_all("/root/.ssh").unwrap();

View file

@ -1,7 +1,5 @@
use crate::{ use crate::{
config::UserConfig, config::UserConfig, linux::{arch_chroot, install_file}, pkg::install_pkgs, print_status
linux::{arch_chroot, install_file},
print_status,
}; };
pub fn change_passwd(user: &str, pw: &str) { pub fn change_passwd(user: &str, pw: &str) {
@ -10,7 +8,10 @@ pub fn change_passwd(user: &str, pw: &str) {
/// Setup the users of the system /// Setup the users of the system
pub fn setup_users(conf: &[UserConfig]) { pub fn setup_users(conf: &[UserConfig]) {
let mut doas_conf = String::new(); if !conf.is_empty() {
install_pkgs(&["doas"]);
install_file("/mnt/etc/doas.conf", "permit persist :wheel as root", 0o644);
}
for user in conf { for user in conf {
let mut cmd = vec!["useradd"]; let mut cmd = vec!["useradd"];
@ -45,11 +46,13 @@ pub fn setup_users(conf: &[UserConfig]) {
change_passwd(&user.name, &user.password); change_passwd(&user.name, &user.password);
if user.doas_root.unwrap_or_default() { if user.wheel.unwrap_or_default() {
print_status(&format!("Allowing root doas for {}", user.name)); print_status(&format!("Adding {} to wheel", user.name));
doas_conf.push_str(&format!("permit {} as root\n", user.name)); arch_chroot(
&vec!["usermod", "-a", "-G", "wheel", user.name.as_str()],
None,
false,
);
} }
} }
install_file("/mnt/etc/doas.conf", &doas_conf, 0o644);
} }

35
src/install/virt.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::{
config::{InstallConfig, InstallMode},
linux::{arch_chroot, systemd_service_enable},
pkg::install_pkgs,
};
pub fn setup_virtualization(conf: &InstallConfig) {
let user_conf = if let Some(user_conf) = &conf.user {
user_conf.clone()
} else {
Vec::new()
};
install_pkgs(&["libvirt"]);
if matches!(conf.general.mode, InstallMode::Desktop) {
install_pkgs(&["virt-manager"]);
}
systemd_service_enable("libvirtd.service");
for user in user_conf {
if user.virtualization.unwrap_or_default() {
arch_chroot(
&vec!["usermod", "-a", "-G", "libvirt", user.name.as_str()],
None,
false,
);
}
}
}
pub fn setup_vm() {
// TODO : Install guest tools if in virt with systemd-detect-virt
}

View file

@ -75,6 +75,7 @@ pub fn install_file(path: &str, content: &str, permissions: u32) {
file.write_all(content.as_bytes()).unwrap(); file.write_all(content.as_bytes()).unwrap();
let permissions = std::fs::Permissions::from_mode(permissions); let permissions = std::fs::Permissions::from_mode(permissions);
// TODO : Fix permission format
print_status(&format!("Wrote file {path} [{permissions:#?}]")); print_status(&format!("Wrote file {path} [{permissions:#?}]"));
std::fs::set_permissions(path, permissions).unwrap(); std::fs::set_permissions(path, permissions).unwrap();
} }

View file

@ -97,20 +97,20 @@ pub fn read_conf(config_file: &str) -> InstallConfig {
let config_content = std::fs::read_to_string(config_file); let config_content = std::fs::read_to_string(config_file);
match config_content { match config_content {
Ok(content) => match toml::from_str(&content) { Ok(content) => match toml::from_str(&content) {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {
eprintln!( eprintln!(
"{} {}", "{} {}",
"Error: Could not deserialize TOML file.".paint(Color::Red), "Error: Could not deserialize TOML file.".paint(Color::Red),
e.paint(Color::Red) e.paint(Color::Red)
); );
std::process::exit(1); std::process::exit(1);
}
},
Err(_) => {
eprintln!("{}", "Error: Could not read config file.".paint(Color::Red));
std::process::exit(1);
}
} }
},
Err(_) => {
eprintln!("{}", "Error: Could not read config file.".paint(Color::Red));
std::process::exit(1);
}
}
} }

View file

@ -3,7 +3,31 @@ use crate::{
linux::{arch_chroot, run_command}, linux::{arch_chroot, run_command},
}; };
pub const DESKTOP_PKG: [&str; 5] = ["plasma", "sddm", "konsole", "dolphin", "navos/navos"]; pub const DESKTOP_PKG: [&str; 16] = [
// Desktop
"plasma",
"sddm",
// Sound
"pipewire",
"pipewire-alsa",
"pipewire-pulse",
"pipewire-jack",
"wireplumber",
// Applications
"konsole",
"dolphin",
"ffmpegthumbs",
"kate",
"okular",
"gwenview",
"ark",
"flatpak",
// Misc
"navos/navos"
];
pub const SERVER_PKG: [&str; 2] = ["tmux", "navos/navos"]; pub const SERVER_PKG: [&str; 2] = ["tmux", "navos/navos"];
@ -32,7 +56,11 @@ pub fn pacstrap(conf: &PackageConfig) {
"git", "git",
"networkmanager", "networkmanager",
"nano", "nano",
"doas", "openssh",
"zsh",
"zsh-completions",
"zsh-autosuggestions",
"man"
]; ];
cmd.extend( cmd.extend(

View file

@ -58,11 +58,17 @@ pub fn print_config(conf: &InstallConfig) {
conf.general.timezone conf.general.timezone
)); ));
if conf.general.root_password.is_some() { if conf.general.root_password.is_some() {
general_info.add_str(format!( general_info.add_str(format!("🔑 Root Password {}", "✔️".paint(Color::Green)));
"🔑 {} {}", }
"Root Password".paint(Color::Yellow), if conf.general.bluetooth.unwrap_or_default() {
"✔️".paint(Color::Green) general_info.add_str(format!("🌀 Bluetooth {}", "✔️".paint(Color::Green)));
)); }
if let Some(vendor) = &conf.general.gpu_driver {
match vendor {
crate::config::GPUVendor::AMD => general_info.add_str(format!("🟥 AMD GPU {}", "✔️".paint(Color::Green))),
crate::config::GPUVendor::INTEL => general_info.add_str(format!("🟦 Intel GPU {}", "✔️".paint(Color::Green))),
crate::config::GPUVendor::NVIDIA => general_info.add_str(format!("🟩 NVIDIA GPU {}", "✔️".paint(Color::Green))),
}
} }
root_info.add_tree("🔨 General", general_info); root_info.add_tree("🔨 General", general_info);
@ -79,7 +85,8 @@ pub fn print_config(conf: &InstallConfig) {
if !conf.pkg.pkg.is_empty() { if !conf.pkg.pkg.is_empty() {
pkg_info.add_str(format!( pkg_info.add_str(format!(
"📦 Additional packages: {}", "📦 {} {}",
"Additional packages:".paint(Color::Yellow),
conf.pkg.pkg.join(" ") conf.pkg.pkg.join(" ")
)); ));
} }
@ -94,7 +101,7 @@ pub fn print_config(conf: &InstallConfig) {
for user in user_conf { for user in user_conf {
let mut groups = Vec::new(); let mut groups = Vec::new();
if user.doas_root.unwrap_or_default() { if user.wheel.unwrap_or_default() {
groups.push("🔑"); groups.push("🔑");
} }
@ -145,7 +152,11 @@ pub fn print_config(conf: &InstallConfig) {
} }
if let Some(models) = &ai_conf.models { if let Some(models) = &ai_conf.models {
ai_info.add_str(format!("⬇️ Pull Models: {}", models.join(", "))); ai_info.add_str(format!(
"⬇️ {} {}",
"Pull Models:".paint(Color::Yellow),
models.join(", ")
));
} }
root_info.add_tree("🦙 Ollama", ai_info); root_info.add_tree("🦙 Ollama", ai_info);