diff --git a/src/action/base/fetch_and_unpack_nix_substituter.rs b/src/action/base/fetch_and_unpack_nix_substituter.rs index 5b58df2..7c9f918 100644 --- a/src/action/base/fetch_and_unpack_nix_substituter.rs +++ b/src/action/base/fetch_and_unpack_nix_substituter.rs @@ -31,6 +31,8 @@ use crate::{ /// * NarHash must be sha256 #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct FetchAndUnpackNixSubstituter { + /// Whether to require valid signatures when checking narinfo + require_sigs: bool, /// Map from key name (e.g. cache.nixos.org-1) to parsed ed25519 key trusted_keys: HashMap, /// Base URLs for substituters, e.g. https://cache.nixos.org/ @@ -59,6 +61,7 @@ impl FetchAndUnpackNixSubstituter { targets: Vec, dest: PathBuf, trusted_keys: Vec, + require_sigs: bool, substituters: Vec, proxy: Option, ssl_cert_file: Option, @@ -86,7 +89,12 @@ impl FetchAndUnpackNixSubstituter { .collect::, _>>() .map_err(Self::error)?; + if !require_sigs { + tracing::warn!("Signatures are not required during substitution. This is insecure."); + } + Ok(Self { + require_sigs, targets, trusted_keys: trusted_keys_parsed, dest, @@ -122,6 +130,7 @@ impl FetchAndUnpackNixSubstituter { } let narinfo = NarInfo::parse_and_verify( + self.require_sigs, &self.trusted_keys, substituter, &output, @@ -458,7 +467,7 @@ struct NarInfo { /// Other store paths referenced by the nar pub references: Vec, /// Signature of the nar, used to sign other items - pub sig: (String, Vec), + pub sig: Option<(String, Vec)>, } impl NarInfo { @@ -527,7 +536,7 @@ impl NarInfo { nar_hash: nar_hash.ok_or_else(|| SubstitutionError::BadNarInfo)?, nar_size: nar_size.ok_or_else(|| SubstitutionError::BadNarInfo)?, references: references.ok_or_else(|| SubstitutionError::BadNarInfo)?, - sig: sig.ok_or_else(|| SubstitutionError::BadNarInfo)?, + sig, }) } @@ -535,7 +544,11 @@ impl NarInfo { &self, trusted_keys: &HashMap, ) -> Result<(), SubstitutionError> { - let Some(key) = trusted_keys.get(&self.sig.0) else { + let Some(sig) = &self.sig else { + return Err(SubstitutionError::BadSignature); + }; + + let Some(key) = trusted_keys.get(&sig.0) else { return Err(SubstitutionError::BadSignature); }; @@ -556,8 +569,7 @@ impl NarInfo { key.verify_strict( fingerprint.as_bytes(), &ed25519_dalek::Signature::from_bytes( - self.sig - .1 + sig.1 .as_slice() .try_into() .map_err(|_| SubstitutionError::BadSignature)?, @@ -567,6 +579,7 @@ impl NarInfo { } pub fn parse_and_verify( + require_sigs: bool, trusted_keys: &HashMap, substituter_url: &Url, expected_store_path: &StorePath, @@ -578,7 +591,9 @@ impl NarInfo { return Err(SubstitutionError::BadSignature); } - parsed.verify(trusted_keys)?; + if require_sigs { + parsed.verify(trusted_keys)?; + } Ok(parsed) } diff --git a/src/action/common/provision_nix.rs b/src/action/common/provision_nix.rs index 2d76cb3..b048ce0 100644 --- a/src/action/common/provision_nix.rs +++ b/src/action/common/provision_nix.rs @@ -62,6 +62,7 @@ impl ProvisionNix { settings.substitution_targets.clone(), PathBuf::from(SCRATCH_DIR), settings.substituter_trusted_keys.clone(), + settings.substituter_require_sigs, settings.substituters.clone(), settings.proxy.clone(), settings.ssl_cert_file.clone(), diff --git a/src/settings.rs b/src/settings.rs index c5e0067..9a6f377 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -192,7 +192,7 @@ pub struct CommonSettings { )] pub substituter_trusted_keys: Vec, - /// Output to download when use_substituters is set + /// Store paths to download when use_substituters is set. Should include lix and cacert #[cfg_attr( feature = "cli", clap(long, env = "NIX_INSTALLER_SUBSTITUTION_TARGETS",) @@ -223,6 +223,19 @@ pub struct CommonSettings { )] pub substitution_targets: Vec, + /// Require trusted signatures when downloading from substituters + #[cfg_attr( + feature = "cli", + clap( + action(ArgAction::SetFalse), + default_value = "true", + global = true, + env = "NIX_INSTALLER_REQUIRE_SIGS", + long = "no-require-sigs" + ) + )] + pub substituter_require_sigs: bool, + /// Download Lix from a substituter instead of an install tarball #[cfg_attr(feature = "cli", clap(long, env = "NIX_INSTALLER_USE_SUBSTITUTER"))] pub use_substituters: bool, @@ -387,6 +400,7 @@ impl CommonSettings { .iter() .map(|s| PathBuf::from(*s)) .collect(), + substituter_require_sigs: true, use_substituters: false, nix_package_url: url.parse()?, proxy: Default::default(), @@ -409,6 +423,7 @@ impl CommonSettings { substituters, substituter_trusted_keys, substitution_targets, + substituter_require_sigs, use_substituters, nix_package_url, proxy, @@ -443,7 +458,6 @@ impl CommonSettings { "nix_build_user_count".into(), serde_json::to_value(nix_build_user_count)?, ); - map.insert("substituters".into(), serde_json::to_value(substituters)?); map.insert( "substituter_trusted_keys".into(), serde_json::to_value(substituter_trusted_keys)?, @@ -452,6 +466,10 @@ impl CommonSettings { "substitution_targets".into(), serde_json::to_value(substitution_targets)?, ); + map.insert( + "substituter_require_sigs".into(), + serde_json::to_value(substituter_require_sigs)?, + ); map.insert( "use_substituters".into(), serde_json::to_value(use_substituters)?, @@ -460,6 +478,7 @@ impl CommonSettings { "nix_package_url".into(), serde_json::to_value(nix_package_url)?, ); + map.insert("substituters".into(), serde_json::to_value(substituters)?); map.insert("proxy".into(), serde_json::to_value(proxy)?); map.insert("ssl_cert_file".into(), serde_json::to_value(ssl_cert_file)?); map.insert("extra_conf".into(), serde_json::to_value(extra_conf)?); diff --git a/tests/fixtures/linux/linux.json b/tests/fixtures/linux/linux.json index ca01ac4..5eeef64 100644 --- a/tests/fixtures/linux/linux.json +++ b/tests/fixtures/linux/linux.json @@ -422,6 +422,7 @@ "substitution_targets": [ "/nix/store/rp7y16q2py2n9y19jvxkjr83lp77bh7y-lix-2.90.0" ], + "substituter_require_sigs": true, "use_substituters": false, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" diff --git a/tests/fixtures/linux/steam-deck.json b/tests/fixtures/linux/steam-deck.json index b9337ea..43bdfe3 100644 --- a/tests/fixtures/linux/steam-deck.json +++ b/tests/fixtures/linux/steam-deck.json @@ -406,6 +406,7 @@ "substitution_targets": [ "/nix/store/rp7y16q2py2n9y19jvxkjr83lp77bh7y-lix-2.90.0" ], + "substituter_require_sigs": true, "use_substituters": false, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz" diff --git a/tests/fixtures/macos/macos.json b/tests/fixtures/macos/macos.json index 998ca2e..aaec8f0 100644 --- a/tests/fixtures/macos/macos.json +++ b/tests/fixtures/macos/macos.json @@ -433,6 +433,7 @@ "substitution_target": [ "/nix/store/rp7y16q2py2n9y19jvxkjr83lp77bh7y-lix-2.90.0" ], + "substituter_require_sigs": true, "use_substituters": false, "nix_package_url": { "Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz"