Skip to content

Commit

Permalink
Make nix-health trusted-users check recognize groups and all-users (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
aravindgopall authored Dec 16, 2023
1 parent 3ca8f2d commit 78ef91a
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 7 deletions.
31 changes: 27 additions & 4 deletions crates/nix_health/src/check/trusted_users.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::collections::HashSet;

use nix_rs::config::TrustedUserValue;
use serde::{Deserialize, Serialize};

use crate::traits::*;
Expand All @@ -13,11 +16,10 @@ impl Checkable for TrustedUsers {
nix_info: &nix_rs::info::NixInfo,
_: Option<nix_rs::flake::url::FlakeUrl>,
) -> Vec<Check> {
let val = &nix_info.nix_config.trusted_users.value;
let current_user = &nix_info.nix_env.current_user;
let result = if val.contains(current_user) {
let result = if is_current_user_trusted(nix_info) {
CheckResult::Green
} else {
let current_user = &nix_info.nix_env.current_user;
let msg = format!("User '{}' not present in trusted_users", current_user);
let suggestion = match nix_info.nix_env.os.nix_system_config_label() {
Some(conf_label) => format!(
Expand All @@ -33,10 +35,31 @@ impl Checkable for TrustedUsers {
};
let check = Check {
title: "Trusted Users".to_string(),
info: format!("trusted-users = {}", val.join(" ")),
info: format!(
"trusted-users = {}",
TrustedUserValue::display_original(&nix_info.nix_config.trusted_users.value)
),
result,
required: true,
};
vec![check]
}
}

/// Check that [crate::nix::config::NixConfig::trusted_users] is set to a good
/// value such that the current user is trusted.
fn is_current_user_trusted(nix_info: &nix_rs::info::NixInfo) -> bool {
let current_user = &nix_info.nix_env.current_user;
let current_user_groups: HashSet<&String> =
nix_info.nix_env.current_user_groups.iter().collect();
nix_info
.nix_config
.trusted_users
.value
.iter()
.any(|x| match x {
TrustedUserValue::Group(x) => current_user_groups.contains(&x),
TrustedUserValue::User(x) => x == current_user,
TrustedUserValue::All => true,
})
}
55 changes: 53 additions & 2 deletions crates/nix_rs/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Rust module for `nix show-config`
use serde::{Deserialize, Serialize};
use std::{convert::Infallible, str::FromStr};

use serde::{Deserialize, Serialize};
use serde_with::DeserializeFromStr;
use tracing::instrument;
use url::Url;

Expand All @@ -18,7 +20,7 @@ pub struct NixConfig {
pub max_jobs: ConfigVal<i32>,
pub substituters: ConfigVal<Vec<Url>>,
pub system: ConfigVal<System>,
pub trusted_users: ConfigVal<Vec<String>>,
pub trusted_users: ConfigVal<Vec<TrustedUserValue>>,
}

/// The value for each 'nix show-config --json' key.
Expand Down Expand Up @@ -47,6 +49,55 @@ impl NixConfig {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, DeserializeFromStr)]
pub enum TrustedUserValue {
/// All users are trusted
All,
/// A specific user is trusted
User(String),
/// Users belonging to a specific group are trusted
Group(String),
}

impl TrustedUserValue {
fn from_str(s: &str) -> Self {
// In nix.conf, groups are prefixed with '@'. '*' means all users are
// trusted.
if s == "*" {
return Self::All;
}
match s.strip_prefix('@') {
Some(s) => Self::Group(s.to_string()),
None => Self::User(s.to_string()),
}
}

pub fn display_original(val: &[TrustedUserValue]) -> String {
val.iter()
.map(|x| match x {
TrustedUserValue::All => "*".to_string(),
TrustedUserValue::User(x) => x.to_string(),
TrustedUserValue::Group(x) => format!("@{}", x),
})
.collect::<Vec<String>>()
.join(" ")
}
}

impl From<String> for TrustedUserValue {
fn from(s: String) -> Self {
Self::from_str(&s)
}
}

impl FromStr for TrustedUserValue {
type Err = Infallible;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_str(s))
}
}

#[tokio::test]
async fn test_nix_config() -> Result<(), crate::command::NixCmdError> {
let v = NixConfig::from_nix(&crate::command::NixCmd::default()).await?;
Expand Down
22 changes: 21 additions & 1 deletion crates/nix_rs/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ use std::{fmt::Display, path::Path};
use bytesize::ByteSize;
use os_info;
use serde::{Deserialize, Serialize};
use std::process::Command;
use tracing::instrument;

/// The environment in which Nix operates
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NixEnv {
/// Current user ($USER)
pub current_user: String,
/// Current user groups
pub current_user_groups: Vec<String>,
/// Underlying OS in which Nix runs
pub os: OS,
/// Total disk space of the volume where /nix exists.
Expand All @@ -36,8 +39,10 @@ impl NixEnv {
);
let total_disk_space = to_bytesize(get_nix_disk(&sys)?.total_space());
let total_memory = to_bytesize(sys.total_memory());
let current_user_groups = get_current_user_groups()?;
Ok(NixEnv {
current_user,
current_user_groups,
os,
total_disk_space,
total_memory,
Expand All @@ -48,8 +53,20 @@ impl NixEnv {
}
}

/// Get the disk where /nix exists
/// Get the current user's groups
fn get_current_user_groups() -> Result<Vec<String>, NixEnvError> {
let output = Command::new("groups")
.output()
.map_err(NixEnvError::GroupsError)?;
let group_info = &String::from_utf8_lossy(&output.stdout);
Ok(group_info
.as_ref()
.split_whitespace()
.map(|v| v.to_string())
.collect())
}

/// Get the disk where /nix exists
fn get_nix_disk(sys: &sysinfo::System) -> Result<&sysinfo::Disk, NixEnvError> {
use sysinfo::{DiskExt, SystemExt};
let by_mount_point: std::collections::HashMap<&Path, &sysinfo::Disk> = sys
Expand Down Expand Up @@ -186,6 +203,9 @@ pub enum NixEnvError {
#[error("Failed to fetch ENV: {0}")]
EnvVarError(#[from] std::env::VarError),

#[error("Failed to fetch groups: {0}")]
GroupsError(std::io::Error),

#[error("Unable to find root disk or /nix volume")]
NoDisk,
}
Expand Down

0 comments on commit 78ef91a

Please sign in to comment.