Compare commits

..

No commits in common. "3ae066d593cdb18e2b8a0824ac319bd5f7c068e1" and "f2358676000c70c7ae1ae7b17c16f6c79433557d" have entirely different histories.

21 changed files with 133 additions and 254 deletions

25
Cargo.lock generated
View file

@ -130,12 +130,6 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "libc"
version = "0.2.169"
@ -155,7 +149,6 @@ dependencies = [
"clap",
"nix",
"serde",
"serde_json",
"toml",
"yansi",
]
@ -190,12 +183,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.216"
@ -216,18 +203,6 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"

View file

@ -7,6 +7,5 @@ edition = "2024"
clap = { version = "4.5.23", features = ["cargo"] }
nix = { version = "0.29.0", features = ["user"] }
serde = { version = "1.0.216", features = ["derive"] }
serde_json = "1.0.135"
toml = "0.8.19"
yansi = "1.0.1"

View file

@ -1,29 +1,59 @@
# Desktop Install Template
# Drive Selection for Install
[drive]
# Device node for the EFI boot filesystem
boot = "/dev/null"
# Device node for the root filesystem
root = "/dev/null"
# Root filesystem encryption passphrase
# If this option is set the root filesystem will be encrypted with LUKS
encryption = "password"
# General configuration
[general]
# Preset
mode = "Desktop"
# System Locale
locale = "de_DE.UTF-8"
# Keymap
keyboard_layout = "de"
keyboard_variant = "mac"
# Timezone
timezone = "Europe/Berlin"
# Hostname
hostname = "navos"
# Root password
root_password = "root"
# Enable Bluetooth
bluetooth = true
# GPU Video Drivers
gpu_driver = "NVIDIA"
firewall = true
[pkg]
# Additional packages
pkg = [
"nano",
"micro"
]
# User configuration
# The `[[user]]` directive can be repeated to create multiple users.
[[user]]
# Username
name = "u"
# User password
password = "pass"
# Add user to wheel group
wheel = true

View file

@ -15,10 +15,6 @@ encryption = "password"
# General configuration
[general]
# Preset
# Available options:
# - `Base`: Basic Arch Linux Installation
# - `Desktop`: navOS Desktop Installation
# - `Server`: navOS Server Installation
mode = "Desktop"
# System Locale
@ -41,12 +37,8 @@ root_password = "root"
bluetooth = true
# GPU Video Drivers
# Available options: `NVIDIA`, `AMD`, `INTEL`
gpu_driver = "NVIDIA"
# Enable firewall
firewall = true
[pkg]
# Additional packages
pkg = [

View file

@ -1,19 +1,38 @@
# Minimal Install Template
# Drive Selection for Install
[drive]
# Device node for the EFI boot filesystem
boot = "/dev/null"
# Device node for the root filesystem
root = "/dev/null"
# Root filesystem encryption passphrase
# If this option is set the root filesystem will be encrypted with LUKS
encryption = "password"
# General configuration
[general]
# Preset
mode = "Base"
# System Locale
locale = "de_DE.UTF-8"
# Keymap
keyboard_layout = "de"
keyboard_variant = "mac"
# Timezone
timezone = "Europe/Berlin"
# Hostname
hostname = "navos_min"
# Root password
root_password = "root"
firewall = false
[pkg]
# Additional packages
pkg = []

View file

@ -1,36 +1,79 @@
# Server Install Template
# Drive Selection for Install
[drive]
# Device node for the EFI boot filesystem
boot = "/dev/null"
# Device node for the root filesystem
root = "/dev/null"
# Root filesystem encryption passphrase
# If this option is set the root filesystem will be encrypted with LUKS
encryption = "password"
# General configuration
[general]
# Preset
mode = "Server"
# System Locale
locale = "de_DE.UTF-8"
# Keymap
keyboard_layout = "de"
keyboard_variant = "mac"
# Timezone
timezone = "Europe/Berlin"
# Hostname
hostname = "navos"
# Root password
root_password = "root"
firewall = true
[pkg]
# Additional packages
pkg = []
# Enable virtualization
virtualization = true
# Enable docker
docker = true
# User configuration
# The `[[user]]` directive can be repeated to create multiple users.
[[user]]
# Username
name = "u"
# User password
password = "pass"
# Add user to wheel group
wheel = true
# Add user to Docker group
docker = true
# Add user to libvirt group
virtualization = true
# SSH Configuration
# If `[ssh]` is set, openssh will be installed and enabled.
[ssh]
# Config file for sshd
# This file will be copied to the new system
sshd_config = "/etc/ssh/sshd_config"
# Install a SSH keys
# To set multiple keys, repeat the `[[ssh.key]]` directive.
# Every key will be installed in the users respective `authorized_keys` file.
[[ssh.key]]
# The SSH Key
key = "ssh-rsa ... user@host"
# The users allowed to login with this key
users = ["u", "root"]

View file

@ -88,16 +88,14 @@ pub struct GeneralConfig {
// Enable Bluetooth
pub bluetooth: Option<bool>,
/// Install Video Driver
pub gpu_driver: Option<GPUVendor>,
// Eanble firewall
pub firewall: Option<bool>,
pub gpu_driver: Option<GPUVendor>
}
#[derive(Debug, Clone, Deserialize)]
pub enum GPUVendor {
AMD,
NVIDIA,
INTEL,
INTEL
}
#[derive(Debug, Clone, Deserialize)]

View file

@ -1,13 +1,10 @@
use yansi::{Color, Paint};
use crate::{config::DriveConfig, linux::run_command, print_status};
use crate::{config::DriveConfig, linux::run_command};
// TODO : Add support for using entire block device
/// Format the drives with the given config
pub fn format_drives(conf: &DriveConfig) {
disk_safe_check(&conf.root);
disk_safe_check(&conf.root);
// TODO : Safety checks !!!
// EFI (BOOT)
run_command(&["mkfs.vfat", "-F", "32", conf.boot.as_str()], None, false);
@ -55,59 +52,3 @@ pub fn mount_drives(conf: &DriveConfig) {
false,
);
}
/// Format a disk with EFI and BOOT partitions.
pub fn partition_disk(dev: &str) {
disk_safe_check(dev);
print_status(&format!("Formatting disk {dev}"));
let cmd = vec!["fdisk", dev];
let input = "g\nn\n1\n\n+1G\nt\n1\nn\n2\n\nw\n";
run_command(&cmd, Some(input), false);
}
/// Check if `dev` contains a filesystem and error if it does
pub fn disk_safe_check(dev: &str) {
if let Some(fs) = has_filesystem(dev) {
println!(
"{} Device {} already contains a filesystem {}",
"Error:".paint(Color::Red),
dev.paint(Color::Red),
fs.paint(Color::Blue)
);
std::process::exit(1);
}
}
pub fn has_filesystem(dev: &str) -> Option<String> {
let blockdevs: serde_json::Value =
serde_json::from_str(&run_command(&["lsblk", "--fs", "--json"], None, false).0).unwrap();
// TODO : Follow if symlink from /dev/disks
let dev = dev.trim_start_matches("/dev/");
let dev_entry = blockdevs
.as_object()
.unwrap()
.get("blockdevices")
.unwrap()
.as_array()
.unwrap()
.iter()
.find(|x| {
x.as_object()
.unwrap()
.get("name")
.unwrap()
.as_str()
.unwrap()
== dev
});
if let Some(dev_entry) = dev_entry {
if let Some(fs) = dev_entry.as_object().unwrap().get("fstype") {
return Some(fs.to_string());
}
}
None
}

View file

@ -1,22 +0,0 @@
use crate::{
linux::{arch_chroot, systemd_service_enable},
pkg::install_pkgs,
print_status,
};
/// Setup UFW
pub fn setup_firewall(ssh: bool) {
print_status("Enabling firewall");
install_pkgs(&["ufw"]);
systemd_service_enable("ufw.service");
arch_chroot(&["ufw", "default", "deny"], None, false);
if ssh {
arch_chroot(&["ufw", "allow", "22/tcp"], None, false);
arch_chroot(&["ufw", "limit", "ssh"], None, false);
}
arch_chroot(&["ufw", "enable"], None, false);
}

View file

@ -1,13 +1,11 @@
use crate::{linux::systemd_service_enable, pkg::install_pkgs, print_status};
/// Setup firmware update daemon
pub fn setup_fwupd() {
print_status("Enabling firmware updates");
install_pkgs(&["fwupd"]);
systemd_service_enable("fwupd-refresh.timer");
}
/// Setup CPU Microcode
pub fn setup_microcode() {
print_status("Installing CPU Microcode");
let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").unwrap();

View file

@ -45,14 +45,7 @@ pub fn first_boot_values(conf: &GeneralConfig) {
print_status("Writing /etc/hostname");
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,
);
install_file("/mnt/etc/hosts", &format!("127.0.0.1 localhost\n::1 localhost\n127.0.1.1 {}\n", conf.hostname), 0o644);
// LOCALE
print_status("Setting locale");

View file

@ -1,28 +1,15 @@
use crate::{config::GPUVendor, pkg::install_pkgs};
/// Setup GPU video drivers
pub fn setup_video_drivers(vendor: &GPUVendor) {
match vendor {
GPUVendor::AMD => {
install_pkgs(&[
"xf86-video-amdgpu",
"mesa",
"lib32-mesa",
"vulkan-radeon",
"lib32-vulkan-radeon",
]);
}
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",
]);
}
install_pkgs(&["xf86-video-intel2", "mesa", "lib32-mesa", "vulkan-intel", "lib32-vulkan-intel"]);
},
}
}

View file

@ -6,7 +6,6 @@ use crate::{
print_status,
};
/// Setup initramfs
pub fn setup_mkinitcpio(conf: &DriveConfig) {
print_status("Writing /etc/mkinitcpio.d/linux.preset");
install_file(

View file

@ -1,5 +1,6 @@
// TODO : Autojoin docker swarm
// TODO : Autojoin teleport
// TODO : Firewall
// DRIVE SELECTION
@ -8,7 +9,6 @@ use boot::setup_bootloader;
use desktop::setup_desktop;
use docker::setup_docker;
use drives::{format_drives, mount_drives};
use firewall::setup_firewall;
use firmware::{setup_fwupd, setup_microcode};
use first_boot::{first_boot_values, genfstab};
use gpu::setup_video_drivers;
@ -28,10 +28,7 @@ pub mod boot;
pub mod desktop;
pub mod docker;
pub mod drives;
pub mod firewall;
pub mod firmware;
pub mod first_boot;
pub mod gpu;
pub mod kernel;
pub mod navos;
pub mod ollama;
@ -41,22 +38,14 @@ pub mod ssh;
pub mod user;
pub mod virt;
pub mod zram;
pub mod firmware;
pub mod gpu;
use crate::{
config::InstallConfig,
pkg::{self, install_pkgs, pacstrap},
};
/// Uncomment the first occurrence of a specified value in a file.
///
/// This function searches for the first line in the specified file that contains
/// the given `value` string. If the line is commented out with a `#` symbol,
/// it removes the `#` and updates the file.
///
/// # Arguments
///
/// * `value` - A string that specifies the value to search for in the file.
/// * `file` - A string specifying the path to the file.
pub fn uncomment_first_value_of(value: &str, file: &str) {
// read in the file
let content = std::fs::read_to_string(file).unwrap();
@ -77,15 +66,6 @@ pub fn uncomment_first_value_of(value: &str, file: &str) {
std::fs::write(file, new).unwrap();
}
/// Uncomment lines that start with a specified tag in a file.
///
/// This function processes all lines in the specified file. For any line that starts
/// with the provided `tag`, it removes the `tag` and updates the file.
///
/// # Arguments
///
/// * `tag` - A string specifying the prefix tag to remove from matching lines.
/// * `file` - A string specifying the path to the file.
pub fn uncomment_tag(tag: &str, file: &str) {
// read in the file
let content = std::fs::read_to_string(file).unwrap();
@ -115,13 +95,15 @@ pub fn install(conf: InstallConfig) {
pacstrap(&conf.pkg);
genfstab();
// Configuration
// System Setup
first_boot_values(&conf.general);
setup_skel(&conf.general);
setup_users(&conf.user.as_ref().unwrap_or(&Vec::new()));
setup_ssh(&conf.ssh);
// Presets
setup_bootloader();
match conf.general.mode {
crate::config::InstallMode::Base => {}
crate::config::InstallMode::Desktop => {
@ -136,8 +118,6 @@ pub fn install(conf: InstallConfig) {
}
}
// Applications
if conf.pkg.virtualization.unwrap_or_default() {
setup_virtualization(&conf);
}
@ -156,24 +136,18 @@ pub fn install(conf: InstallConfig) {
setup_ollama(&ai);
}
// Connectivity
setup_zram();
if conf.general.bluetooth.unwrap_or_default() {
setup_bluetooth();
}
if conf.general.firewall.unwrap_or(true) {
setup_firewall(conf.ssh.is_some());
}
// System
setup_zram();
setup_vm();
if let Some(gpu_vendor) = &conf.general.gpu_driver {
setup_video_drivers(gpu_vendor);
}
setup_fwupd();
setup_microcode();
setup_bootloader();
setup_mkinitcpio(&conf.drive);
setup_secure_boot();
@ -181,5 +155,5 @@ pub fn install(conf: InstallConfig) {
setup_tpm_unlock(&conf.drive);
}
println!("{}", "System install complete".bold().paint(Color::Green));
println!("{}", "System install complete".paint(Color::Green));
}

View file

@ -1,9 +1,4 @@
use crate::{
config::OllamaConfig,
linux::{arch_chroot, systemd_service_enable},
pkg::install_pkgs,
print_status,
};
use crate::{config::OllamaConfig, linux::systemd_service_enable, pkg::install_pkgs};
/// Setup Ollama AI Service
pub fn setup_ollama(conf: &OllamaConfig) {
@ -15,20 +10,7 @@ pub fn setup_ollama(conf: &OllamaConfig) {
systemd_service_enable("ollama.service");
print_status("Pulling Models");
let mut ollama_server = std::process::Command::new("arch-chroot")
.arg("/mnt")
.arg("runuser -u ollama -- env OLLAMA_MODELS=/var/lib/ollama HOME=/var/lib/ollama /usr/bin/ollama serve")
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start ollama server");
let models = conf.models.clone().unwrap_or_default();
for model in models {
arch_chroot(&["ollama", "pull", &model], None, true);
for model in conf.models.clone().unwrap_or_default() {
// TODO : Pull models
}
ollama_server.kill().unwrap();
}

View file

@ -1,9 +1,4 @@
use crate::{
config::SSHConfig,
linux::{install_file, systemd_service_enable},
pkg::install_pkgs,
print_status,
};
use crate::{config::SSHConfig, linux::install_file, pkg::install_pkgs, print_status};
use std::io::Write;
/// Setup SSH on the system
@ -39,7 +34,5 @@ pub fn setup_ssh(conf: &Option<SSHConfig>) {
writeln!(authorized_keys, "{}\n", key.key).unwrap();
}
}
systemd_service_enable("sshd.service");
}
}

View file

@ -1,8 +1,5 @@
use crate::{
config::UserConfig,
linux::{arch_chroot, install_file},
pkg::install_pkgs,
print_status,
config::UserConfig, linux::{arch_chroot, install_file}, pkg::install_pkgs, print_status
};
pub fn change_passwd(user: &str, pw: &str) {

View file

@ -1,8 +1,7 @@
use crate::{
config::{InstallConfig, InstallMode},
linux::{arch_chroot, run_command, systemd_service_enable},
linux::{arch_chroot, systemd_service_enable},
pkg::install_pkgs,
print_status,
};
pub fn setup_virtualization(conf: &InstallConfig) {
@ -31,19 +30,6 @@ pub fn setup_virtualization(conf: &InstallConfig) {
}
}
/// Setup guest utils if running inside a VM
pub fn setup_vm() {
let is_vm = run_command(&["systemd-detect-virt", "--vm"], None, false)
.0
.trim();
match is_vm {
"qemu" | "kvm" => {
print_status("Detected KVM. Installing utils");
install_pkgs(&["qemu-guest-agent", "spice-vdagent"]);
systemd_service_enable("qemu-guest-agent.service");
systemd_service_enable("spice-vdagentd.service");
}
_ => {}
}
// 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();
let permissions = std::fs::Permissions::from_mode(permissions);
print_status(&format!("Wrote file {path} [{:o}]", permissions.mode()));
// TODO : Fix permission format
print_status(&format!("Wrote file {path} [{permissions:#?}]"));
std::fs::set_permissions(path, permissions).unwrap();
}

View file

@ -3,16 +3,18 @@ use crate::{
linux::{arch_chroot, run_command},
};
pub const DESKTOP_PKG: [&str; 17] = [
pub const DESKTOP_PKG: [&str; 16] = [
// Desktop
"plasma",
"sddm",
// Sound
"pipewire",
"pipewire-alsa",
"pipewire-pulse",
"pipewire-jack",
"wireplumber",
// Applications
"konsole",
"dolphin",
@ -22,9 +24,9 @@ pub const DESKTOP_PKG: [&str; 17] = [
"gwenview",
"ark",
"flatpak",
// Misc
"navos/navos",
"man",
"navos/navos"
];
pub const SERVER_PKG: [&str; 2] = ["tmux", "navos/navos"];
@ -58,6 +60,7 @@ pub fn pacstrap(conf: &PackageConfig) {
"zsh",
"zsh-completions",
"zsh-autosuggestions",
"man"
];
cmd.extend(

View file

@ -65,20 +65,11 @@ pub fn print_config(conf: &InstallConfig) {
}
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)))
}
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))),
}
}
if conf.general.firewall.unwrap_or(true) {
general_info.add_str(format!("🔥 Firewall {}", "✔️".paint(Color::Green)));
}
root_info.add_tree("🔨 General", general_info);