diff --git a/CONTEXT.md b/CONTEXT.md index 66e647a9..5c7bbe45 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -22,6 +22,14 @@ operation transition. Host capability operations use a separate seam. They turn Host readiness into Rust task wakeups and do not share the caller-to-core broker state machine. +## Credential grant + +A credential grant contains the authorization constraints shared by generated, +imported, managed, and attached-peer credentials: ACL groups, relay permission, +allowed proxy CIDRs, and whether concurrent reuse is allowed. It does not own +credential identity, key material, lifetime, persistence, or runtime ownership. +Each credential intake path normalizes the grant before installing it. + ## Attached peer An attached peer is an ordinary `PeerManagerCore` connected to another diff --git a/easytier-core/src/config/api.rs b/easytier-core/src/config/api.rs index aed8330b..69bc9ada 100644 --- a/easytier-core/src/config/api.rs +++ b/easytier-core/src/config/api.rs @@ -4,7 +4,10 @@ use easytier_proto::api::manage::{ self, NetworkConfig, NetworkingMethod, PortForwardConfig as ApiPortForwardConfig, }; -use super::toml::{ConfigLoader as _, TomlConfig}; +use super::{ + api_input::managed_credential_to_proto, + toml::{ConfigLoader as _, TomlConfig}, +}; pub fn network_config_from_toml(config: &TomlConfig) -> NetworkConfig { let default_config = TomlConfig::default(); @@ -121,15 +124,7 @@ pub fn network_config_from_toml(config: &TomlConfig) -> NetworkConfig { result.managed_credentials = config .get_managed_credentials() .into_iter() - .map(|credential| manage::ManagedCredentialConfig { - credential_id: credential.credential_id, - credential_secret: credential.credential_secret, - groups: credential.groups, - allow_relay: credential.allow_relay, - allowed_proxy_cidrs: credential.allowed_proxy_cidrs, - expiry_unix: credential.expiry_unix, - reusable: Some(credential.reusable), - }) + .map(managed_credential_to_proto) .collect(); let flags = config.get_flags(); diff --git a/easytier-core/src/config/api_input.rs b/easytier-core/src/config/api_input.rs index 15863520..5764f92b 100644 --- a/easytier-core/src/config/api_input.rs +++ b/easytier-core/src/config/api_input.rs @@ -51,6 +51,34 @@ pub fn add_proxy_network_to_config( pub type NetworkingMethod = easytier_proto::api::manage::NetworkingMethod; pub type NetworkConfig = easytier_proto::api::manage::NetworkConfig; +pub(crate) fn managed_credential_from_proto( + credential: &manage::ManagedCredentialConfig, +) -> ManagedCredentialConfig { + ManagedCredentialConfig { + credential_id: credential.credential_id.clone(), + credential_secret: credential.credential_secret.clone(), + groups: credential.groups.clone(), + allow_relay: credential.allow_relay, + allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(), + expiry_unix: credential.expiry_unix, + reusable: credential.reusable.unwrap_or(true), + } +} + +pub(crate) fn managed_credential_to_proto( + credential: ManagedCredentialConfig, +) -> manage::ManagedCredentialConfig { + manage::ManagedCredentialConfig { + credential_id: credential.credential_id, + credential_secret: credential.credential_secret, + groups: credential.groups, + allow_relay: credential.allow_relay, + allowed_proxy_cidrs: credential.allowed_proxy_cidrs, + expiry_unix: credential.expiry_unix, + reusable: Some(credential.reusable), + } +} + pub trait NetworkConfigExt { fn gen_config(&self) -> Result; fn new_from_config(config: impl ConfigLoader) -> Result; @@ -301,15 +329,7 @@ impl NetworkConfigExt for NetworkConfig { cfg.set_managed_credentials( self.managed_credentials .iter() - .map(|credential| ManagedCredentialConfig { - credential_id: credential.credential_id.clone(), - credential_secret: credential.credential_secret.clone(), - groups: credential.groups.clone(), - allow_relay: credential.allow_relay, - allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(), - expiry_unix: credential.expiry_unix, - reusable: credential.reusable.unwrap_or(true), - }) + .map(managed_credential_from_proto) .collect(), ); @@ -624,15 +644,7 @@ impl NetworkConfigExt for NetworkConfig { result.managed_credentials = config .get_managed_credentials() .into_iter() - .map(|credential| manage::ManagedCredentialConfig { - credential_id: credential.credential_id, - credential_secret: credential.credential_secret, - groups: credential.groups, - allow_relay: credential.allow_relay, - allowed_proxy_cidrs: credential.allowed_proxy_cidrs, - expiry_unix: credential.expiry_unix, - reusable: Some(credential.reusable), - }) + .map(managed_credential_to_proto) .collect(); let flags = config.get_flags(); let default_flags = default_config.get_flags(); diff --git a/easytier-core/src/instance/management.rs b/easytier-core/src/instance/management.rs index ec26474d..001dbfd9 100644 --- a/easytier-core/src/instance/management.rs +++ b/easytier-core/src/instance/management.rs @@ -157,14 +157,7 @@ where let generated = self .peer_manager .credential_manager() - .generate_credential_with_options( - options.groups, - options.allow_relay, - options.allowed_proxy_cidrs, - options.ttl, - options.credential_id, - options.reusable, - ) + .generate_credential_with_options(options) .map_err(anyhow::Error::msg)?; self.peer_manager.notify_credential_changed(); Ok(generated) diff --git a/easytier-core/src/management/full/config_patch.rs b/easytier-core/src/management/full/config_patch.rs index e1b59c36..b86e9459 100644 --- a/easytier-core/src/management/full/config_patch.rs +++ b/easytier-core/src/management/full/config_patch.rs @@ -8,9 +8,10 @@ use easytier_proto::api::config::{ use crate::{ config::{ + api_input::managed_credential_from_proto, peers::AclRuleConfig, runtime::CoreInstanceRuntimeConfig, - toml::{ConfigLoader as _, ManagedCredentialConfig, TomlConfig}, + toml::{ConfigLoader as _, TomlConfig}, }, instance::{CoreInstance, CoreInstanceConfig, CoreInstanceHost, CoreInstanceState}, peers::credential_manager::CredentialManager, @@ -164,15 +165,7 @@ where let entries = managed .entries .iter() - .map(|credential| ManagedCredentialConfig { - credential_id: credential.credential_id.clone(), - credential_secret: credential.credential_secret.clone(), - groups: credential.groups.clone(), - allow_relay: credential.allow_relay, - allowed_proxy_cidrs: credential.allowed_proxy_cidrs.clone(), - expiry_unix: credential.expiry_unix, - reusable: credential.reusable.unwrap_or(true), - }) + .map(managed_credential_from_proto) .collect::>(); let replacement = credential_manager .validate_managed_credentials(&entries) diff --git a/easytier-core/src/peers/attached.rs b/easytier-core/src/peers/attached.rs index 9ae26aa8..e431e5a5 100644 --- a/easytier-core/src/peers/attached.rs +++ b/easytier-core/src/peers/attached.rs @@ -75,13 +75,8 @@ impl AttachedCredentialRegistration { network_runtime_config.snapshot().as_ref(), &configured_groups, ); - let credential_id = network_peer_manager.register_ephemeral_credential( - public_key, - groups, - false, - Vec::new(), - false, - )?; + let credential_id = + network_peer_manager.register_ephemeral_credential(public_key, groups)?; let task_peer_manager = network_peer_manager.clone(); let policy_task = tokio::spawn(async move { while peer_changes.changed().await.is_ok() { diff --git a/easytier-core/src/peers/credential_manager.rs b/easytier-core/src/peers/credential_manager.rs index a1acf1b5..67cb3801 100644 --- a/easytier-core/src/peers/credential_manager.rs +++ b/easytier-core/src/peers/credential_manager.rs @@ -47,15 +47,68 @@ pub struct CredentialUpsertOptions { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub(crate) struct CredentialEntry { - pubkey: String, - #[serde(default)] - secret: String, +struct CredentialGrant { groups: Vec, allow_relay: bool, allowed_proxy_cidrs: Vec, #[serde(default = "default_true")] reusable: bool, +} + +#[derive(Debug)] +struct InvalidAllowedProxyCidr(String); + +impl std::fmt::Display for InvalidAllowedProxyCidr { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "invalid allowed_proxy_cidr: {}", self.0) + } +} + +impl CredentialGrant { + fn new( + groups: Vec, + allow_relay: bool, + allowed_proxy_cidrs: Vec, + reusable: bool, + ) -> Result { + let mut grant = Self { + groups, + allow_relay, + allowed_proxy_cidrs, + reusable, + }; + grant.normalize()?; + Ok(grant) + } + + fn normalize(&mut self) -> Result<(), InvalidAllowedProxyCidr> { + for cidr in &mut self.allowed_proxy_cidrs { + let normalized = cidr.trim().to_owned(); + normalized + .parse::() + .map_err(|_| InvalidAllowedProxyCidr(normalized.clone()))?; + *cidr = normalized; + } + Ok(()) + } + + fn for_attached_peer(groups: Vec) -> Self { + Self { + groups, + allow_relay: false, + allowed_proxy_cidrs: Vec::new(), + reusable: false, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub(crate) struct CredentialEntry { + pubkey: String, + #[serde(default)] + secret: String, + #[serde(flatten)] + grant: CredentialGrant, expiry_unix: i64, created_at_unix: i64, } @@ -68,22 +121,22 @@ impl CredentialEntry { fn to_trusted_credential(&self) -> Option { Some(TrustedCredentialPubkey { pubkey: CredentialManager::decode_pubkey_b64(&self.pubkey)?, - groups: self.groups.clone(), - allow_relay: self.allow_relay, + groups: self.grant.groups.clone(), + allow_relay: self.grant.allow_relay, expiry_unix: self.expiry_unix, - allowed_proxy_cidrs: self.allowed_proxy_cidrs.clone(), - reusable: Some(self.reusable), + allowed_proxy_cidrs: self.grant.allowed_proxy_cidrs.clone(), + reusable: Some(self.grant.reusable), }) } fn to_credential_info(&self, credential_id: &str) -> CredentialInfo { CredentialInfo { credential_id: credential_id.to_string(), - groups: self.groups.clone(), - allow_relay: self.allow_relay, + groups: self.grant.groups.clone(), + allow_relay: self.grant.allow_relay, expiry_unix: self.expiry_unix, - allowed_proxy_cidrs: self.allowed_proxy_cidrs.clone(), - reusable: Some(self.reusable), + allowed_proxy_cidrs: self.grant.allowed_proxy_cidrs.clone(), + reusable: Some(self.grant.reusable), public_key_fingerprint: CredentialManager::public_key_fingerprint(&self.pubkey) .unwrap_or_default(), } @@ -97,20 +150,22 @@ impl CredentialEntry { .try_into() .map_err(|_| format!("credential_secret for {credential_id} must contain 32 bytes"))?; let private = StaticSecret::from(private_bytes); - let mut allowed_proxy_cidrs = Vec::with_capacity(entry.allowed_proxy_cidrs.len()); - for cidr in &entry.allowed_proxy_cidrs { - let cidr = cidr.trim(); - cidr.parse::() - .map_err(|_| format!("invalid allowed_proxy_cidr for {credential_id}: {cidr}"))?; - allowed_proxy_cidrs.push(cidr.to_owned()); - } + let grant = CredentialGrant::new( + entry.groups.clone(), + entry.allow_relay, + entry.allowed_proxy_cidrs.clone(), + entry.reusable, + ) + .map_err(|error| { + format!( + "invalid allowed_proxy_cidr for {credential_id}: {}", + error.0 + ) + })?; Ok(Self { pubkey: BASE64_STANDARD.encode(PublicKey::from(&private).as_bytes()), secret: BASE64_STANDARD.encode(private.as_bytes()), - groups: entry.groups.clone(), - allow_relay: entry.allow_relay, - allowed_proxy_cidrs, - reusable: entry.reusable, + grant, expiry_unix: entry.expiry_unix, created_at_unix: 0, }) @@ -192,7 +247,9 @@ impl CredentialManager { pub fn from_storage(storage: Arc) -> Self { let loaded = match storage.load() { - Ok(Some(serialized)) => serde_json::from_str(&serialized).map_err(anyhow::Error::from), + Ok(Some(serialized)) => serde_json::from_str(&serialized) + .map_err(anyhow::Error::from) + .and_then(Self::normalize_loaded_entries), Ok(None) => Ok(HashMap::new()), Err(error) => Err(error), }; @@ -213,15 +270,29 @@ impl CredentialManager { } } + fn normalize_loaded_entries( + mut entries: HashMap, + ) -> anyhow::Result> { + for (credential_id, entry) in &mut entries { + entry.grant.normalize().map_err(|error| { + anyhow::anyhow!("invalid stored credential {credential_id}: {error}") + })?; + } + Ok(entries) + } + pub fn generate_credential_with_options( &self, - groups: Vec, - allow_relay: bool, - allowed_proxy_cidrs: Vec, - ttl: Duration, - credential_id: Option, - reusable: bool, + options: CredentialCreateOptions, ) -> Result { + let CredentialCreateOptions { + groups, + allow_relay, + allowed_proxy_cidrs, + ttl, + credential_id, + reusable, + } = options; self.ensure_storage_available() .map_err(|error| error.to_string())?; let mut state = self.state.lock().unwrap(); @@ -254,15 +325,11 @@ impl CredentialManager { } } }; + let grant = CredentialGrant::new(groups, allow_relay, allowed_proxy_cidrs, reusable) + .map_err(|error| error.to_string())?; let (entry, secret) = loop { - let generated = Self::build_entry( - groups.clone(), - allow_relay, - allowed_proxy_cidrs.clone(), - reusable, - ttl, - ); + let generated = Self::build_entry(grant.clone(), ttl); let public_key_in_use = updated .values() .chain(Self::managed_values(&state)) @@ -285,13 +352,7 @@ impl CredentialManager { }) } - fn build_entry( - groups: Vec, - allow_relay: bool, - allowed_proxy_cidrs: Vec, - reusable: bool, - ttl: Duration, - ) -> (CredentialEntry, String) { + fn build_entry(grant: CredentialGrant, ttl: Duration) -> (CredentialEntry, String) { let private = StaticSecret::random_from_rng(rand::rngs::OsRng); let public = PublicKey::from(&private); let pubkey = BASE64_STANDARD.encode(public.as_bytes()); @@ -306,10 +367,7 @@ impl CredentialManager { let entry = CredentialEntry { pubkey, secret: secret.clone(), - groups, - allow_relay, - allowed_proxy_cidrs, - reusable, + grant, expiry_unix, created_at_unix: now, }; @@ -335,17 +393,11 @@ impl CredentialManager { &self, public_key: [u8; 32], groups: Vec, - allow_relay: bool, - allowed_proxy_cidrs: Vec, - reusable: bool, ) -> Result { let entry = CredentialEntry { pubkey: BASE64_STANDARD.encode(public_key), secret: String::new(), - groups, - allow_relay, - allowed_proxy_cidrs, - reusable, + grant: CredentialGrant::for_attached_peer(groups), expiry_unix: i64::MAX, created_at_unix: current_unix_timestamp(), }; @@ -376,10 +428,10 @@ impl CredentialManager { ) -> Option { let mut state = self.state.lock().unwrap(); let credential = state.ephemeral.get_mut(&credential_id)?; - if credential.groups == groups { + if credential.grant.groups == groups { return Some(false); } - credential.groups = groups; + credential.grant.groups = groups; Some(true) } @@ -416,13 +468,12 @@ impl CredentialManager { .try_into() .map_err(|_| "credential_secret must contain 32 bytes".to_string())?; let private = StaticSecret::from(private_bytes); + let grant = CredentialGrant::new(groups, allow_relay, allowed_proxy_cidrs, reusable) + .map_err(|error| error.to_string())?; let entry = CredentialEntry { pubkey: BASE64_STANDARD.encode(PublicKey::from(&private).as_bytes()), secret: BASE64_STANDARD.encode(private.as_bytes()), - groups, - allow_relay, - allowed_proxy_cidrs, - reusable, + grant, expiry_unix, created_at_unix: current_unix_timestamp(), }; @@ -455,10 +506,7 @@ impl CredentialManager { let changed = state.base.get(&credential_id).is_none_or(|existing| { existing.secret != entry.secret || existing.pubkey != entry.pubkey - || existing.groups != entry.groups - || existing.allow_relay != entry.allow_relay - || existing.allowed_proxy_cidrs != entry.allowed_proxy_cidrs - || existing.reusable != entry.reusable + || existing.grant != entry.grant || existing.expiry_unix != entry.expiry_unix }); if !changed { @@ -722,7 +770,43 @@ mod tests { let entry = CredentialEntry::from_managed(&credential).unwrap(); - assert_eq!(entry.allowed_proxy_cidrs, ["10.0.0.0/24"]); + assert_eq!(entry.grant.allowed_proxy_cidrs, ["10.0.0.0/24"]); + } + + #[test] + fn generated_and_imported_credentials_normalize_allowed_proxy_cidrs() { + let source = CredentialManager::new(); + let generated = source + .generate_credential_with_options(CredentialCreateOptions { + groups: Vec::new(), + allow_relay: false, + allowed_proxy_cidrs: vec![" 10.0.0.0/24 ".to_owned()], + ttl: Duration::from_secs(3600), + credential_id: None, + reusable: true, + }) + .unwrap(); + assert_eq!( + source.list_credentials()[0].allowed_proxy_cidrs, + ["10.0.0.0/24"] + ); + + let target = CredentialManager::new(); + target + .upsert_credential(CredentialUpsertOptions { + credential_id: "imported".to_owned(), + credential_secret: generated.secret, + groups: Vec::new(), + allow_relay: false, + allowed_proxy_cidrs: vec![" 192.168.0.0/16 ".to_owned()], + expiry_unix: generated.expiry_unix, + reusable: true, + }) + .unwrap(); + assert_eq!( + target.list_credentials()[0].allowed_proxy_cidrs, + ["192.168.0.0/16"] + ); } impl CredentialManager { @@ -733,14 +817,14 @@ mod tests { allowed_proxy_cidrs: Vec, ttl: Duration, ) -> GeneratedCredential { - self.generate_credential_with_options( + self.generate_credential_with_options(CredentialCreateOptions { groups, allow_relay, allowed_proxy_cidrs, ttl, - None, - true, - ) + credential_id: None, + reusable: true, + }) .unwrap() } @@ -752,14 +836,14 @@ mod tests { ttl: Duration, credential_id: Option, ) -> GeneratedCredential { - self.generate_credential_with_options( + self.generate_credential_with_options(CredentialCreateOptions { groups, allow_relay, allowed_proxy_cidrs, ttl, credential_id, - true, - ) + reusable: true, + }) .unwrap() } } @@ -780,6 +864,24 @@ mod tests { } } + fn credential_storage_with_proxy_cidr( + allowed_proxy_cidr: &str, + ) -> (Arc, String) { + let storage = Arc::new(MemoryCredentialStorage::default()); + let manager = CredentialManager::from_storage(storage.clone()); + let generated = manager.generate_credential( + Vec::new(), + false, + vec!["10.0.0.0/24".to_owned()], + Duration::from_secs(3600), + ); + let serialized = storage.serialized.lock().unwrap().clone().unwrap(); + let mut snapshot: serde_json::Value = serde_json::from_str(&serialized).unwrap(); + snapshot[&generated.credential_id]["allowed_proxy_cidrs"][0] = allowed_proxy_cidr.into(); + *storage.serialized.lock().unwrap() = Some(serde_json::to_string(&snapshot).unwrap()); + (storage, generated.credential_id) + } + struct FailOnceCredentialStorage { serialized: Mutex>, fail_next_store: Mutex, @@ -887,14 +989,14 @@ mod tests { fn upsert_credential_preserves_key_attributes_and_storage() { let source = CredentialManager::new(); let generated = source - .generate_credential_with_options( - vec!["users".to_string()], - false, - vec!["10.0.0.0/8".to_string()], - Duration::from_secs(3600), - Some("shared-id".to_string()), - false, - ) + .generate_credential_with_options(CredentialCreateOptions { + groups: vec!["users".to_string()], + allow_relay: false, + allowed_proxy_cidrs: vec!["10.0.0.0/8".to_string()], + ttl: Duration::from_secs(3600), + credential_id: Some("shared-id".to_string()), + reusable: false, + }) .unwrap(); let source_info = source.list_credentials().remove(0); let options = CredentialUpsertOptions { @@ -1011,6 +1113,67 @@ mod tests { assert!(reloaded.list_credentials().is_empty()); } + #[test] + fn stored_credentials_normalize_allowed_proxy_cidrs_on_load() { + let (storage, credential_id) = credential_storage_with_proxy_cidr(" 10.0.0.0/24 "); + + let manager = CredentialManager::from_storage(storage); + + assert_eq!(manager.list_credentials()[0].credential_id, credential_id); + assert_eq!( + manager.list_credentials()[0].allowed_proxy_cidrs, + ["10.0.0.0/24"] + ); + } + + #[test] + fn stored_credentials_with_invalid_proxy_cidr_fail_closed() { + let (storage, credential_id) = credential_storage_with_proxy_cidr("not-a-cidr"); + + let manager = CredentialManager::from_storage(storage); + + assert!(manager.list_credentials().is_empty()); + let error = manager + .install_initial_managed_credentials(&[]) + .unwrap_err(); + assert!(error.contains(&credential_id), "{error}"); + assert!(error.contains("invalid allowed_proxy_cidr"), "{error}"); + } + + #[test] + fn credential_storage_schema_remains_flat() { + let storage = Arc::new(MemoryCredentialStorage::default()); + let manager = CredentialManager::from_storage(storage.clone()); + manager.generate_credential( + vec!["ops".to_owned()], + true, + vec!["10.0.0.0/24".to_owned()], + Duration::from_secs(3600), + ); + + let serialized = storage.serialized.lock().unwrap().clone().unwrap(); + let mut snapshot: serde_json::Value = serde_json::from_str(&serialized).unwrap(); + let entry = snapshot.as_object().unwrap().values().next().unwrap(); + + assert!(entry.get("grant").is_none()); + assert_eq!(entry["groups"][0], "ops"); + assert_eq!(entry["allowed_proxy_cidrs"][0], "10.0.0.0/24"); + assert_eq!(entry["allow_relay"], true); + assert_eq!(entry["reusable"], true); + + snapshot + .as_object_mut() + .unwrap() + .values_mut() + .next() + .unwrap() + .as_object_mut() + .unwrap() + .remove("reusable"); + let legacy: HashMap = serde_json::from_value(snapshot).unwrap(); + assert!(legacy.values().next().unwrap().grant.reusable); + } + #[test] fn malformed_storage_is_fail_closed() { let storage = Arc::new(MemoryCredentialStorage { @@ -1031,7 +1194,7 @@ mod tests { let public = *PublicKey::from(&private).as_bytes(); let credential_id = manager - .register_ephemeral_credential(public, vec!["ops".to_owned()], false, Vec::new(), false) + .register_ephemeral_credential(public, vec!["ops".to_owned()]) .unwrap(); assert!(manager.is_pubkey_trusted(&public)); @@ -1096,25 +1259,19 @@ mod tests { .unwrap(); let error = manager - .generate_credential_with_options( - Vec::new(), - false, - Vec::new(), - Duration::from_secs(60), - Some("pending".to_owned()), - true, - ) + .generate_credential_with_options(CredentialCreateOptions { + groups: Vec::new(), + allow_relay: false, + allowed_proxy_cidrs: Vec::new(), + ttl: Duration::from_secs(60), + credential_id: Some("pending".to_owned()), + reusable: true, + }) .unwrap_err(); assert!(error.contains("managed by configuration")); assert!( manager - .register_ephemeral_credential( - *public.as_bytes(), - Vec::new(), - false, - Vec::new(), - false, - ) + .register_ephemeral_credential(*public.as_bytes(), Vec::new()) .is_err() ); assert!(!manager.is_pubkey_trusted(public.as_bytes())); @@ -1122,13 +1279,7 @@ mod tests { drop(replacement); assert!( manager - .register_ephemeral_credential( - *public.as_bytes(), - Vec::new(), - false, - Vec::new(), - false, - ) + .register_ephemeral_credential(*public.as_bytes(), Vec::new()) .is_ok() ); } @@ -1146,14 +1297,14 @@ mod tests { .unwrap(); let error = manager - .generate_credential_with_options( - Vec::new(), - false, - Vec::new(), - Duration::from_secs(60), - Some("managed".to_owned()), - true, - ) + .generate_credential_with_options(CredentialCreateOptions { + groups: Vec::new(), + allow_relay: false, + allowed_proxy_cidrs: Vec::new(), + ttl: Duration::from_secs(60), + credential_id: Some("managed".to_owned()), + reusable: true, + }) .unwrap_err(); assert!(error.contains("managed by configuration")); diff --git a/easytier-core/src/peers/peer_manager.rs b/easytier-core/src/peers/peer_manager.rs index 95d3f088..365749a6 100644 --- a/easytier-core/src/peers/peer_manager.rs +++ b/easytier-core/src/peers/peer_manager.rs @@ -1166,19 +1166,10 @@ impl PeerManagerCore { &self, public_key: [u8; 32], groups: Vec, - allow_relay: bool, - allowed_proxy_cidrs: Vec, - reusable: bool, ) -> anyhow::Result { let credential_id = self .credential_manager() - .register_ephemeral_credential( - public_key, - groups, - allow_relay, - allowed_proxy_cidrs, - reusable, - ) + .register_ephemeral_credential(public_key, groups) .map_err(anyhow::Error::msg)?; self.notify_credential_changed(); Ok(credential_id)