-
-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Find what linux-keystore is supported #123
Comments
Hi @wolfv thanks for the thanks :)! If you are running on Linux then it seems you could always use the Linux kernel's keyutils storage module (which this module supports). It runs fine both headless and non-headless, and it's included in all modern Linux systems. That would save you the trouble both of trying to detect the existence of the gnome keyring and writing your own fallback provider. Depending on their usage scenario, your users might want to make the persistence of the entries longer than the default 3-5 days between usage (which would require admin access). But it would be easy for you to write a command-line utility that made that adjustment for them (which they would run with Is there a particular reason you would like to switch to gnome-keyring on headed systems? Using gnome-keyring would have the following drawbacks:
I hope this is useful information for you. Please continue the discussion if you have more questions. |
That's good to know. Our package manager should run nicely in both CI settings and interactive "desktop" workflows. I have the feeling that the gnome-keyring is better integrated in an interactive desktop setting, and we'd want the credentials to stay there indefinitely. Are there any tips on detecting the availability of gnome-keyring? Would getting a random entry and checking the error work? We've also experimented a bit with implementing a credentials daemon (git style, https://git-scm.com/docs/git-credential-cache). We could try to upstream that if it's interesting, but falling back to teh built in linux keyutils store seems like another good option. |
Thanks for the update - it helps me understand your goals better. I'm not a super linux expert, but @landhb is so I'm tagging him here to take a look at your question. My take: I'm not really sure what happens if you run a binary that calls the secret service on a system that isn't running the secret service. As long as you can assume your headless CI boxes are all running the secret service, then yes I would imagine that the simplest way to detect whether you're headless would be simply to try and use a credential and look at whether you get an access denied error. But I'm not sure whether that would be distinguishable from the error you get when a headed user hadn't unlocked the keyring yet. So it might almost be easier to just have your users declare in their invocation whether they are running headed or not. That would always allow you to use the right store for that situation. A completely different approach, by the way, might be to always use the kernel keystore and write a separate tool that headed users would use to transfer their gnome credentials into the persistent store before a run. (You could use keyring to write that separate tool.) That way your CI system would completely avoid all the bloat that comes with supporting the secret service and not bias your async rust users in any way. |
For my personal project, I use the However you can create a builder that tries to use use keyring::{
credential::{CredentialApi, CredentialBuilderApi},
keyutils::KeyutilsCredential,
secret_service::SsCredential,
Entry,
};
use std::any::Any;
use std::error::Error;
use dialoguer::Password;
/// A custom builder that falls back to keyutils
#[derive(Debug)]
struct FallbackBuilder {
/// Indicator to only test once
secret_service_missing: bool,
}
impl FallbackBuilder {
fn new() -> Result<Self, Box<dyn Error>> {
// Create a fake cred
let ss = SsCredential::new_with_target(None, "test", "user")?;
// Force a connect to secret service to determine if it exists
let missing = match ss.map_matching_items(|_item| Ok(()), false) {
Err(keyring::Error::PlatformFailure(_x)) => true,
_ => false,
};
Ok(Self {
secret_service_missing: missing,
})
}
}
impl CredentialBuilderApi for FallbackBuilder {
/// Helper method to try secret-service first, then fallback to the kernel's store
fn build(
&self,
target: Option<&str>,
service: &str,
user: &str,
) -> Result<Box<(dyn CredentialApi + Send + Sync + 'static)>, keyring::Error> {
// First try secret-service if it exists
if !self.secret_service_missing {
let cred = SsCredential::new_with_target(target, service, user)?;
return Ok(Box::new(cred));
}
// Fallback to the kernel's keystore
let cred = KeyutilsCredential::new_with_target(target, service, user)?;
Ok(Box::new(cred))
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() -> Result<(), Box<dyn Error>> {
// Replace the default store at startup
let backend = FallbackBuilder::new()?;
keyring::set_default_credential_builder(Box::new(backend));
// Start the entry
let entry = Entry::new("test", "user")?;
// Attempt to get the entry, if it doesn't exist, prompt the user
// instead.
let _password = match entry.get_password() {
Ok(pass) => pass,
_ => {
// Prompt the user
let passphrase = Password::new().with_prompt("Re-enter password").interact()?;
// Store the password in the available store
entry.set_password(&passphrase)?;
// Use this for now
passphrase
}
};
Ok(())
} |
@wolfv Have you got more questions about this? If not, I'll close this issue in a few days. |
We're working on an application (package manager) that needs to run in headless and non-headless mode.
I would like to set a default storage on Linux based on wether the gnome-keyring / libsecret package is available or not. Is there a function to figure this out dynamically (at runtime)?
I am considering to implement my own storage provider as a fallback that writes the credentials to a JSON file in the home folder when no gnome-keyring is available. I think it would be nice if there was a good way to provide a fallback provider. Any thoughts on that?
Thanks for the nice library by the way!
The text was updated successfully, but these errors were encountered: