bring existing agent cert roles to the 1h token ttl on controller start
This commit is contained in:
parent
e8190a4972
commit
76de27fc97
1 changed files with 513 additions and 13 deletions
|
|
@ -8,7 +8,10 @@
|
|||
|
||||
use std::future::Future;
|
||||
|
||||
use swarm_secret_client::{Error, SecretStore, client::EntityRef};
|
||||
use swarm_secret_client::{
|
||||
Error, SecretStore,
|
||||
client::{CertRoleTokenTtls, EntityRef},
|
||||
};
|
||||
|
||||
/// The cert-auth role the controller logs in under.
|
||||
///
|
||||
|
|
@ -48,6 +51,20 @@ pub(crate) trait AccessStore {
|
|||
policies: &[&str],
|
||||
) -> impl Future<Output = Result<(), Error>> + Send;
|
||||
|
||||
/// As [`SecretStore::cert_role_token_ttls`].
|
||||
fn cert_role_token_ttls(
|
||||
&self,
|
||||
mount: &str,
|
||||
name: &str,
|
||||
) -> impl Future<Output = Result<Option<CertRoleTokenTtls>, Error>> + Send;
|
||||
|
||||
/// As [`SecretStore::set_cert_role_token_ttls`].
|
||||
fn set_cert_role_token_ttls(
|
||||
&self,
|
||||
mount: &str,
|
||||
name: &str,
|
||||
) -> impl Future<Output = Result<(), Error>> + Send;
|
||||
|
||||
/// As [`SecretStore::auth_mount_accessor`].
|
||||
fn auth_mount_accessor(
|
||||
&self,
|
||||
|
|
@ -99,6 +116,18 @@ impl AccessStore for SecretStore {
|
|||
SecretStore::write_cert_role(self, mount, name, certificate, common_name, policies).await
|
||||
}
|
||||
|
||||
async fn cert_role_token_ttls(
|
||||
&self,
|
||||
mount: &str,
|
||||
name: &str,
|
||||
) -> Result<Option<CertRoleTokenTtls>, Error> {
|
||||
SecretStore::cert_role_token_ttls(self, mount, name).await
|
||||
}
|
||||
|
||||
async fn set_cert_role_token_ttls(&self, mount: &str, name: &str) -> Result<(), Error> {
|
||||
SecretStore::set_cert_role_token_ttls(self, mount, name).await
|
||||
}
|
||||
|
||||
async fn auth_mount_accessor(&self, mount: &str) -> Result<String, Error> {
|
||||
SecretStore::auth_mount_accessor(self, mount).await
|
||||
}
|
||||
|
|
@ -137,16 +166,35 @@ impl AccessStore for SecretStore {
|
|||
/// outcome of the access calls.
|
||||
#[cfg(test)]
|
||||
pub(crate) mod fake {
|
||||
use std::sync::Mutex;
|
||||
use std::{collections::BTreeMap, sync::Mutex};
|
||||
|
||||
use swarm_secret_client::{Error, client::EntityRef};
|
||||
use swarm_secret_client::{
|
||||
Error,
|
||||
client::{CERT_ROLE_TOKEN_TTL_SECS, CertRoleTokenTtls, EntityRef},
|
||||
};
|
||||
|
||||
use super::AccessStore;
|
||||
|
||||
/// A cert-auth role as the fake keeps it: what a write of the role sets,
|
||||
/// and the lifetimes a write of only those can change.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct FakeRole {
|
||||
pub(crate) certificate: String,
|
||||
pub(crate) common_name: String,
|
||||
pub(crate) policies: Vec<String>,
|
||||
pub(crate) ttls: CertRoleTokenTtls,
|
||||
}
|
||||
|
||||
/// What the fake answers; the default is a store with a readable cert mount
|
||||
/// and no entities, which accepts every write.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FakeAccess {
|
||||
/// The cert-auth roles, by name.
|
||||
roles: Mutex<BTreeMap<String, FakeRole>>,
|
||||
/// Roles whose lifetimes cannot be read.
|
||||
unreadable_roles: Vec<String>,
|
||||
/// Roles whose lifetimes cannot be written.
|
||||
unwritable_roles: Vec<String>,
|
||||
/// The accessor read fails.
|
||||
no_accessor: bool,
|
||||
/// Every policy write is refused.
|
||||
|
|
@ -182,6 +230,32 @@ pub(crate) mod fake {
|
|||
}
|
||||
}
|
||||
|
||||
/// A store that holds `role` under `name`.
|
||||
pub(crate) fn with_role(self, name: &str, role: FakeRole) -> Self {
|
||||
self.roles
|
||||
.lock()
|
||||
.expect("not poisoned")
|
||||
.insert(name.to_owned(), role);
|
||||
self
|
||||
}
|
||||
|
||||
/// A store that cannot read the lifetimes of the role `name`.
|
||||
pub(crate) fn with_unreadable_role(mut self, name: &str) -> Self {
|
||||
self.unreadable_roles.push(name.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// A store that refuses to write the lifetimes of the role `name`.
|
||||
pub(crate) fn with_unwritable_role(mut self, name: &str) -> Self {
|
||||
self.unwritable_roles.push(name.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// The role `name` as the store holds it now.
|
||||
pub(crate) fn role(&self, name: &str) -> Option<FakeRole> {
|
||||
self.roles.lock().expect("not poisoned").get(name).cloned()
|
||||
}
|
||||
|
||||
/// The calls so far, as `verb name` strings, in order.
|
||||
pub(crate) fn calls(&self) -> Vec<String> {
|
||||
self.calls.lock().expect("not poisoned").clone()
|
||||
|
|
@ -217,11 +291,50 @@ pub(crate) mod fake {
|
|||
&self,
|
||||
_mount: &str,
|
||||
name: &str,
|
||||
_certificate: &str,
|
||||
_common_name: &str,
|
||||
certificate: &str,
|
||||
common_name: &str,
|
||||
policies: &[&str],
|
||||
) -> Result<(), Error> {
|
||||
self.log(format!("write_cert_role {name} {}", policies.join(",")));
|
||||
self.roles.lock().expect("not poisoned").insert(
|
||||
name.to_owned(),
|
||||
FakeRole {
|
||||
certificate: certificate.to_owned(),
|
||||
common_name: common_name.to_owned(),
|
||||
policies: policies.iter().map(|p| (*p).to_owned()).collect(),
|
||||
ttls: CertRoleTokenTtls {
|
||||
token_ttl: CERT_ROLE_TOKEN_TTL_SECS,
|
||||
token_max_ttl: CERT_ROLE_TOKEN_TTL_SECS,
|
||||
},
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn cert_role_token_ttls(
|
||||
&self,
|
||||
_mount: &str,
|
||||
name: &str,
|
||||
) -> Result<Option<CertRoleTokenTtls>, Error> {
|
||||
self.log(format!("cert_role_token_ttls {name}"));
|
||||
if self.unreadable_roles.iter().any(|r| r == name) {
|
||||
return Err(Self::denied());
|
||||
}
|
||||
Ok(self.role(name).map(|r| r.ttls))
|
||||
}
|
||||
|
||||
async fn set_cert_role_token_ttls(&self, _mount: &str, name: &str) -> Result<(), Error> {
|
||||
self.log(format!("set_cert_role_token_ttls {name}"));
|
||||
if self.unwritable_roles.iter().any(|r| r == name) {
|
||||
return Err(Self::denied());
|
||||
}
|
||||
// Only the lifetimes: the store keeps the fields a write omits.
|
||||
if let Some(role) = self.roles.lock().expect("not poisoned").get_mut(name) {
|
||||
role.ttls = CertRoleTokenTtls {
|
||||
token_ttl: CERT_ROLE_TOKEN_TTL_SECS,
|
||||
token_max_ttl: CERT_ROLE_TOKEN_TTL_SECS,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue