Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ ambient-id = { version = "0.0.11", default-features = false }
anyhow = "1.0.98"
apple-native-keyring-store = { version = "1.0.0", default-features = false }
archspec = "0.2.0"
# TODO: switch to the crates.io release once arwen-codesign >= 0.1.0 is published
arwen-codesign = { git = "https://github.com/wolfv/arwen", branch = "claude/arwen-codesign-rattler-frrg3u" }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is released Id love to pull this in!

assert_matches = "1.5.0"
async-compression = { version = "0.4", features = [
"gzip",
Expand Down
1 change: 1 addition & 0 deletions crates/rattler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ features = ["cli-tools", "indicatif"]

[dependencies]
anyhow = { workspace = true }
arwen-codesign = { workspace = true }
base64 = { workspace = true }
jiff = { workspace = true }
clap = { workspace = true, optional = true }
Expand Down
57 changes: 53 additions & 4 deletions crates/rattler/src/install/apple_codesign.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
//! Code signing for Apple Silicon binaries
//! Code signing for macOS binaries.
//!
//! Prefix replacement modifies binary content, which invalidates any existing
//! code signature. macOS (and especially Apple Silicon, where valid
//! signatures are mandatory) then kills the binary on launch, so every
//! modified Mach-O binary must be re-signed with an ad-hoc signature.
//!
//! Signing happens in-process through the [`arwen_codesign`] crate. This is
//! orders of magnitude faster than spawning `/usr/bin/codesign` per binary
//! and also works when installing a macOS environment from a non-macOS host.
//! On macOS hosts, `/usr/bin/codesign` is kept as a fallback for anything the
//! in-process signer cannot handle.

use super::LinkFileError;
use arwen_codesign::{AdhocSignOptions, Entitlements};
use std::path::Path;

/// Controls the behavior of the [`super::link_package`] function when it encounters a binary that needs
Expand All @@ -16,10 +28,47 @@ pub enum AppleCodeSignBehavior {
Fail,
}

/// Sign a binary using the `codesign` tool with an ad-hoc certificate on macOS.
/// This is required for binaries to run on macOS when their signature has been invalidated
/// by prefix replacement (modifying binary content). The function preserves existing entitlements.
/// The signature identifier for a binary, derived from its file name (this is
/// also what `codesign --sign -` derives it from).
pub(crate) fn signing_identifier(destination_path: &Path) -> String {
destination_path.file_name().map_or_else(
|| String::from("rattler-signed"),
|name| name.to_string_lossy().into_owned(),
)
}

/// Sign a binary with an ad-hoc signature, the equivalent of
/// `codesign --sign - --force --preserve-metadata=entitlements`. This is
/// required for binaries to run on macOS when their signature has been
/// invalidated by prefix replacement (modifying binary content). The function
/// preserves existing entitlements.
///
/// Signing happens in-process (thin binaries are signed in a single streaming
/// pass, fat binaries per architecture slice). If that fails and the host is
/// macOS, `/usr/bin/codesign` is used as a fallback.
pub(crate) fn codesign(destination_path: &Path) -> Result<(), LinkFileError> {
let identifier = signing_identifier(destination_path);
let options = AdhocSignOptions::new(&identifier).with_entitlements(Entitlements::Preserve);

match arwen_codesign::adhoc_sign_file(destination_path, &options) {
Ok(()) => Ok(()),
Err(err) => {
tracing::warn!(
"in-process ad-hoc signing of {} failed: {err}",
destination_path.display()
);
codesign_fallback(destination_path)
}
}
}

/// Sign a binary by invoking the `/usr/bin/codesign` tool. Only available on
/// macOS hosts.
fn codesign_fallback(destination_path: &Path) -> Result<(), LinkFileError> {
if !cfg!(target_os = "macos") {
return Err(LinkFileError::FailedToSignAppleBinary);
}

let status = std::process::Command::new("/usr/bin/codesign")
.arg("--sign")
// Use an ad-hoc certificate (`-`)
Expand Down
Loading
Loading