When running on Windows with Wintun, assigning an overlay IPv4 address
via netsh triggers Duplicate Address Detection (DAD). During this tentative
window, binding a UDP socket to the address returns WSAEADDRNOTAVAIL
(os error 10049), causing the agent to prematurely log a false-positive
PluginError ('allocated ... but the address is not on any interface').
Furthermore, Russian TSPU / DPI filters block unrecognized SNIs and
non-standard ALPNs on TCP port 443 with TCP RST injection (os error 10054).
The default 'tsunagi.local' SNI and 'tsunagi-tls/1' ALPN were trivially
fingerprinted.
Key improvements:
- In crates/tsunagi/src/overlay/tun.rs, enhance address_is_local on Windows
to inspect active OS interfaces via netdev::get_interfaces() if UDP
bind fails.
- In crates/tsunagi/src/agent/network.rs, introduce an asynchronous retry
loop in publish_allocations on Windows (up to 1.5s in 150ms intervals)
to gracefully allow DAD and IP helper updates to complete before
reporting missing addresses.
- In crates/tsunagi-tcp-tls, add support for custom SNI domain via
protocol options (-o sni=<domain>), defaulting to 'cloudflare.com'
instead of 'tsunagi.local'.
- Switch TLS ALPN in tsunagi-tcp-tls from 'tsunagi-tls/1' to 'http/1.1'
to camouflage handshake traffic as standard HTTPS web traffic.
- Update tests to verify custom and default SNI behavior.
Validation on Linux:
- cargo fmt --all -- --check passed.
- cargo clippy --locked --workspace --all-targets -- -D warnings passed.
- cargo test --locked --workspace --all-targets passed (348 tests passed).
- cargo build --release passed.
54 lines
1.8 KiB
Rust
54 lines
1.8 KiB
Rust
#![allow(missing_docs)]
|
|
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
|
|
use rustls::client::danger::ServerCertVerifier;
|
|
use rustls::pki_types::{ServerName, UnixTime};
|
|
use tsunagi::identity::DeviceIdentity;
|
|
use tsunagi_tcp_tls::cert::{PinnedEndpointVerifier, generate_self_signed_cert};
|
|
|
|
#[test]
|
|
fn test_generate_and_pin_certificate() {
|
|
let identity = DeviceIdentity::generate();
|
|
let peer_id = identity.endpoint_id();
|
|
|
|
let (cert, _key) =
|
|
generate_self_signed_cert(&identity.secret_bytes(), tsunagi_tcp_tls::cert::DEFAULT_SNI)
|
|
.expect("cert generation should succeed");
|
|
|
|
let verifier = PinnedEndpointVerifier::new(peer_id);
|
|
let server_name = ServerName::try_from(tsunagi_tcp_tls::cert::DEFAULT_SNI).unwrap();
|
|
|
|
let verified = verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
|
|
|
|
assert!(
|
|
verified.is_ok(),
|
|
"certificate matching peer endpoint id must verify"
|
|
);
|
|
|
|
let other_identity = DeviceIdentity::generate();
|
|
let wrong_verifier = PinnedEndpointVerifier::new(other_identity.endpoint_id());
|
|
|
|
let wrong_verified =
|
|
wrong_verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
|
|
|
|
assert!(
|
|
wrong_verified.is_err(),
|
|
"certificate with wrong endpoint id must fail verification"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_make_client_and_server_config() {
|
|
let identity = DeviceIdentity::generate();
|
|
let peer_id = identity.endpoint_id();
|
|
|
|
let (cert, key) = generate_self_signed_cert(&identity.secret_bytes(), "custom.example.com")
|
|
.expect("cert generation should succeed");
|
|
|
|
let server_config = tsunagi_tcp_tls::cert::make_server_config(cert, key);
|
|
assert!(server_config.is_ok());
|
|
|
|
let client_config = tsunagi_tcp_tls::cert::make_client_config(peer_id);
|
|
assert!(client_config.is_ok());
|
|
}
|