Module keyring::keyutils

source ·
Expand description

§Linux kernel (keyutils) credential store

Modern linux kernels have a built-in secure store, keyutils. This module (written primarily by @landhb) uses that secure store as the persistent back end for entries.

Entries in keyutils are identified by a string description. If an entry is created with an explicit target, that value is used as the keyutils description. Otherwise, the string keyring-rs:user@service is used (where user and service come from the entry creation call).

§Persistence

The key management facility provided by the kernel is completely in-memory and will not persist across reboots. Consider the keyring a secure cache and plan for your application to handle cases where the entry is no-longer available in-memory.

In general you should prepare for Entry::get_password to fail and have a fallback to re-load the credential into memory.

Potential options to re-load the credential into memory are:

  • Re-prompt the user (most common/effective for CLI applications)
  • Create a PAM module or use pam_exec to load a credential securely when the user logs in.
  • If you’re running as a systemd service you can use systemd-ask-password to prompt the user when your service starts.
use std::error::Error;
use keyring::Entry;

/// Simple user code that handles retrieving a credential regardless
/// of the credential state.
struct CredentialManager {
    entry: Entry,
}

impl CredentialManager {
    /// Init the service as normal
    pub fn new(service: &str, user: &str) -> Result<Self, Box<dyn Error>> {
        Ok(Self {
            entry: Entry::new(service, user)?
        })
    }

    /// Method that first attempts to retreive the credential from memory
    /// and falls back to prompting the user.
    pub fn get(&self) -> Result<String, Box<dyn Error>> {
        self.entry.get_password().or_else(|_| self.prompt())
    }

    /// Internal method to prompt the user and cache the credential
    /// in memory for subsequent lookups.
    fn prompt(&self) -> Result<String, Box<dyn Error>> {
        let password = rpassword::read_password()?;
        self.entry.set_password(&password)?;
        Ok(password)
    }
}

A single entry in keyutils can be on multiple “keyrings”, each of which has a subtly different lifetime. The core storage for keyring keys is provided by the user-specific persistent keyring, whose lifetime defaults to a few days (and is controllable by administrators). But whenever an entry’s credential is used, it is also added to the user’s session keyring: this ensures that the credential will persist as long as the user session exists, and when the user logs out the credential will persist as long as the persistent keyring doesn’t expire while the user is logged out.

Each time the Entry::new() operation is performed, the persistent keyring’s expiration timer is reset to the value configured in:

proc/sys/kernel/keys/persistent_keyring_expiry
Persistent Keyring StateSession Keyring StateUser Key State
ActiveActiveActive
ExpiredActiveActive
ActiveLogged OutActive (Accessible on next login)
ExpiredLogged OutExpired

Note: As mentioned above, a reboot clears all keyrings.

§Headless usage

If you are trying to use keyring on a headless linux box, it’s strongly recommended that you use this credential store, because (as part of the kernel) it’s designed to be used headlessly. To set this module as your default store, build with --features linux-default-keyutils. Alternatively, you can drop the secret-service credential store altogether (which will slim your build significantly) by building keyring with --no-default-features and --features linux-no-secret-service.

Structs§

Functions§