fix(transport): harden QUIC admission boundaries

This commit is contained in:
sechmachine
2026-08-12 20:04:51 +07:00
parent 111092becb
commit 09f40eb9c7
7 changed files with 508 additions and 78 deletions
+83 -31
View File
@@ -46,6 +46,7 @@ type admission struct {
lastTranscript []byte
reusable bool
failWorkOnce bool
releaseFails bool
authority protocol.SessionAuthority
work protocol.ProviderSessionWork
public ed25519.PublicKey
@@ -67,11 +68,15 @@ func (a *admission) ProviderWork(context.Context, protocol.SessionAuthority) (pr
if a.failWorkOnce { a.failWorkOnce = false; return protocol.ProviderSessionWork{}, context.DeadlineExceeded }
return a.work, nil
}
func (a *admission) Release(context.Context, protocol.SessionAuthority) error { return nil }
func (a *admission) Release(context.Context, protocol.SessionAuthority) error {
if a.releaseFails { return errors.New("release failed") }
return nil
}
type provider struct{}
type provider struct { failStart bool }
type session struct { state protocol.ProviderState; video chan gateway.ProviderMedia; audio chan gateway.ProviderMedia; events chan gateway.ProviderEvent }
func (provider) Start(_ context.Context, request gateway.LaunchRequest) (gateway.ProviderSession, error) {
func (p provider) Start(_ context.Context, request gateway.LaunchRequest) (gateway.ProviderSession, error) {
if p.failStart { return nil, context.DeadlineExceeded }
return &session{state: protocol.ProviderState{Version:"1", SessionID:request.SessionID, State:gateway.ProviderStateReady, Channels:[]string{"video","audio","input","feedback"}}, video:make(chan gateway.ProviderMedia), audio:make(chan gateway.ProviderMedia), events:make(chan gateway.ProviderEvent)}, nil
}
func (s *session) Ready(context.Context) error { return nil }
@@ -125,9 +130,9 @@ func main() {
_, firstReplayErr := replayGuard.Admit(context.Background(),replayRequest)
_, secondReplayErr := replayGuard.Admit(context.Background(),replayRequest)
replayRejected := firstReplayErr == nil && errors.Is(secondReplayErr,gateway.ErrAdmissionRejected)
admissionService := &admission{authority:authority,work:work,public:admissionKey.Public().(ed25519.PublicKey),reusable:mode == "reusable",failWorkOnce:mode == "post-retryable"}
admissionService := &admission{authority:authority,work:work,public:admissionKey.Public().(ed25519.PublicKey),reusable:mode == "reusable",failWorkOnce:mode == "post-retryable",releaseFails:mode == "cleanup-release-failure"}
newService := func() *gateway.Server {
service, err := gateway.NewServer(gateway.ServerConfig{ListenAddress:"127.0.0.1:0",TLSConfig:server,GatewayID:authority.GatewayID,Admission:admissionService,Provider:provider{}}); if err != nil { panic(err) }
service, err := gateway.NewServer(gateway.ServerConfig{ListenAddress:"127.0.0.1:0",TLSConfig:server,GatewayID:authority.GatewayID,Admission:admissionService,Provider:provider{failStart:mode == "provider-start-lost-response" || mode == "cleanup-release-failure"}}); if err != nil { panic(err) }
return service
}
service := newService()
@@ -139,7 +144,7 @@ func main() {
services = []*gateway.Server{draining, service}
addresses = []string{draining.Addr().String(), service.Addr().String()}
}
if mode == "post-retryable" {
if mode == "post-retryable" || mode == "provider-start-lost-response" || mode == "cleanup-release-failure" {
second := newService()
services = []*gateway.Server{service, second}
addresses = []string{service.Addr().String(), second.Addr().String()}
@@ -303,7 +308,7 @@ fn callback_ed25519_signer_completes_tls13_quic_admission_without_private_key_in
assert_eq!(session.authority().session_id(), "session-1");
assert_eq!(admission_calls.load(Ordering::SeqCst), 1);
assert!(tls_calls.load(Ordering::SeqCst) >= 1);
assert_eq!(tls_calls.load(Ordering::SeqCst), 1);
assert!(admission_inputs
.lock()
.expect("admission transcript")
@@ -598,6 +603,41 @@ fn overall_deadline_includes_synchronous_admission_signing() {
assert!(started.elapsed() < Duration::from_secs(2));
}
#[test]
fn tls_signer_finite_deadline_overrun_is_cancelled_not_tls() {
let oracle = Oracle::start("");
let manifest = ConnectionManifest::decode(oracle.ready.manifest.as_bytes()).expect("manifest");
let credential =
NativeTunnelCredential::decode(oracle.ready.credential.as_bytes()).expect("credential");
let admission_key = test_key(&oracle.ready.admission_key);
let tls_key = test_key(&oracle.ready.client_key);
let cancellation = Cancellation::new();
let trigger = cancellation.clone();
let canceller = thread::spawn(move || {
thread::sleep(Duration::from_millis(25));
trigger.cancel();
});
let result =
tokio::runtime::Runtime::new()
.expect("runtime")
.block_on(connect_with_cancellation(
&manifest,
&credential,
Signers::new(
AdmissionSigner::new(signer(admission_key)),
TlsEd25519Signer::new(move |message| {
thread::sleep(Duration::from_millis(100));
signer(Arc::clone(&tls_key))(message)
}),
),
"2026-08-12T00:00:00Z",
Duration::from_secs(5),
&cancellation,
));
canceller.join().expect("canceller");
assert_eq!(result.err(), Some(CoreError::Cancelled));
}
#[test]
fn explicit_cancellation_interrupts_network_wait() {
let oracle = Oracle::start("");
@@ -733,29 +773,6 @@ fn invalid_first_manifest_address_does_not_block_live_second_address() {
fn retryable_stable_error_advances_to_live_manifest_address() {
let oracle = Oracle::start("retryable");
let manifest = ConnectionManifest::decode(oracle.ready.manifest.as_bytes()).expect("manifest");
let credential =
NativeTunnelCredential::decode(oracle.ready.credential.as_bytes()).expect("credential");
let runtime = tokio::runtime::Runtime::new().expect("runtime");
let session = runtime
.block_on(connect(
&manifest,
&credential,
recording_signers(
test_key(&oracle.ready.admission_key),
test_key(&oracle.ready.client_key),
Arc::new(Mutex::new(Vec::new())),
),
"2026-08-12T00:00:00Z",
Duration::from_secs(5),
))
.expect("retryable draining response advanced to live address");
runtime.block_on(session.close());
}
#[test]
fn post_admission_retry_uses_a_fresh_signed_nonce() {
let oracle = Oracle::start("post-retryable");
let manifest = ConnectionManifest::decode(oracle.ready.manifest.as_bytes()).expect("manifest");
let credential =
NativeTunnelCredential::decode(oracle.ready.credential.as_bytes()).expect("credential");
let transcripts = Arc::new(Mutex::new(Vec::new()));
@@ -772,13 +789,48 @@ fn post_admission_retry_uses_a_fresh_signed_nonce() {
"2026-08-12T00:00:00Z",
Duration::from_secs(5),
))
.expect("post-admission retry used a fresh request");
.expect("retryable draining response advanced to live address");
runtime.block_on(session.close());
let transcripts = transcripts.lock().expect("transcripts");
assert_eq!(transcripts.len(), 2);
assert_ne!(transcripts[0], transcripts[1]);
}
#[test]
fn post_admission_failure_is_terminal_and_does_not_mutate_twice() {
for mode in [
"post-retryable",
"provider-start-lost-response",
"cleanup-release-failure",
] {
let oracle = Oracle::start(mode);
let manifest =
ConnectionManifest::decode(oracle.ready.manifest.as_bytes()).expect("manifest");
let credential =
NativeTunnelCredential::decode(oracle.ready.credential.as_bytes()).expect("credential");
let transcripts = Arc::new(Mutex::new(Vec::new()));
let runtime = tokio::runtime::Runtime::new().expect("runtime");
let result = runtime.block_on(connect(
&manifest,
&credential,
recording_signers(
test_key(&oracle.ready.admission_key),
test_key(&oracle.ready.client_key),
Arc::clone(&transcripts),
),
"2026-08-12T00:00:00Z",
Duration::from_secs(5),
));
assert!(result.is_err(), "{mode} was retried to success");
let transcripts = transcripts.lock().expect("transcripts");
assert_eq!(
transcripts.len(),
1,
"{mode} attempted admission mutation twice"
);
}
}
#[test]
fn repeated_production_gateway_admissions_remain_bounded() {
let runtime = tokio::runtime::Runtime::new().expect("runtime");
+8
View File
@@ -145,6 +145,14 @@ fn manifest_public_identity_requires_dns_sni_not_ip_or_uuid() {
"non-DNS SNI accepted: {invalid_identity}"
);
}
let identity_equals_dns_shaped_gateway_id = String::from_utf8(valid_manifest().to_vec())
.expect("fixture is UTF-8")
.replace("\"id\":\"gateway\"", "\"id\":\"gateway.test\"");
assert!(
ConnectionManifest::decode(identity_equals_dns_shaped_gateway_id.as_bytes()).is_err(),
"public SNI identity matched the logical gateway id"
);
}
#[test]