mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-12 16:16:55 +00:00
feat(heal): gate control capability by cluster topology (#4994)
* fix(rpc): bind internode auth to exact targets * fix(heal): initialize the runtime atomically * fix(heal): aggregate status across cluster nodes * fix(heal): return canonical tokens for duplicate starts * feat(heal): add authenticated control RPC contract * feat(heal): gate control capability by cluster topology * fix(heal): return canonical tokens for duplicate starts (#4992) --------- Co-authored-by: Zhengchao An <anzhengchao@gmail.com>
This commit is contained in:
@@ -141,6 +141,19 @@ pub fn internode_rpc_max_message_size() -> usize {
|
||||
}
|
||||
|
||||
pub const HEAL_CONTROL_RPC_MAX_MESSAGE_SIZE: usize = 65 * 1024;
|
||||
pub const HEAL_CONTROL_PROTOCOL_VERSION: u32 = 1;
|
||||
pub const HEAL_CONTROL_CAPABILITY_PROBE_PREFIX: &[u8] = b"rustfs-heal-control-capability-v1\0";
|
||||
|
||||
pub fn heal_control_capability_probe(nonce: &[u8; 16]) -> Vec<u8> {
|
||||
let mut probe = Vec::with_capacity(HEAL_CONTROL_CAPABILITY_PROBE_PREFIX.len() + nonce.len());
|
||||
probe.extend_from_slice(HEAL_CONTROL_CAPABILITY_PROBE_PREFIX);
|
||||
probe.extend_from_slice(nonce);
|
||||
probe
|
||||
}
|
||||
|
||||
pub fn is_heal_control_capability_probe(command: &[u8]) -> bool {
|
||||
command.len() == HEAL_CONTROL_CAPABILITY_PROBE_PREFIX.len() + 16 && command.starts_with(HEAL_CONTROL_CAPABILITY_PROBE_PREFIX)
|
||||
}
|
||||
|
||||
/// Builds the stable byte representation authenticated for a heal-control request.
|
||||
///
|
||||
@@ -164,9 +177,32 @@ pub fn canonical_heal_control_request_body(
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
/// Builds the exact acknowledgement expected from a peer that supports the
|
||||
/// heal-control protocol and agrees with the caller's storage topology.
|
||||
pub fn canonical_heal_control_capability_ack(
|
||||
version: u32,
|
||||
topology_fingerprint: &str,
|
||||
probe: &[u8],
|
||||
) -> Result<Vec<u8>, std::num::TryFromIntError> {
|
||||
const DOMAIN: &[u8] = b"rustfs-heal-control-capability-ack-v1\0";
|
||||
|
||||
let fingerprint = topology_fingerprint.as_bytes();
|
||||
let mut body = Vec::with_capacity(DOMAIN.len() + 4 + 8 + fingerprint.len() + 8 + probe.len());
|
||||
body.extend_from_slice(DOMAIN);
|
||||
body.extend_from_slice(&version.to_be_bytes());
|
||||
body.extend_from_slice(&u64::try_from(fingerprint.len())?.to_be_bytes());
|
||||
body.extend_from_slice(fingerprint);
|
||||
body.extend_from_slice(&u64::try_from(probe.len())?.to_be_bytes());
|
||||
body.extend_from_slice(probe);
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod heal_control_tests {
|
||||
use super::canonical_heal_control_request_body;
|
||||
use super::{
|
||||
HEAL_CONTROL_CAPABILITY_PROBE_PREFIX, canonical_heal_control_capability_ack, canonical_heal_control_request_body,
|
||||
heal_control_capability_probe, is_heal_control_capability_probe,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn canonical_heal_control_body_binds_every_field_and_boundary() {
|
||||
@@ -196,6 +232,27 @@ mod heal_control_tests {
|
||||
canonical_heal_control_request_body(1, "a", b"bc").expect("small request should encode")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canonical_capability_ack_binds_version_and_topology() {
|
||||
let probe = heal_control_capability_probe(&[7; 16]);
|
||||
let ack = canonical_heal_control_capability_ack(1, "ab", &probe).expect("small acknowledgement should encode");
|
||||
let mut golden = b"rustfs-heal-control-capability-ack-v1\0".to_vec();
|
||||
golden.extend_from_slice(&1_u32.to_be_bytes());
|
||||
golden.extend_from_slice(&2_u64.to_be_bytes());
|
||||
golden.extend_from_slice(b"ab");
|
||||
golden.extend_from_slice(&u64::try_from(probe.len()).unwrap().to_be_bytes());
|
||||
golden.extend_from_slice(&probe);
|
||||
assert_eq!(ack, golden);
|
||||
assert_ne!(ack, canonical_heal_control_capability_ack(2, "ab", &probe).unwrap());
|
||||
assert_ne!(ack, canonical_heal_control_capability_ack(1, "ac", &probe).unwrap());
|
||||
assert_ne!(
|
||||
ack,
|
||||
canonical_heal_control_capability_ack(1, "ab", &heal_control_capability_probe(&[8; 16])).unwrap()
|
||||
);
|
||||
assert!(is_heal_control_capability_probe(&probe));
|
||||
assert!(!is_heal_control_capability_probe(HEAL_CONTROL_CAPABILITY_PROBE_PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether internode metadata RPCs should send only the msgpack `_bin` payloads and leave the JSON
|
||||
|
||||
Reference in New Issue
Block a user