mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-01 08:49:16 +00:00
fix(credential): trust pinned admin keys on first connect (#2528)
This commit is contained in:
@@ -687,7 +687,8 @@ impl PeerConn {
|
|||||||
/// | Client role | Server role | Typical credential condition | Client auth level | Server auth level | Client sees server type | Server sees client type |
|
/// | Client role | Server role | Typical credential condition | Client auth level | Server auth level | Client sees server type | Server sees client type |
|
||||||
/// | --- | --- | --- | --- | --- | --- | --- |
|
/// | --- | --- | --- | --- | --- | --- | --- |
|
||||||
/// | Admin | Admin | same network_secret, proof verified | NetworkSecretConfirmed | NetworkSecretConfirmed | Admin | Admin |
|
/// | Admin | Admin | same network_secret, proof verified | NetworkSecretConfirmed | NetworkSecretConfirmed | Admin | Admin |
|
||||||
/// | Credential | Admin | client pubkey is trusted by admin | EncryptedUnauthenticated | PeerVerified | Admin | Credential |
|
/// | Credential | Admin | admin key is pinned and client key is trusted | PeerVerified | PeerVerified | Admin | Credential |
|
||||||
|
/// | Credential | Admin | client pubkey is trusted, admin key is not pinned | EncryptedUnauthenticated | PeerVerified | Admin | Credential |
|
||||||
/// | Credential | Admin | client pubkey is unknown | handshake may fail | handshake reject | unknown | unknown |
|
/// | Credential | Admin | client pubkey is unknown | handshake may fail | handshake reject | unknown | unknown |
|
||||||
/// | Admin | SharedNode | pinned key match | PeerVerified | EncryptedUnauthenticated | SharedNode | Admin |
|
/// | Admin | SharedNode | pinned key match | PeerVerified | EncryptedUnauthenticated | SharedNode | Admin |
|
||||||
/// | Admin | SharedNode | local has no pinned key requirement | EncryptedUnauthenticated | EncryptedUnauthenticated | SharedNode | Admin |
|
/// | Admin | SharedNode | local has no pinned key requirement | EncryptedUnauthenticated | EncryptedUnauthenticated | SharedNode | Admin |
|
||||||
@@ -696,8 +697,7 @@ impl PeerConn {
|
|||||||
///
|
///
|
||||||
/// Logic (in priority order):
|
/// Logic (in priority order):
|
||||||
/// 1. **NetworkSecretConfirmed**: proof verification succeeds
|
/// 1. **NetworkSecretConfirmed**: proof verification succeeds
|
||||||
/// 2. **PeerVerified**: pinned_pubkey matches and is in trusted list
|
/// 2. **PeerVerified**: pinned_pubkey matches
|
||||||
/// (if no network_secret, pinned_pubkey must be in trusted list)
|
|
||||||
/// 3. **PeerVerified**: pubkey is in trusted list
|
/// 3. **PeerVerified**: pubkey is in trusted list
|
||||||
/// 4. **EncryptedUnauthenticated**: initiator without network_secret
|
/// 4. **EncryptedUnauthenticated**: initiator without network_secret
|
||||||
/// 5. **Reject**: none of the above
|
/// 5. **Reject**: none of the above
|
||||||
@@ -727,16 +727,6 @@ impl PeerConn {
|
|||||||
"pinned remote static pubkey mismatch".to_owned(),
|
"pinned remote static pubkey mismatch".to_owned(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// If no network_secret, pinned key must be in trusted list
|
|
||||||
if !has_network_secret
|
|
||||||
&& !self
|
|
||||||
.context
|
|
||||||
.is_pubkey_trusted(remote_pubkey, remote_network_name)
|
|
||||||
{
|
|
||||||
return Err(Error::WaitRespError(
|
|
||||||
"pinned pubkey not in trusted list".to_owned(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return Ok(SecureAuthLevel::PeerVerified);
|
return Ok(SecureAuthLevel::PeerVerified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use easytier_core::process_runtime::CoreProcessRuntime;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
common::{
|
||||||
config::{ConfigLoader, NetworkIdentity, TomlConfigLoader},
|
config::{ConfigLoader, NetworkIdentity, PeerConfig, TomlConfigLoader},
|
||||||
global_ctx::GlobalCtxEvent,
|
global_ctx::GlobalCtxEvent,
|
||||||
},
|
},
|
||||||
instance::test_instance::TestInstance as Instance,
|
instance::test_instance::TestInstance as Instance,
|
||||||
@@ -763,17 +763,24 @@ async fn wait_stable_single_visible_peer_on_admins(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test 1: Basic credential node connectivity
|
/// Test 1: Basic credential node connectivity with and without an admin key pin
|
||||||
/// Topology: Admin ← Credential
|
/// Topology: Admin ← Credential
|
||||||
/// Verifies that a credential node can connect to an admin node and appears in routes
|
/// Verifies that a credential node can connect to an admin node and appears in routes
|
||||||
|
#[rstest]
|
||||||
|
#[case(false)]
|
||||||
|
#[case(true)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn credential_basic_connectivity() {
|
async fn credential_basic_connectivity(#[case] pin_admin: bool) {
|
||||||
prepare_credential_network();
|
prepare_credential_network();
|
||||||
let process_runtime = CoreProcessRuntime::new();
|
let process_runtime = CoreProcessRuntime::new();
|
||||||
|
|
||||||
// Create admin node
|
// Create admin node
|
||||||
let admin_config = create_admin_config("admin", Some("ns_adm"), "10.144.144.1", "fd00::1/64");
|
let admin_config = create_admin_config("admin", Some("ns_adm"), "10.144.144.1", "fd00::1/64");
|
||||||
|
let admin_public_key = admin_config
|
||||||
|
.get_secure_mode()
|
||||||
|
.and_then(|config| config.local_public_key)
|
||||||
|
.unwrap();
|
||||||
let mut admin_inst = Instance::new_with_process_runtime(admin_config, process_runtime.clone());
|
let mut admin_inst = Instance::new_with_process_runtime(admin_config, process_runtime.clone());
|
||||||
admin_inst.run().await.unwrap();
|
admin_inst.run().await.unwrap();
|
||||||
|
|
||||||
@@ -786,11 +793,19 @@ async fn credential_basic_connectivity() {
|
|||||||
"fd00::2/64",
|
"fd00::2/64",
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
if pin_admin {
|
||||||
|
cred_config.set_peers(vec![PeerConfig {
|
||||||
|
uri: "tcp://10.1.1.1:11010".parse().unwrap(),
|
||||||
|
peer_public_key: Some(admin_public_key),
|
||||||
|
}]);
|
||||||
|
}
|
||||||
let mut cred_inst = Instance::new_with_process_runtime(cred_config, process_runtime.clone());
|
let mut cred_inst = Instance::new_with_process_runtime(cred_config, process_runtime.clone());
|
||||||
cred_inst.run().await.unwrap();
|
cred_inst.run().await.unwrap();
|
||||||
|
|
||||||
// Credential connects to admin
|
// Credential connects to admin
|
||||||
cred_inst.add_connector_url("tcp://10.1.1.1:11010".parse().unwrap());
|
if !pin_admin {
|
||||||
|
cred_inst.add_connector_url("tcp://10.1.1.1:11010".parse().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
let cred_peer_id = cred_inst.peer_id();
|
let cred_peer_id = cred_inst.peer_id();
|
||||||
let admin_peer_id = admin_inst.peer_id();
|
let admin_peer_id = admin_inst.peer_id();
|
||||||
@@ -853,6 +868,66 @@ async fn credential_basic_connectivity() {
|
|||||||
drop_insts(vec![admin_inst, cred_inst]).await;
|
drop_insts(vec![admin_inst, cred_inst]).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A credential node must reject an admin whose Noise key does not match its pin.
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial_test::serial]
|
||||||
|
async fn credential_rejects_incorrect_admin_pin() {
|
||||||
|
prepare_credential_network();
|
||||||
|
let process_runtime = CoreProcessRuntime::new();
|
||||||
|
|
||||||
|
let admin_config = create_admin_config("admin", Some("ns_adm"), "10.144.144.1", "fd00::1/64");
|
||||||
|
let admin_public_key = admin_config
|
||||||
|
.get_secure_mode()
|
||||||
|
.and_then(|config| config.local_public_key)
|
||||||
|
.unwrap();
|
||||||
|
let mut admin_inst = Instance::new_with_process_runtime(admin_config, process_runtime.clone());
|
||||||
|
admin_inst.run().await.unwrap();
|
||||||
|
|
||||||
|
let cred_config = create_credential_config(
|
||||||
|
&admin_inst,
|
||||||
|
"cred",
|
||||||
|
Some("ns_c1"),
|
||||||
|
"10.144.144.2",
|
||||||
|
"fd00::2/64",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let incorrect_admin_public_key = generate_secure_mode_config().local_public_key.unwrap();
|
||||||
|
assert_ne!(incorrect_admin_public_key, admin_public_key);
|
||||||
|
cred_config.set_peers(vec![PeerConfig {
|
||||||
|
uri: "tcp://10.1.1.1:11010".parse().unwrap(),
|
||||||
|
peer_public_key: Some(incorrect_admin_public_key),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
let mut cred_inst = Instance::new_with_process_runtime(cred_config, process_runtime.clone());
|
||||||
|
cred_inst.run().await.unwrap();
|
||||||
|
|
||||||
|
let admin_peer_id = admin_inst.peer_id();
|
||||||
|
let cred_peer_id = cred_inst.peer_id();
|
||||||
|
for _ in 0..5 {
|
||||||
|
let admin_peers = admin_inst.get_core_instance().connected_peers().await;
|
||||||
|
let cred_peers = cred_inst.get_core_instance().connected_peers().await;
|
||||||
|
let admin_routes = admin_inst.get_core_instance().route_snapshots().await;
|
||||||
|
let cred_routes = cred_inst.get_core_instance().route_snapshots().await;
|
||||||
|
|
||||||
|
assert!(!admin_peers.contains(&cred_peer_id));
|
||||||
|
assert!(!cred_peers.contains(&admin_peer_id));
|
||||||
|
assert!(
|
||||||
|
!admin_routes
|
||||||
|
.iter()
|
||||||
|
.any(|route| route.peer_id == cred_peer_id)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!cred_routes
|
||||||
|
.iter()
|
||||||
|
.any(|route| route.peer_id == admin_peer_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_insts(vec![admin_inst, cred_inst]).await;
|
||||||
|
}
|
||||||
|
|
||||||
/// Test 5-6: Credential relay capability with allow_relay parameter
|
/// Test 5-6: Credential relay capability with allow_relay parameter
|
||||||
/// Topology: Admin ← Credential_A, Admin ← Credential_B, Admin ← Credential_C(listener, allow_relay)
|
/// Topology: Admin ← Credential_A, Admin ← Credential_B, Admin ← Credential_C(listener, allow_relay)
|
||||||
/// Verifies routing behavior based on allow_relay flag:
|
/// Verifies routing behavior based on allow_relay flag:
|
||||||
|
|||||||
Reference in New Issue
Block a user