give cert-auth role tokens a 15m ttl so logins stop leaving 32-day leases
This commit is contained in:
parent
9b0c0ad7be
commit
00df3ccffb
1 changed files with 160 additions and 30 deletions
|
|
@ -5,7 +5,10 @@ use std::time::Duration;
|
|||
use rustify_derive::Endpoint;
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use vaultrs::{
|
||||
api::pki::{requests::GenerateCertificateRequest, responses::GenerateCertificateResponse},
|
||||
api::{
|
||||
auth::cert::requests::CreateCaCertificateRoleRequest,
|
||||
pki::{requests::GenerateCertificateRequest, responses::GenerateCertificateResponse},
|
||||
},
|
||||
client::{Client, VaultClient, VaultClientSettings, VaultClientSettingsBuilder},
|
||||
};
|
||||
|
||||
|
|
@ -25,6 +28,15 @@ pub const ENV_CACERT: &str = "BAO_CACERT";
|
|||
/// The auth mount a cert login goes through, unless a caller says otherwise.
|
||||
pub const DEFAULT_CERT_MOUNT: &str = "cert";
|
||||
|
||||
/// How long a token from a cert login lives, and the longest it can be made to.
|
||||
///
|
||||
/// Every cert-auth role is written with it as `token_ttl` and `token_max_ttl`.
|
||||
/// The store keeps a lease for each token until it expires, so a role without
|
||||
/// it leaves one lease per login for the mount's default of 32 days.
|
||||
/// [`SecretStore`] holds the token it logs in with and renews nothing: log in
|
||||
/// again rather than keep a handle past this.
|
||||
pub const CERT_ROLE_TOKEN_TTL: &str = "15m";
|
||||
|
||||
/// Upper bound on one request to the store, login included.
|
||||
///
|
||||
/// vaultrs sets no timeout by default, so a store that accepts the connection
|
||||
|
|
@ -371,18 +383,8 @@ impl SecretStore {
|
|||
common_name: &str,
|
||||
policies: &[&str],
|
||||
) -> Result<(), Error> {
|
||||
let mut opts =
|
||||
vaultrs::api::auth::cert::requests::CreateCaCertificateRoleRequest::builder();
|
||||
opts.allowed_common_names(vec![common_name.to_owned()])
|
||||
.token_policies(policies.iter().map(|p| (*p).to_owned()).collect::<Vec<_>>());
|
||||
vaultrs::auth::cert::ca_cert_role::set(
|
||||
&self.inner,
|
||||
mount,
|
||||
name,
|
||||
certificate,
|
||||
Some(&mut opts),
|
||||
)
|
||||
.await?;
|
||||
let request = cert_role_request(mount, name, certificate, common_name, policies);
|
||||
vaultrs::api::exec_with_empty(&self.inner, request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -667,6 +669,28 @@ fn issue_request(mount: &str, role: &str, common_name: &str) -> GenerateCertific
|
|||
}
|
||||
}
|
||||
|
||||
/// The request that writes a cert-auth role: `certificate` pinned, only
|
||||
/// `common_name` accepted, `policies` attached, tokens living
|
||||
/// [`CERT_ROLE_TOKEN_TTL`].
|
||||
fn cert_role_request(
|
||||
mount: &str,
|
||||
name: &str,
|
||||
certificate: &str,
|
||||
common_name: &str,
|
||||
policies: &[&str],
|
||||
) -> CreateCaCertificateRoleRequest {
|
||||
CreateCaCertificateRoleRequest {
|
||||
mount: mount.to_owned(),
|
||||
name: name.to_owned(),
|
||||
certificate: certificate.to_owned(),
|
||||
allowed_common_names: Some(vec![common_name.to_owned()]),
|
||||
token_policies: Some(policies.iter().map(|p| (*p).to_owned()).collect()),
|
||||
token_ttl: Some(CERT_ROLE_TOKEN_TTL.to_owned()),
|
||||
token_max_ttl: Some(CERT_ROLE_TOKEN_TTL.to_owned()),
|
||||
..CreateCaCertificateRoleRequest::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// The store's answer, moved straight into the redacting
|
||||
/// [`mtls::Credential`]. `GenerateCertificateResponse` derives `Debug` and
|
||||
/// holds the private key, so it is consumed here and never formatted.
|
||||
|
|
@ -711,6 +735,20 @@ mod tests {
|
|||
assert_eq!(built.timeout, Some(VAULT_REQUEST_TIMEOUT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_cert_role_is_written_with_short_lived_tokens() {
|
||||
let r = cert_role_request("cert", "hive-agent-a", "PEM", "hive-agent-a", &["shared"]);
|
||||
assert_eq!(r.token_ttl.as_deref(), Some("15m"));
|
||||
assert_eq!(r.token_max_ttl.as_deref(), Some("15m"));
|
||||
// The control: the rest of the role is still written.
|
||||
assert_eq!(
|
||||
r.allowed_common_names,
|
||||
Some(vec!["hive-agent-a".to_owned()])
|
||||
);
|
||||
assert_eq!(r.token_policies, Some(vec!["shared".to_owned()]));
|
||||
assert_eq!(r.certificate, "PEM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_complete_environment_is_accepted() {
|
||||
// The control: without this, every assertion below could be passing
|
||||
|
|
|
|||
Loading…
Reference in a new issue