Skip to content

Commit

Permalink
Support targeting installs to Determinate Nix Enterprise Edition. (#905)
Browse files Browse the repository at this point in the history
* Drop riff support

* Relocate the darwin diskutil structs to make room for keychain

* Clean up a few warnings

* Initial support for Determinate Nix Enterprise

* Don't create the org.nixos.darwin-store service under enterprise nix.

* Only write out systems.determinate.nix-daemon under nix enterprise

* Bump the nix crate from 0.27.0 to 0.28.0 for nix-rust/nix#2304

* Fixup the test fixture for macos

* Correct ConfigureInitService on Linux

* Fixup fixtures for linux

* Use Determinate Nix for macOS to perform the mount during installation.

* Force-enable encryption for Nix Enterprise on macOS

* fixup

* drop extraneous comment

* Create a separate CreateNixEnterpriseVolume to simplify CreateNixVolume.

* fixup

* Relocate the nix-enterprise flag down into the macOS planner, since our planning requires no changes on other targets at the moment.

* Fail the install if --nix-enterprise is passed but isn't available

* fixup nix_enterprise bits

* I hate this.

* Rename to be Determinate Nix Enterprise Edition, to be more consistent across tools and products.

* Note why we match on enterprise edition in the macos planner for the otherwise no-op enterprise_editition encryption case

* fixup

* Factor out the enterprise init service setup into its own module

* Conditionally import / use EE init service

* nit

* derp

* fixup: too many args to plan

* ...derp...
  • Loading branch information
grahamc authored Apr 15, 2024
1 parent cba481b commit 1c4976a
Show file tree
Hide file tree
Showing 18 changed files with 687 additions and 99 deletions.
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 1 addition & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ license = "LGPL-2.1"
repository = "https://github.com/DeterminateSystems/nix-installer"
documentation = "https://docs.rs/nix-installer/latest/nix_installer"

[package.metadata.riff.targets.aarch64-apple-darwin]
build-inputs = ["darwin.apple_sdk.frameworks.Security"]

[package.metadata.riff.targets.x86_64-apple-darwin]
build-inputs = ["darwin.apple_sdk.frameworks.Security"]

[features]
default = ["cli", "diagnostics"]
cli = ["eyre", "color-eyre", "clap", "tracing-subscriber", "tracing-error"]
Expand All @@ -30,7 +24,7 @@ clap = { version = "4", features = ["std", "color", "usage", "help", "error-cont
color-eyre = { version = "0.6.2", default-features = false, features = [ "track-caller", "issue-url", "tracing-error", "capture-spantrace", "color-spantrace" ], optional = true }
eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ], optional = true }
glob = { version = "0.3.0", default-features = false }
nix = { version = "0.27.0", default-features = false, features = ["user", "fs", "process", "term"] }
nix = { version = "0.28.0", default-features = false, features = ["user", "fs", "process", "term"] }
owo-colors = { version = "4.0.0", default-features = false, features = [ "supports-colors" ] }
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls-native-roots", "stream", "socks"] }
serde = { version = "1.0.144", default-features = false, features = [ "std", "derive" ] }
Expand Down
188 changes: 188 additions & 0 deletions src/action/common/configure_enterprise_edition_init_service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use std::path::PathBuf;

#[cfg(target_os = "macos")]
use serde::{Deserialize, Serialize};
#[cfg(target_os = "macos")]
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use tracing::{span, Span};

use crate::action::{ActionError, ActionErrorKind, ActionTag, StatefulAction};
use crate::execute_command;

use crate::action::{Action, ActionDescription};

#[cfg(target_os = "macos")]
const DARWIN_ENTERPRISE_EDITION_DAEMON_DEST: &str =
"/Library/LaunchDaemons/systems.determinate.nix-daemon.plist";
/**
Configure the init to run the Nix daemon
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureEnterpriseEditionInitService {
start_daemon: bool,
}

impl ConfigureEnterpriseEditionInitService {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(start_daemon: bool) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { start_daemon }.into())
}
}

#[async_trait::async_trait]
#[typetag::serde(name = "configure_enterprise_edition_init_service")]
impl Action for ConfigureEnterpriseEditionInitService {
fn action_tag() -> ActionTag {
ActionTag("configure_enterprise_edition_init_service")
}
fn tracing_synopsis(&self) -> String {
"Configure the Determinate Nix Enterprise Edition daemon related settings with launchctl"
.to_string()
}

fn tracing_span(&self) -> Span {
span!(
tracing::Level::DEBUG,
"configure_enterprise_edition_init_service"
)
}

fn execute_description(&self) -> Vec<ActionDescription> {
let mut explanation = vec![format!("Create `{DARWIN_ENTERPRISE_EDITION_DAEMON_DEST}`")];
if self.start_daemon {
explanation.push(format!(
"Run `launchctl load {DARWIN_ENTERPRISE_EDITION_DAEMON_DEST}`"
));
}

vec![ActionDescription::new(self.tracing_synopsis(), explanation)]
}

#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { start_daemon } = self;

let daemon_file = DARWIN_ENTERPRISE_EDITION_DAEMON_DEST;
let domain = "system";
let service = "systems.determinate.nix-daemon";

let generated_plist = generate_plist();

let mut options = tokio::fs::OpenOptions::new();
options.create(true).write(true).read(true);

let mut file = options
.open(&daemon_file)
.await
.map_err(|e| Self::error(ActionErrorKind::Open(PathBuf::from(daemon_file), e)))?;

let mut buf = Vec::new();
plist::to_writer_xml(&mut buf, &generated_plist).map_err(Self::error)?;
file.write_all(&buf)
.await
.map_err(|e| Self::error(ActionErrorKind::Write(PathBuf::from(daemon_file), e)))?;

execute_command(
Command::new("launchctl")
.process_group(0)
.args(["load", "-w"])
.arg(daemon_file)
.stdin(std::process::Stdio::null()),
)
.await
.map_err(Self::error)?;

let is_disabled = crate::action::macos::service_is_disabled(domain, service)
.await
.map_err(Self::error)?;
if is_disabled {
execute_command(
Command::new("launchctl")
.process_group(0)
.arg("enable")
.arg(&format!("{domain}/{service}"))
.stdin(std::process::Stdio::null()),
)
.await
.map_err(Self::error)?;
}

if *start_daemon {
execute_command(
Command::new("launchctl")
.process_group(0)
.arg("kickstart")
.arg("-k")
.arg(&format!("{domain}/{service}"))
.stdin(std::process::Stdio::null()),
)
.await
.map_err(Self::error)?;
}

Ok(())
}

fn revert_description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new(
"Unconfigure Nix daemon related settings with launchctl".to_string(),
vec![format!(
"Run `launchctl unload {DARWIN_ENTERPRISE_EDITION_DAEMON_DEST}`"
)],
)]
}

#[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> {
execute_command(
Command::new("launchctl")
.process_group(0)
.arg("unload")
.arg(DARWIN_ENTERPRISE_EDITION_DAEMON_DEST),
)
.await
.map_err(Self::error)?;

Ok(())
}
}

#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum ConfigureEnterpriseEditionNixDaemonServiceError {}

#[cfg(target_os = "macos")]
#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct DeterminateNixDaemonPlist {
label: String,
program: String,
keep_alive: bool,
run_at_load: bool,
standard_error_path: String,
standard_out_path: String,
soft_resource_limits: ResourceLimits,
}

#[cfg(target_os = "macos")]
#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct ResourceLimits {
number_of_files: usize,
}

#[cfg(target_os = "macos")]
fn generate_plist() -> DeterminateNixDaemonPlist {
DeterminateNixDaemonPlist {
keep_alive: true,
run_at_load: true,
label: "systems.determinate.nix-daemon".into(),
program: "/usr/local/bin/determinate-nix-ee".into(),
standard_error_path: "/var/log/determinate-nix-daemon.log".into(),
standard_out_path: "/var/log/determinate-nix-daemon.log".into(),
soft_resource_limits: ResourceLimits {
number_of_files: 1048576,
},
}
}
52 changes: 37 additions & 15 deletions src/action/common/configure_init_service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#[cfg(target_os = "linux")]
use std::path::Path;
use std::path::PathBuf;

#[cfg(target_os = "macos")]
use serde::{Deserialize, Serialize};
use tokio::process::Command;
use tracing::{span, Span};

Expand Down Expand Up @@ -130,7 +133,7 @@ impl Action for ConfigureInitService {
}

fn tracing_span(&self) -> Span {
span!(tracing::Level::DEBUG, "configure_init_service",)
span!(tracing::Level::DEBUG, "configure_init_service")
}

fn execute_description(&self) -> Vec<ActionDescription> {
Expand All @@ -152,7 +155,7 @@ impl Action for ConfigureInitService {
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
let mut explanation = vec![format!(
"Copy `{DARWIN_NIX_DAEMON_SOURCE}` to `DARWIN_NIX_DAEMON_DEST`"
"Copy `{DARWIN_NIX_DAEMON_SOURCE}` to `{DARWIN_NIX_DAEMON_DEST}`"
)];
if self.start_daemon {
explanation.push(format!("Run `launchctl load {DARWIN_NIX_DAEMON_DEST}`"));
Expand All @@ -172,30 +175,29 @@ impl Action for ConfigureInitService {
match init {
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
let daemon_file = DARWIN_NIX_DAEMON_DEST;
let domain = "system";
let service = "org.nixos.nix-daemon";
let src = std::path::Path::new(DARWIN_NIX_DAEMON_SOURCE);
tokio::fs::copy(src, DARWIN_NIX_DAEMON_DEST)
.await
.map_err(|e| {
Self::error(ActionErrorKind::Copy(
src.to_path_buf(),
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
e,
))
})?;

tokio::fs::copy(src, daemon_file).await.map_err(|e| {
Self::error(ActionErrorKind::Copy(
src.to_path_buf(),
PathBuf::from(daemon_file),
e,
))
})?;

execute_command(
Command::new("launchctl")
.process_group(0)
.args(["load", "-w"])
.arg(DARWIN_NIX_DAEMON_DEST)
.arg(daemon_file)
.stdin(std::process::Stdio::null()),
)
.await
.map_err(Self::error)?;

let domain = "system";
let service = "org.nixos.nix-daemon";

let is_disabled = crate::action::macos::service_is_disabled(domain, service)
.await
.map_err(Self::error)?;
Expand Down Expand Up @@ -525,6 +527,26 @@ pub enum ConfigureNixDaemonServiceError {
InitNotSupported,
}

#[cfg(target_os = "macos")]
#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct DeterminateNixDaemonPlist {
label: String,
program: String,
keep_alive: bool,
run_at_load: bool,
standard_error_path: String,
standard_out_path: String,
soft_resource_limits: ResourceLimits,
}

#[cfg(target_os = "macos")]
#[derive(Deserialize, Clone, Debug, Serialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct ResourceLimits {
number_of_files: usize,
}

#[cfg(target_os = "linux")]
async fn stop(unit: &str) -> Result<(), ActionErrorKind> {
let mut command = Command::new("systemctl");
Expand Down
4 changes: 4 additions & 0 deletions src/action/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! [`Action`](crate::action::Action)s which only call other base plugins

#[cfg(target_os = "macos")]
pub(crate) mod configure_enterprise_edition_init_service;
pub(crate) mod configure_init_service;
pub(crate) mod configure_nix;
pub(crate) mod configure_shell_profile;
Expand All @@ -9,6 +11,8 @@ pub(crate) mod delete_users;
pub(crate) mod place_nix_configuration;
pub(crate) mod provision_nix;

#[cfg(target_os = "macos")]
pub use configure_enterprise_edition_init_service::ConfigureEnterpriseEditionInitService;
pub use configure_init_service::{ConfigureInitService, ConfigureNixDaemonServiceError};
pub use configure_nix::ConfigureNix;
pub use configure_shell_profile::ConfigureShellProfile;
Expand Down
Loading

0 comments on commit 1c4976a

Please sign in to comment.