Proxy Shared User Drafts

This commit is contained in:
Alexey
2026-09-14 20:19:48 +03:00
parent b37f1ebdeb
commit 0ac236955a
59 changed files with 1485 additions and 401 deletions
+20
View File
@@ -9,6 +9,7 @@ use rand::RngExt;
use serde::{Deserialize, Serialize};
use tracing::warn;
use crate::crypto::sha256;
use crate::error::{ProxyError, Result};
use super::defaults::*;
@@ -230,6 +231,25 @@ impl ProxyConfig {
self.runtime_user_auth.as_deref()
}
/// Returns the credential identity frozen into this runtime snapshot.
pub(crate) fn runtime_user_credential_id(&self, user: &str) -> Option<[u8; 16]> {
self.runtime_user_auth()
.and_then(|snapshot| snapshot.credential_id_by_name(user))
.or_else(|| {
self.access
.users
.get(user)
.and_then(|secret| hex::decode(secret).ok())
.and_then(|secret| <[u8; 16]>::try_from(secret).ok())
.map(|secret| {
let digest = sha256(&secret);
let mut credential_id = [0; 16];
credential_id.copy_from_slice(&digest[..16]);
credential_id
})
})
}
/// Validates cross-field configuration invariants after deserialization.
pub fn validate(&self) -> Result<()> {
if self.access.users.is_empty() {
+13
View File
@@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use crate::crypto::sha256;
use crate::error::{ProxyError, Result};
const ACCESS_SECRET_BYTES: usize = 16;
@@ -19,6 +20,8 @@ pub(crate) struct UserAuthSnapshot {
pub(crate) struct UserAuthEntry {
pub(crate) user: String,
pub(crate) secret: [u8; ACCESS_SECRET_BYTES],
/// Stable secret identity used by process-wide admission fencing.
pub(crate) credential_id: [u8; 16],
}
impl UserAuthSnapshot {
@@ -46,9 +49,13 @@ impl UserAuthSnapshot {
let mut secret = [0u8; ACCESS_SECRET_BYTES];
secret.copy_from_slice(&decoded);
let digest = sha256(&secret);
let mut credential_id = [0; 16];
credential_id.copy_from_slice(&digest[..16]);
entries.push(UserAuthEntry {
user: user.clone(),
secret,
credential_id,
});
by_name.insert(user.clone(), user_id);
sni_index
@@ -88,6 +95,12 @@ impl UserAuthSnapshot {
self.entries.get(idx)
}
pub(crate) fn credential_id_by_name(&self, user: &str) -> Option<[u8; 16]> {
self.user_id_by_name(user)
.and_then(|user_id| self.entry_by_id(user_id))
.map(|entry| entry.credential_id)
}
pub(crate) fn sni_candidates(&self, sni: &str) -> Option<&[u32]> {
self.sni_index
.get(&Self::sni_lookup_hash(sni))
+1
View File
@@ -63,6 +63,7 @@ pub(super) fn rebuild(config: &mut ProxyConfig) -> Result<()> {
host: vhost.host.clone(),
public_addr: vhost.public_addr,
user: profile.user.clone(),
credential_id: auth_entry.credential_id,
secret_mode: profile.secret_mode,
carrier: config.web.carrier,
carrier_negotiation_enabled: config.web.carrier_negotiation_enabled(),
+2
View File
@@ -35,6 +35,8 @@ pub(crate) struct WebRuntimeProfile {
pub(crate) public_addr: SocketAddr,
/// Exact access user authenticated by logical streams.
pub(crate) user: String,
/// Stable credential identity used by process-wide admission fencing.
pub(crate) credential_id: [u8; 16],
/// Client secret representation and inner protocol policy.
pub(crate) secret_mode: WebSecretMode,
/// Sole carrier or final fallback frozen into the issued bridge policy.