mirror of
https://github.com/telemt/telemt.git
synced 2026-10-07 18:05:57 +03:00
Descriptor-anchored Secured Filesystem Operations
This commit is contained in:
@@ -42,7 +42,7 @@ pub(super) fn ip_prefix_hint_key(peer_ip: IpAddr) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn sticky_hint_get_by_ip(shared: &ProxySharedState, peer_ip: IpAddr) -> Option<u32> {
|
||||
pub(super) fn sticky_hint_get_by_ip(shared: &ProxySharedState, peer_ip: IpAddr) -> Option<u64> {
|
||||
shared
|
||||
.handshake
|
||||
.sticky_user_by_ip
|
||||
@@ -53,7 +53,7 @@ pub(super) fn sticky_hint_get_by_ip(shared: &ProxySharedState, peer_ip: IpAddr)
|
||||
pub(super) fn sticky_hint_get_by_ip_prefix(
|
||||
shared: &ProxySharedState,
|
||||
peer_ip: IpAddr,
|
||||
) -> Option<u32> {
|
||||
) -> Option<u64> {
|
||||
shared
|
||||
.handshake
|
||||
.sticky_user_by_ip_prefix
|
||||
@@ -61,7 +61,7 @@ pub(super) fn sticky_hint_get_by_ip_prefix(
|
||||
.map(|entry| *entry)
|
||||
}
|
||||
|
||||
pub(super) fn sticky_hint_get_by_sni(shared: &ProxySharedState, sni: &str) -> Option<u32> {
|
||||
pub(super) fn sticky_hint_get_by_sni(shared: &ProxySharedState, sni: &str) -> Option<u64> {
|
||||
let key = sni_hint_hash(sni);
|
||||
shared
|
||||
.handshake
|
||||
@@ -73,20 +73,20 @@ pub(super) fn sticky_hint_get_by_sni(shared: &ProxySharedState, sni: &str) -> Op
|
||||
pub(super) fn sticky_hint_record_success_in(
|
||||
shared: &ProxySharedState,
|
||||
peer_ip: IpAddr,
|
||||
user_id: u32,
|
||||
hint_key: u64,
|
||||
sni: Option<&str>,
|
||||
) {
|
||||
bounded_sticky_hint_upsert(
|
||||
&shared.handshake.sticky_user_by_ip,
|
||||
&shared.handshake.sticky_user_by_ip_slots,
|
||||
peer_ip,
|
||||
user_id,
|
||||
hint_key,
|
||||
);
|
||||
bounded_sticky_hint_upsert(
|
||||
&shared.handshake.sticky_user_by_ip_prefix,
|
||||
&shared.handshake.sticky_user_by_ip_prefix_slots,
|
||||
ip_prefix_hint_key(peer_ip),
|
||||
user_id,
|
||||
hint_key,
|
||||
);
|
||||
|
||||
if let Some(sni) = sni {
|
||||
@@ -94,34 +94,55 @@ pub(super) fn sticky_hint_record_success_in(
|
||||
&shared.handshake.sticky_user_by_sni_hash,
|
||||
&shared.handshake.sticky_user_by_sni_hash_slots,
|
||||
sni_hint_hash(sni),
|
||||
user_id,
|
||||
hint_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn bounded_sticky_hint_upsert<K>(
|
||||
entries: &DashMap<K, u32>,
|
||||
entries: &DashMap<K, u64>,
|
||||
slots: &crate::slot_budget::SlotBudget,
|
||||
key: K,
|
||||
user_id: u32,
|
||||
hint_key: u64,
|
||||
) where
|
||||
K: Eq + Hash,
|
||||
K: Clone + Eq + Hash,
|
||||
{
|
||||
match entries.entry(key) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.insert(user_id);
|
||||
if let Some(mut existing) = entries.get_mut(&key) {
|
||||
*existing = hint_key;
|
||||
return;
|
||||
}
|
||||
|
||||
for _ in 0..2 {
|
||||
if let Some(slot) = slots.try_acquire() {
|
||||
match entries.entry(key.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.insert(hint_key);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(hint_key);
|
||||
slot.commit();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
let Some(slot) = slots.try_acquire() else {
|
||||
return;
|
||||
};
|
||||
entry.insert(user_id);
|
||||
slot.commit();
|
||||
|
||||
let Some((victim_key, victim_hint_key)) = entries
|
||||
.iter()
|
||||
.next()
|
||||
.map(|entry| (entry.key().clone(), *entry.value()))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if entries
|
||||
.remove_if(&victim_key, |_, current| *current == victim_hint_key)
|
||||
.is_some()
|
||||
{
|
||||
slots.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn record_recent_user_success_in(shared: &ProxySharedState, user_id: u32) {
|
||||
pub(super) fn record_recent_user_success_in(shared: &ProxySharedState, hint_key: u64) {
|
||||
let ring = &shared.handshake.recent_user_ring;
|
||||
if ring.is_empty() {
|
||||
return;
|
||||
@@ -131,7 +152,7 @@ pub(super) fn record_recent_user_success_in(shared: &ProxySharedState, user_id:
|
||||
.recent_user_ring_seq
|
||||
.fetch_add(1, Ordering::Relaxed);
|
||||
let idx = (seq as usize) % ring.len();
|
||||
ring[idx].store(user_id.saturating_add(1), Ordering::Relaxed);
|
||||
ring[idx].store(hint_key, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub(super) fn mark_candidate_if_new(
|
||||
@@ -387,7 +408,7 @@ mod bounded_registry_tests {
|
||||
sticky_hint_record_success_in(
|
||||
shared.as_ref(),
|
||||
peer_ip,
|
||||
index as u32,
|
||||
index as u64 | 1,
|
||||
Some(&format!("host-{index}.example")),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ pub(crate) fn auth_probe_fail_streak_for_testing_in_shared(
|
||||
}
|
||||
|
||||
pub(crate) fn clear_auth_probe_state_for_testing_in_shared(shared: &ProxySharedState) {
|
||||
let removed = shared.handshake.auth_probe.len();
|
||||
assert_eq!(shared.handshake.auth_probe_slots.used(), removed);
|
||||
shared.handshake.auth_probe.clear();
|
||||
shared.handshake.auth_probe_slots.reset_for_testing();
|
||||
shared.handshake.auth_probe_slots.release_many(removed);
|
||||
match shared.handshake.auth_probe_saturation.lock() {
|
||||
Ok(mut saturation) => {
|
||||
*saturation = None;
|
||||
@@ -35,6 +37,28 @@ pub(crate) fn clear_auth_probe_state_for_testing_in_shared(shared: &ProxySharedS
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert_auth_probe_state_for_testing_in_shared(
|
||||
shared: &ProxySharedState,
|
||||
peer_ip: IpAddr,
|
||||
state: AuthProbeState,
|
||||
) {
|
||||
let peer_ip = normalize_auth_probe_ip(peer_ip);
|
||||
let slot = shared
|
||||
.handshake
|
||||
.auth_probe_slots
|
||||
.try_acquire()
|
||||
.expect("test auth-probe registry capacity must be available");
|
||||
match shared.handshake.auth_probe.entry(peer_ip) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.insert(state);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(state);
|
||||
slot.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn auth_probe_state_for_testing_in_shared(
|
||||
shared: &ProxySharedState,
|
||||
) -> &DashMap<IpAddr, AuthProbeState> {
|
||||
|
||||
@@ -151,10 +151,14 @@ where
|
||||
if let Some(snapshot) = config.runtime_user_auth() {
|
||||
let sticky_ip_hint = sticky_hint_get_by_ip(shared, peer.ip());
|
||||
let sticky_prefix_hint = sticky_hint_get_by_ip_prefix(shared, peer.ip());
|
||||
let sticky_ip_candidates = sticky_ip_hint
|
||||
.and_then(|hint_key| snapshot.candidate_ids_by_hint_key(hint_key));
|
||||
let sticky_prefix_candidates = sticky_prefix_hint
|
||||
.and_then(|hint_key| snapshot.candidate_ids_by_hint_key(hint_key));
|
||||
let preferred_user_id = preferred_user.and_then(|user| snapshot.user_id_by_name(user));
|
||||
let exact_user_id = exact_user.and_then(|user| snapshot.user_id_by_name(user));
|
||||
let has_hint = sticky_ip_hint.is_some()
|
||||
|| sticky_prefix_hint.is_some()
|
||||
let has_hint = sticky_ip_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| sticky_prefix_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| preferred_user_id.is_some()
|
||||
|| exact_user_id.is_some();
|
||||
let overload = auth_probe_saturation_is_throttled_in(shared, Instant::now());
|
||||
@@ -204,9 +208,17 @@ where
|
||||
|
||||
let mut matched = exact_user_id.is_some_and(|user_id| try_user_id!(user_id));
|
||||
if exact_user.is_none()
|
||||
&& let Some(user_id) = sticky_ip_hint
|
||||
&& let Some(candidate_ids) = sticky_ip_candidates
|
||||
{
|
||||
matched = try_user_id!(user_id);
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if exact_user.is_none()
|
||||
@@ -218,9 +230,17 @@ where
|
||||
|
||||
if exact_user.is_none()
|
||||
&& !matched
|
||||
&& let Some(user_id) = sticky_prefix_hint
|
||||
&& let Some(candidate_ids) = sticky_prefix_candidates
|
||||
{
|
||||
matched = try_user_id!(user_id);
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if exact_user.is_none() && !matched && !budget_exhausted {
|
||||
@@ -231,18 +251,22 @@ where
|
||||
.recent_user_ring_seq
|
||||
.load(Ordering::Relaxed);
|
||||
let scan_limit = ring.len().min(RECENT_USER_RING_SCAN_LIMIT);
|
||||
for offset in 0..scan_limit {
|
||||
'recent_hints: for offset in 0..scan_limit {
|
||||
let idx = (next_seq as usize + ring.len() - 1 - offset) % ring.len();
|
||||
let encoded_user_id = ring[idx].load(Ordering::Relaxed);
|
||||
if encoded_user_id == 0 {
|
||||
let hint_key = ring[idx].load(Ordering::Relaxed);
|
||||
if hint_key == 0 {
|
||||
continue;
|
||||
}
|
||||
if try_user_id!(encoded_user_id - 1) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
if let Some(candidate_ids) = snapshot.candidate_ids_by_hint_key(hint_key) {
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break 'recent_hints;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break 'recent_hints;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,8 +381,10 @@ where
|
||||
|
||||
auth_probe_record_success_in(shared, peer.ip());
|
||||
if let Some(user_id) = matched_user_id {
|
||||
sticky_hint_record_success_in(shared, peer.ip(), user_id, None);
|
||||
record_recent_user_success_in(shared, user_id);
|
||||
if let Some(entry) = snapshot.entry_by_id(user_id) {
|
||||
sticky_hint_record_success_in(shared, peer.ip(), entry.hint_key, None);
|
||||
record_recent_user_success_in(shared, entry.hint_key);
|
||||
}
|
||||
}
|
||||
|
||||
let max_pending = config.general.crypto_pending_buffer;
|
||||
|
||||
@@ -396,8 +396,18 @@ where
|
||||
auth_probe_record_success_in(shared, peer.ip());
|
||||
|
||||
if let Some(user_id) = validated_user_id {
|
||||
sticky_hint_record_success_in(shared, peer.ip(), user_id, client_sni.as_deref());
|
||||
record_recent_user_success_in(shared, user_id);
|
||||
if let Some(entry) = config
|
||||
.runtime_user_auth()
|
||||
.and_then(|snapshot| snapshot.entry_by_id(user_id))
|
||||
{
|
||||
sticky_hint_record_success_in(
|
||||
shared,
|
||||
peer.ip(),
|
||||
entry.hint_key,
|
||||
client_sni.as_deref(),
|
||||
);
|
||||
record_recent_user_success_in(shared, entry.hint_key);
|
||||
}
|
||||
}
|
||||
|
||||
HandshakeResult::Success((
|
||||
|
||||
@@ -40,11 +40,17 @@ pub(super) async fn validate_tls_client(
|
||||
};
|
||||
|
||||
let sticky_ip_hint = sticky_hint_get_by_ip(shared, peer.ip());
|
||||
let sticky_ip_candidates = sticky_ip_hint
|
||||
.and_then(|hint_key| snapshot.candidate_ids_by_hint_key(hint_key));
|
||||
let preferred_user_id = preferred_user_hint.and_then(|user| snapshot.user_id_by_name(user));
|
||||
let sticky_sni_hint = client_sni
|
||||
.as_deref()
|
||||
.and_then(|sni| sticky_hint_get_by_sni(shared, sni));
|
||||
let sticky_sni_candidates = sticky_sni_hint
|
||||
.and_then(|hint_key| snapshot.candidate_ids_by_hint_key(hint_key));
|
||||
let sticky_prefix_hint = sticky_hint_get_by_ip_prefix(shared, peer.ip());
|
||||
let sticky_prefix_candidates = sticky_prefix_hint
|
||||
.and_then(|hint_key| snapshot.candidate_ids_by_hint_key(hint_key));
|
||||
let sni_candidates = client_sni
|
||||
.as_deref()
|
||||
.and_then(|sni| snapshot.sni_candidates(sni));
|
||||
@@ -52,10 +58,10 @@ pub(super) async fn validate_tls_client(
|
||||
.as_deref()
|
||||
.and_then(|sni| snapshot.sni_initial_candidates(sni));
|
||||
|
||||
let has_hint = sticky_ip_hint.is_some()
|
||||
let has_hint = sticky_ip_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| preferred_user_id.is_some()
|
||||
|| sticky_sni_hint.is_some()
|
||||
|| sticky_prefix_hint.is_some()
|
||||
|| sticky_sni_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| sticky_prefix_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| sni_candidates.is_some_and(|ids| !ids.is_empty())
|
||||
|| sni_initial_candidates.is_some_and(|ids| !ids.is_empty());
|
||||
let overload = auth_probe_saturation_is_throttled_in(shared, Instant::now());
|
||||
@@ -95,20 +101,44 @@ pub(super) async fn validate_tls_client(
|
||||
}
|
||||
|
||||
let mut matched = false;
|
||||
if let Some(user_id) = sticky_ip_hint {
|
||||
matched = try_user_id!(user_id);
|
||||
if let Some(candidate_ids) = sticky_ip_candidates {
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !matched && let Some(user_id) = preferred_user_id {
|
||||
matched = try_user_id!(user_id);
|
||||
}
|
||||
|
||||
if !matched && let Some(user_id) = sticky_sni_hint {
|
||||
matched = try_user_id!(user_id);
|
||||
if !matched && let Some(candidate_ids) = sticky_sni_candidates {
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !matched && let Some(user_id) = sticky_prefix_hint {
|
||||
matched = try_user_id!(user_id);
|
||||
if !matched && let Some(candidate_ids) = sticky_prefix_candidates {
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !matched
|
||||
@@ -149,18 +179,22 @@ pub(super) async fn validate_tls_client(
|
||||
.recent_user_ring_seq
|
||||
.load(Ordering::Relaxed);
|
||||
let scan_limit = ring.len().min(RECENT_USER_RING_SCAN_LIMIT);
|
||||
for offset in 0..scan_limit {
|
||||
'recent_hints: for offset in 0..scan_limit {
|
||||
let idx = (next_seq as usize + ring.len() - 1 - offset) % ring.len();
|
||||
let encoded_user_id = ring[idx].load(Ordering::Relaxed);
|
||||
if encoded_user_id == 0 {
|
||||
let hint_key = ring[idx].load(Ordering::Relaxed);
|
||||
if hint_key == 0 {
|
||||
continue;
|
||||
}
|
||||
if try_user_id!(encoded_user_id - 1) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break;
|
||||
if let Some(candidate_ids) = snapshot.candidate_ids_by_hint_key(hint_key) {
|
||||
for &user_id in candidate_ids {
|
||||
if try_user_id!(user_id) {
|
||||
matched = true;
|
||||
break 'recent_hints;
|
||||
}
|
||||
if budget_exhausted {
|
||||
break 'recent_hints;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user