diff --git a/bin/client/main.rs b/bin/client/main.rs index 8d70435..09dc208 100644 --- a/bin/client/main.rs +++ b/bin/client/main.rs @@ -125,9 +125,24 @@ async fn run() -> std::result::Result<(), ClientError> { PickYourAuth::None(NoToken) }; + let keyring_path: PathBuf = opts + .keyring + .unwrap_or_else(|| default_config_dir().join("keyring.toml")); + + if let Some(p) = keyring_path.parent() { + tokio::fs::create_dir_all(p).await?; + } + let keyring = Arc::new( + keyring_path + .load() + .await + .unwrap_or_else(|_| KeyRing::default()), + ); + let bindle_client = ClientBuilder::default() .danger_accept_invalid_certs(opts.insecure) - .build(&opts.server_url, token)?; + .verification_strategy(opts.strategy) + .build(&opts.server_url, token, keyring)?; let local = bindle::provider::file::FileProvider::new( bindle_dir, @@ -136,18 +151,6 @@ async fn run() -> std::result::Result<(), ClientError> { .await; let cache = DumbCache::new(bindle_client.clone(), local); - let keyring_path: PathBuf = opts - .keyring - .unwrap_or_else(|| default_config_dir().join("keyring.toml")); - - if let Some(p) = keyring_path.parent() { - tokio::fs::create_dir_all(p).await?; - } - let mut keyring = keyring_path - .load() - .await - .unwrap_or_else(|_| KeyRing::default()); - match opts.subcmd { SubCommand::Info(info_opts) => { let inv = match info_opts.yanked { @@ -362,7 +365,7 @@ async fn run() -> std::result::Result<(), ClientError> { Err(e) if matches!(e.kind(), std::io::ErrorKind::NotFound) => { println!("File {} does not exist. Creating it.", dir.display()); let mut keyfile = SecretKeyFile::default(); - let newkey = SecretKeyEntry::new(create_opts.label, roles); + let newkey = SecretKeyEntry::new(&create_opts.label, roles); keyfile.key.push(newkey.clone()); keyfile .save_file(dir) @@ -381,7 +384,7 @@ async fn run() -> std::result::Result<(), ClientError> { let mut keyfile = SecretKeyFile::load_file(&dir) .await .map_err(|e| ClientError::Other(e.to_string()))?; - let newkey = SecretKeyEntry::new(create_opts.label, roles); + let newkey = SecretKeyEntry::new(&create_opts.label, roles); keyfile.key.push(newkey.clone()); keyfile .save_file(dir) @@ -394,6 +397,10 @@ async fn run() -> std::result::Result<(), ClientError> { }; if !create_opts.skip_keyring { + let mut keyring = keyring_path + .load() + .await + .unwrap_or_else(|_| KeyRing::default()); keyring.add_entry(key.try_into()?); keyring_path .save(&keyring) @@ -402,6 +409,10 @@ async fn run() -> std::result::Result<(), ClientError> { } } Keys::Add(opts) => { + let mut keyring = keyring_path + .load() + .await + .unwrap_or_else(|_| KeyRing::default()); // First, check that the key is actually valid let raw = base64::decode(&opts.key) .map_err(|_| SignatureError::CorruptKey(opts.key.clone()))?; diff --git a/bin/client/opts.rs b/bin/client/opts.rs index c112800..2e2d396 100644 --- a/bin/client/opts.rs +++ b/bin/client/opts.rs @@ -1,3 +1,4 @@ +use bindle::VerificationStrategy; use clap::Parser; use std::path::PathBuf; @@ -67,6 +68,13 @@ pub struct Opts { )] pub insecure: bool, + #[clap( + long = "strategy", + help = "The verification strategy to use when pulling the bindle", + default_value = "MultipleAttestationGreedy[Host]" + )] + pub strategy: VerificationStrategy, + #[clap(subcommand)] pub subcmd: SubCommand, } diff --git a/bin/server.rs b/bin/server.rs index a352941..b80d403 100644 --- a/bin/server.rs +++ b/bin/server.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use std::path::PathBuf; use clap::Parser; -use tracing::{info, warn}; +use tracing::{debug, info, warn}; use bindle::{ invoice::signature::{KeyRing, SignatureRole}, @@ -201,8 +201,14 @@ async fn main() -> anyhow::Result<()> { // - or config file signing-keys entry // - or $XDG_DATA/bindle/signing-keys.toml let signing_keys: PathBuf = match config.signing_file { - Some(keypath) => keypath, - None => ensure_signing_keys().await?, + Some(keypath) => { + debug!(path = %keypath.display(), "Signing keys file was set, loading..."); + keypath + } + None => { + debug!("No signing key file set, attempting to load from default"); + ensure_signing_keys().await? + } }; // Map doesn't work here because we've already moved data out of opts @@ -414,7 +420,7 @@ async fn ensure_signing_keys() -> anyhow::Result { "Creating a default host signing key and storing it in {}", signing_keyfile.display() ); - let key = SecretKeyEntry::new("Default host key".to_owned(), vec![SignatureRole::Host]); + let key = SecretKeyEntry::new("Default host key", vec![SignatureRole::Host]); default_keyfile.key.push(key); default_keyfile .save_file(&signing_keyfile) diff --git a/examples/client.rs b/examples/client.rs index 26869cb..4c953ea 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -9,7 +9,11 @@ async fn main() -> Result<(), Box> { let root = std::env::var("CARGO_MANIFEST_DIR")?; let root_path = std::path::PathBuf::from(root); - let bindle_client = client::Client::new(&url, client::tokens::NoToken)?; + let bindle_client = client::Client::new( + &url, + client::tokens::NoToken, + std::sync::Arc::new(bindle::signature::KeyRing::default()), + )?; // Load an invoice manually and send it to the server println!("Creating invoice 1"); diff --git a/src/cache/lru.rs b/src/cache/lru.rs index b8b27e9..969afbb 100644 --- a/src/cache/lru.rs +++ b/src/cache/lru.rs @@ -411,7 +411,7 @@ mod test { // Make sure all the create operations pass through let provider = TestProvider::default(); let cache = LruCache::new(10, provider.clone()); - let sk = SecretKeyEntry::new("TEST".to_owned(), vec![SignatureRole::Proxy]); + let sk = SecretKeyEntry::new("TEST", vec![SignatureRole::Proxy]); let scaffold = testing::Scaffold::load("valid_v1").await; let verified = VerificationStrategy::MultipleAttestation(vec![]) diff --git a/src/client/mod.rs b/src/client/mod.rs index e388ef1..c61e46f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -7,6 +7,7 @@ pub mod tokens; use std::convert::TryInto; use std::path::Path; +use std::sync::Arc; use reqwest::header::{self, HeaderMap}; use reqwest::Client as HttpClient; @@ -16,8 +17,9 @@ use tracing::{debug, info, instrument, trace}; use url::Url; use crate::provider::{Provider, ProviderError}; -use crate::verification::Verified; -use crate::{Id, Signed}; +use crate::signature::{KeyRing, SignatureRole}; +use crate::verification::{Verified, VerifiedInvoice}; +use crate::{Id, Invoice, Signed, VerificationStrategy}; pub use error::ClientError; @@ -36,6 +38,9 @@ pub struct Client { client: HttpClient, base_url: Url, token_manager: T, + verification_strategy: VerificationStrategy, + // In an arc as the keyring could be fairly large and we don't want to clone it everywhere + keyring: Arc, } /// The operation being performed against a Bindle server. @@ -48,10 +53,22 @@ enum Operation { } /// A builder for for setting up a `Client`. Created using `Client::builder` -#[derive(Default)] pub struct ClientBuilder { http2_prior_knowledge: bool, danger_accept_invalid_certs: bool, + verification_strategy: VerificationStrategy, +} + +impl Default for ClientBuilder { + fn default() -> Self { + ClientBuilder { + http2_prior_knowledge: false, + danger_accept_invalid_certs: false, + verification_strategy: VerificationStrategy::MultipleAttestationGreedy(vec![ + SignatureRole::Host, + ]), + } + } } impl ClientBuilder { @@ -69,12 +86,30 @@ impl ClientBuilder { self } - /// Returns a new Client with the given URL and token manager, configured using the set options. + /// Sets the verification strategy this client should use. + /// + /// Defaults to MultipleAttestationGreedy(Host), which will validate that all signatures are + /// valid and will verify the signature of the host against the keyring + pub fn verification_strategy(mut self, verification_strategy: VerificationStrategy) -> Self { + self.verification_strategy = verification_strategy; + self + } + + /// Returns a new Client with the given URL, token manager, and keyring, configured using the + /// set options. /// /// This URL should be the FQDN plus any namespacing (like `v1`). So if you were running a /// bindle server mounted at the v1 endpoint, your URL would look something like /// `http://my.bindle.com/v1/`. Will return an error if the URL is not valid - pub fn build(self, base_url: &str, token_manager: T) -> Result> { + /// + /// This requires `KeyRing` to be wrapped in an `Arc` for efficiency sake. `KeyRing`s can be + /// quite large and so we require it to be wrapped in an Arc to avoid a large clone cost + pub fn build( + self, + base_url: &str, + token_manager: T, + keyring: Arc, + ) -> Result> { let (base_parsed, headers) = base_url_and_headers(base_url)?; let client = HttpClient::builder() .and_if(self.http2_prior_knowledge, |b| b.http2_prior_knowledge()) @@ -89,6 +124,8 @@ impl ClientBuilder { client, base_url: base_parsed, token_manager, + verification_strategy: self.verification_strategy, + keyring, }) } } @@ -113,8 +150,11 @@ impl Client { /// This URL should be the FQDN plus any namespacing (like `v1`). So if you were running a /// bindle server mounted at the v1 endpoint, your URL would look something like /// `http://my.bindle.com/v1/`. Will return an error if the URL is not valid - pub fn new(base_url: &str, token_manager: T) -> Result { - ClientBuilder::default().build(base_url, token_manager) + /// + /// This requires `KeyRing` to be wrapped in an `Arc` for efficiency sake. `KeyRing`s can be + /// quite large and so we require it to be wrapped in an Arc to avoid a large clone cost + pub fn new(base_url: &str, token_manager: T, keyring: Arc) -> Result { + ClientBuilder::default().build(base_url, token_manager, keyring) } /// Returns a [`ClientBuilder`](ClientBuilder) configured with defaults @@ -200,12 +240,16 @@ impl Client { //////////////// Get Invoice //////////////// - /// Returns the requested invoice from the bindle server if it exists. This can take any form - /// that can convert into the `Id` type, but generally speaking, this is the canonical name of - /// the bindle (e.g. `example.com/foo/1.0.0`). If you want to fetch a yanked invoice, use the - /// [`get_yanked_invoice`](Client::get_yanked_invoice) function + /// Returns the requested invoice from the bindle server if it exists. + /// + /// This can take any form that can convert into the `Id` type, but generally speaking, this is + /// the canonical name of the bindle (e.g. `example.com/foo/1.0.0`). If you want to fetch a + /// yanked invoice, use the [`get_yanked_invoice`](Client::get_yanked_invoice) function + /// + /// Once the invoice is fetched, it will be verified using the configured verification strategy + /// and keyring for this client #[instrument(level = "trace", skip(self, id), fields(invoice_id))] - pub async fn get_invoice(&self, id: I) -> Result + pub async fn get_invoice(&self, id: I) -> Result> where I: TryInto, I::Error: Into, @@ -221,7 +265,7 @@ impl Client { /// Same as `get_invoice` but allows you to fetch a yanked invoice #[instrument(level = "trace", skip(self, id), fields(invoice_id))] - pub async fn get_yanked_invoice(&self, id: I) -> Result + pub async fn get_yanked_invoice(&self, id: I) -> Result> where I: TryInto, I::Error: Into, @@ -235,13 +279,14 @@ impl Client { self.get_invoice_request(url).await } - async fn get_invoice_request(&self, url: Url) -> Result { + async fn get_invoice_request(&self, url: Url) -> Result> { let req = self.client.get(url); let req = self.token_manager.apply_auth_header(req).await?; trace!(?req); let resp = req.send().await?; let resp = unwrap_status(resp, Endpoint::Invoice, Operation::Get).await?; - Ok(toml::from_slice(&resp.bytes().await?)?) + let inv: Invoice = toml::from_slice(&resp.bytes().await?)?; + Ok(self.verification_strategy.verify(inv, &self.keyring)?) } //////////////// Query Invoice //////////////// @@ -495,6 +540,7 @@ impl Provider for Client { self.get_yanked_invoice(parsed_id) .await .map_err(|e| e.into()) + .map(|inv| inv.into()) } async fn yank_invoice(&self, id: I) -> crate::provider::Result<()> diff --git a/src/invoice/mod.rs b/src/invoice/mod.rs index f8e2987..790053f 100644 --- a/src/invoice/mod.rs +++ b/src/invoice/mod.rs @@ -455,8 +455,8 @@ mod test { .expect("If no signature, then this should verify fine"); // Create two signing keys. - let signer_name1 = "Matt Butcher ".to_owned(); - let signer_name2 = "Not Matt Butcher ".to_owned(); + let signer_name1 = "Matt Butcher "; + let signer_name2 = "Not Matt Butcher "; let keypair1 = SecretKeyEntry::new(signer_name1, vec![SignatureRole::Creator]); let keypair2 = SecretKeyEntry::new(signer_name2, vec![SignatureRole::Proxy]); diff --git a/src/invoice/signature.rs b/src/invoice/signature.rs index 69806b3..104e4bc 100644 --- a/src/invoice/signature.rs +++ b/src/invoice/signature.rs @@ -165,6 +165,7 @@ impl + Sync> KeyRingSaver for T { .await?; file.write_all(&toml::to_vec(keyring)?).await?; + file.flush().await?; Ok(()) } } @@ -341,12 +342,12 @@ pub struct SecretKeyEntry { } impl SecretKeyEntry { - pub fn new(label: String, roles: Vec) -> Self { + pub fn new(label: &str, roles: Vec) -> Self { let mut rng = rand::rngs::OsRng {}; let rawkey = Keypair::generate(&mut rng); let keypair = base64::encode(rawkey.to_bytes()); Self { - label, + label: label.to_owned(), keypair, roles, } @@ -489,10 +490,8 @@ mod test { async fn test_secret_keys() { let mut kr = SecretKeyFile::default(); assert_eq!(kr.key.len(), 0); - kr.key.push(SecretKeyEntry::new( - "test".to_owned(), - vec![SignatureRole::Proxy], - )); + kr.key + .push(SecretKeyEntry::new("test", vec![SignatureRole::Proxy])); assert_eq!(kr.key.len(), 1); let outdir = tempfile::tempdir().expect("created a temp dir"); diff --git a/src/invoice/verification.rs b/src/invoice/verification.rs index 76ee83d..93dbe8c 100644 --- a/src/invoice/verification.rs +++ b/src/invoice/verification.rs @@ -271,7 +271,8 @@ impl VerificationStrategy { } } -/// An invoice whose signatures have been verified. Can be converted borrowed as a plain [`Invoice`] +/// An invoice whose signatures have been verified. Can be converted or borrowed as a plain +/// [`Invoice`] pub struct VerifiedInvoice>(T); impl> Verified for VerifiedInvoice {} @@ -528,12 +529,10 @@ mod test { "#; let invoice: crate::Invoice = toml::from_str(invoice).expect("a nice clean parse"); - let key_creator = - SecretKeyEntry::new("Test Creator".to_owned(), vec![SignatureRole::Creator]); - let key_approver = - SecretKeyEntry::new("Test Approver".to_owned(), vec![SignatureRole::Approver]); - let key_host = SecretKeyEntry::new("Test Host".to_owned(), vec![SignatureRole::Host]); - let key_proxy = SecretKeyEntry::new("Test Proxy".to_owned(), vec![SignatureRole::Proxy]); + let key_creator = SecretKeyEntry::new("Test Creator", vec![SignatureRole::Creator]); + let key_approver = SecretKeyEntry::new("Test Approver", vec![SignatureRole::Approver]); + let key_host = SecretKeyEntry::new("Test Host", vec![SignatureRole::Host]); + let key_proxy = SecretKeyEntry::new("Test Proxy", vec![SignatureRole::Proxy]); let keyring_keys = vec![ key_approver.clone().try_into().expect("convert to pubkey"), key_host.clone().try_into().expect("convert to pubkey"), @@ -675,8 +674,7 @@ mod test { .expect("signed as creator"); // Mock an unknown key and don't add it to keyring - let key_anon = - SecretKeyEntry::new("Unknown key".to_owned(), vec![SignatureRole::Approver]); + let key_anon = SecretKeyEntry::new("Unknown key", vec![SignatureRole::Approver]); inv.sign(SignatureRole::Approver, &key_anon) .expect("signed with unknown key"); diff --git a/src/provider/file/mod.rs b/src/provider/file/mod.rs index a919638..96b069f 100644 --- a/src/provider/file/mod.rs +++ b/src/provider/file/mod.rs @@ -670,7 +670,7 @@ mod test { fn mock_secret_key() -> SecretKeyEntry { SecretKeyEntry::new( - "Bogo Key".to_owned(), + "Bogo Key", vec![ SignatureRole::Host, SignatureRole::Proxy, diff --git a/src/server/mod.rs b/src/server/mod.rs index 29059d5..1728482 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -302,7 +302,7 @@ mod test { KeyRing::default(), ); - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); // Insert an invoice let scaffold = testing::Scaffold::load("incomplete").await; @@ -380,7 +380,7 @@ mod test { ); let valid_raw = bindles.get("valid_v1").expect("Missing scaffold"); let valid = testing::Scaffold::from(valid_raw.clone()); - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); let verified = VerificationStrategy::MultipleAttestation(vec![]) .verify(valid.invoice.clone(), &KeyRing::default()) .unwrap(); @@ -432,7 +432,7 @@ mod test { let scaffold = testing::Scaffold::load("valid_v1").await; let parcel = scaffold.parcel_files.get("parcel").expect("Missing parcel"); let data = std::io::Cursor::new(parcel.data.clone()); - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); let verified = VerificationStrategy::MultipleAttestation(vec![]) .verify(scaffold.invoice.clone(), &KeyRing::default()) .unwrap(); @@ -469,7 +469,7 @@ mod test { let scaffold = testing::Scaffold::load("invalid").await; - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); let verified = VerificationStrategy::MultipleAttestation(vec![]) .verify(scaffold.invoice.clone(), &KeyRing::default()) .unwrap(); @@ -528,7 +528,7 @@ mod test { ); let bindles_to_insert = vec!["incomplete", "valid_v1", "valid_v2"]; - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); for b in bindles_to_insert.into_iter() { let current = testing::Scaffold::load(b).await; @@ -642,7 +642,7 @@ mod test { ); let scaffold = testing::Scaffold::load("lotsa_parcels").await; - let sk = SecretKeyEntry::new("test".to_owned(), vec![SignatureRole::Host]); + let sk = SecretKeyEntry::new("test", vec![SignatureRole::Host]); let verified = VerificationStrategy::MultipleAttestation(vec![]) .verify(scaffold.invoice.clone(), &KeyRing::default()) .unwrap(); diff --git a/src/standalone/mod.rs b/src/standalone/mod.rs index 1208f7d..4e54ea7 100644 --- a/src/standalone/mod.rs +++ b/src/standalone/mod.rs @@ -227,7 +227,10 @@ async fn create_or_get_invoice( } else { Some(missing) }; - Ok(crate::InvoiceCreateResponse { invoice, missing }) + Ok(crate::InvoiceCreateResponse { + invoice: invoice.into(), + missing, + }) } Err(e) => Err(e), } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index fb443c2..3073c65 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -255,7 +255,7 @@ impl Default for MockKeyStore { fn default() -> Self { MockKeyStore { mock_secret_key: SecretKeyEntry::new( - "Test ".to_owned(), + "Test ", vec![SignatureRole::Host], ), } diff --git a/tests/cli.rs b/tests/cli.rs index e7ea0bc..62f85f2 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -150,6 +150,7 @@ async fn test_get() { "enterprise.com/warpcore/1.0.0", ]) .env(ENV_BINDLE_URL, &controller.base_url) + .env(ENV_BINDLE_KEYRING, &controller.keyring_path) .output() .expect("Should be able to run command"); @@ -170,6 +171,7 @@ async fn test_get() { "enterprise.com/warpcore/1.0.0", ]) .env(ENV_BINDLE_URL, &controller.base_url) + .env(ENV_BINDLE_KEYRING, &controller.keyring_path) .output() .expect("Should be able to run command"); @@ -193,6 +195,7 @@ async fn test_get() { "enterprise.com/warpcore/1.0.0", ]) .env(ENV_BINDLE_URL, &controller.base_url) + .env(ENV_BINDLE_KEYRING, &controller.keyring_path) .output() .expect("Should be able to run command"); @@ -233,6 +236,7 @@ async fn test_get_invoice() { "enterprise.com/warpcore/1.0.0", ]) .env(ENV_BINDLE_URL, &controller.base_url) + .env(ENV_BINDLE_KEYRING, &controller.keyring_path) .output() .expect("Should be able to run command"); diff --git a/tests/standalone.rs b/tests/standalone.rs index ede82f9..42eb9ea 100644 --- a/tests/standalone.rs +++ b/tests/standalone.rs @@ -159,10 +159,7 @@ async fn test_push() { .await .expect("Should be able to read standalone bindle"); - let client = bindle::client::Client::new(&controller.base_url, bindle::client::tokens::NoToken) - .expect("Invalid client config"); - - read.push(&client) + read.push(&controller.client) .await .expect("Should be able to push successfully"); @@ -181,10 +178,7 @@ async fn test_push_tarball() { .await .expect("Should be able to read standalone bindle"); - let client = bindle::client::Client::new(&controller.base_url, bindle::client::tokens::NoToken) - .expect("Invalid client config"); - - read.push(&client) + read.push(&controller.client) .await .expect("Should be able to push successfully"); diff --git a/tests/test_util.rs b/tests/test_util.rs index 918e94a..59403e3 100644 --- a/tests/test_util.rs +++ b/tests/test_util.rs @@ -1,6 +1,9 @@ use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; +use std::path::PathBuf; +use std::sync::Arc; use bindle::client::{tokens::NoToken, Client}; +use bindle::signature::{KeyRing, KeyRingSaver, SecretKeyEntry, SecretKeyFile, SignatureRole}; #[allow(dead_code)] pub const ENV_BINDLE_URL: &str = "BINDLE_URL"; @@ -14,6 +17,8 @@ pub const BINARY_NAME: &str = "bindle-server.exe"; pub struct TestController { pub client: Client, pub base_url: String, + pub keyring: KeyRing, + pub keyring_path: PathBuf, server_handle: std::process::Child, // Keep a handle to the tempdir so it doesn't drop until the controller drops _tempdir: tempfile::TempDir, @@ -44,6 +49,24 @@ impl TestController { let address = format!("127.0.0.1:{}", get_random_port()); let base_url = format!("http://{}/v1/", address); + // Create the host key + let secret_file_path = tempdir.path().join("secret_keys.toml"); + let key = SecretKeyEntry::new("test ", vec![SignatureRole::Host]); + let mut secret_file = SecretKeyFile::default(); + secret_file.key.push(key.clone()); + secret_file + .save_file(&secret_file_path) + .await + .expect("Unable to save host key"); + + let mut keyring = KeyRing::default(); + keyring.add_entry(key.try_into().unwrap()); + + let keyring_path = tempdir.path().join("keyring.toml"); + keyring_path + .save(&keyring) + .await + .expect("Unable to save keyring to disk"); let server_handle = std::process::Command::new( std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) @@ -57,6 +80,7 @@ impl TestController { "-i", address.as_str(), ]) + .env("BINDLE_SIGNING_KEYS", secret_file_path) .spawn() .expect("unable to start bindle server"); @@ -77,10 +101,13 @@ impl TestController { } } - let client = Client::new(&base_url, NoToken).expect("unable to setup bindle client"); + let client = Client::new(&base_url, NoToken, Arc::new(keyring.clone())) + .expect("unable to setup bindle client"); TestController { client, base_url, + keyring, + keyring_path, server_handle, _tempdir: tempdir, }