Files
alknet/crates/alknet-core/src/interface/config.rs
glm-5.1 752ead9d9f feat(core): implement RawFramingInterface accept/recv/send with first-frame auth
Implement RawFramingSession with tokio::io::split for read/write halves,
BufReader/BufWriter for buffered I/O, and decode_with_remainder for
partial frame reassembly. Add first-frame authentication via
IdentityProvider::resolve_from_token(). Add RawFramingConfig.auth
field for IdentityProvider reference. Update SshInterface test for
new RawFramingConfig shape.
2026-06-09 10:52:54 +00:00

271 lines
8.0 KiB
Rust

use std::sync::Arc;
use arc_swap::ArcSwap;
use russh::keys::PrivateKey;
use serde::{Deserialize, Serialize};
use crate::auth::IdentityProvider;
use crate::config::DynamicConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum StreamInterfaceKind {
Ssh,
RawFraming,
}
impl std::fmt::Display for StreamInterfaceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StreamInterfaceKind::Ssh => write!(f, "ssh"),
StreamInterfaceKind::RawFraming => write!(f, "raw-framing"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MessageInterfaceKind {
Http,
Dns,
}
impl std::fmt::Display for MessageInterfaceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageInterfaceKind::Http => write!(f, "http"),
MessageInterfaceKind::Dns => write!(f, "dns"),
}
}
}
#[non_exhaustive]
pub enum InterfaceConfig {
Ssh(SshInterfaceConfig),
RawFraming(RawFramingConfig),
}
impl InterfaceConfig {
pub fn kind(&self) -> StreamInterfaceKind {
#[allow(unreachable_patterns)]
match self {
InterfaceConfig::Ssh(_) => StreamInterfaceKind::Ssh,
InterfaceConfig::RawFraming(_) => StreamInterfaceKind::RawFraming,
_ => StreamInterfaceKind::Ssh,
}
}
}
#[non_exhaustive]
pub enum StreamInterfaceConfig {
Ssh(SshInterfaceConfig),
RawFraming(RawFramingConfig),
}
impl StreamInterfaceConfig {
pub fn kind(&self) -> StreamInterfaceKind {
match self {
StreamInterfaceConfig::Ssh(_) => StreamInterfaceKind::Ssh,
StreamInterfaceConfig::RawFraming(_) => StreamInterfaceKind::RawFraming,
}
}
}
impl std::fmt::Display for StreamInterfaceConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StreamInterfaceConfig::Ssh(_) => write!(f, "ssh"),
StreamInterfaceConfig::RawFraming(_) => write!(f, "raw-framing"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum MessageInterfaceConfig {
Http(HttpInterfaceConfig),
Dns(DnsInterfaceConfig),
}
impl MessageInterfaceConfig {
pub fn kind(&self) -> MessageInterfaceKind {
match self {
MessageInterfaceConfig::Http(_) => MessageInterfaceKind::Http,
MessageInterfaceConfig::Dns(_) => MessageInterfaceKind::Dns,
}
}
}
impl std::fmt::Display for MessageInterfaceConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageInterfaceConfig::Http(_) => write!(f, "http"),
MessageInterfaceConfig::Dns(_) => write!(f, "dns"),
}
}
}
pub struct SshInterfaceConfig {
pub auth: Arc<dyn IdentityProvider>,
pub forwarding: Arc<ArcSwap<DynamicConfig>>,
pub host_key: Arc<PrivateKey>,
}
pub struct RawFramingConfig {
pub auth: Arc<dyn IdentityProvider>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HttpInterfaceConfig {
pub bind_addr: std::net::SocketAddr,
pub tls: bool,
pub stealth: bool,
}
impl std::fmt::Display for HttpInterfaceConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "http {}", self.bind_addr)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DnsInterfaceConfig {
pub bind_addr: std::net::SocketAddr,
pub tls: bool,
}
impl std::fmt::Display for DnsInterfaceConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dns {}", self.bind_addr)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::auth::ConfigIdentityProvider;
#[test]
fn stream_interface_kind_display() {
assert_eq!(StreamInterfaceKind::Ssh.to_string(), "ssh");
assert_eq!(StreamInterfaceKind::RawFraming.to_string(), "raw-framing");
}
#[test]
fn message_interface_kind_display() {
assert_eq!(MessageInterfaceKind::Http.to_string(), "http");
assert_eq!(MessageInterfaceKind::Dns.to_string(), "dns");
}
#[test]
fn stream_interface_config_kind() {
let auth = Arc::new(crate::auth::ConfigIdentityProvider::new(Arc::new(
ArcSwap::new(Arc::new(DynamicConfig::default())),
)));
let ssh_config = StreamInterfaceConfig::Ssh(SshInterfaceConfig {
auth,
forwarding: Arc::new(ArcSwap::new(Arc::new(DynamicConfig::default()))),
host_key: Arc::new(
russh::keys::PrivateKey::random(
&mut rand_core::OsRng,
russh::keys::Algorithm::Ed25519,
)
.unwrap(),
),
});
assert_eq!(ssh_config.kind(), StreamInterfaceKind::Ssh);
let raw_config = StreamInterfaceConfig::RawFraming(RawFramingConfig {
auth: Arc::new(ConfigIdentityProvider::new(Arc::new(ArcSwap::new(
Arc::new(DynamicConfig::default()),
)))),
});
assert_eq!(raw_config.kind(), StreamInterfaceKind::RawFraming);
}
#[test]
fn message_interface_config_kind() {
let http_config = MessageInterfaceConfig::Http(HttpInterfaceConfig {
bind_addr: "127.0.0.1:8080".parse().unwrap(),
tls: false,
stealth: false,
});
assert_eq!(http_config.kind(), MessageInterfaceKind::Http);
let dns_config = MessageInterfaceConfig::Dns(DnsInterfaceConfig {
bind_addr: "127.0.0.1:53".parse().unwrap(),
tls: false,
});
assert_eq!(dns_config.kind(), MessageInterfaceKind::Dns);
}
#[test]
fn stream_interface_kind_equality() {
assert_eq!(StreamInterfaceKind::Ssh, StreamInterfaceKind::Ssh);
assert_eq!(
StreamInterfaceKind::RawFraming,
StreamInterfaceKind::RawFraming
);
assert_ne!(StreamInterfaceKind::Ssh, StreamInterfaceKind::RawFraming);
}
#[test]
fn message_interface_kind_equality() {
assert_eq!(MessageInterfaceKind::Http, MessageInterfaceKind::Http);
assert_eq!(MessageInterfaceKind::Dns, MessageInterfaceKind::Dns);
assert_ne!(MessageInterfaceKind::Http, MessageInterfaceKind::Dns);
}
#[test]
fn raw_framing_config_minimal() {
let auth: Arc<dyn IdentityProvider> = Arc::new(ConfigIdentityProvider::new(Arc::new(
ArcSwap::new(Arc::new(DynamicConfig::default())),
)));
let _config = RawFramingConfig { auth };
}
#[test]
fn http_interface_config_display() {
let config = HttpInterfaceConfig {
bind_addr: "127.0.0.1:8080".parse().unwrap(),
tls: true,
stealth: true,
};
assert_eq!(config.to_string(), "http 127.0.0.1:8080");
}
#[test]
fn dns_interface_config_display() {
let config = DnsInterfaceConfig {
bind_addr: "127.0.0.1:53".parse().unwrap(),
tls: false,
};
assert_eq!(config.to_string(), "dns 127.0.0.1:53");
}
#[test]
fn http_interface_config_serialization() {
let config = HttpInterfaceConfig {
bind_addr: "127.0.0.1:8080".parse().unwrap(),
tls: true,
stealth: false,
};
let serialized = serde_json::to_string(&config).unwrap();
let deserialized: HttpInterfaceConfig = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.bind_addr, config.bind_addr);
assert_eq!(deserialized.tls, config.tls);
}
#[test]
fn dns_interface_config_serialization() {
let config = DnsInterfaceConfig {
bind_addr: "0.0.0.0:53".parse().unwrap(),
tls: true,
};
let serialized = serde_json::to_string(&config).unwrap();
let deserialized: DnsInterfaceConfig = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.bind_addr, config.bind_addr);
assert_eq!(deserialized.tls, config.tls);
}
}