fix(gateway): strip provider authority from client egress
Verify Data Plane / gateway (push) Failing after 4m32s

This commit is contained in:
sechmachine
2026-08-12 12:30:28 +07:00
parent 93246c14bf
commit 12ad2a4daa
4 changed files with 75 additions and 13 deletions
+65 -8
View File
@@ -12,6 +12,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"io" "io"
"math/big" "math/big"
@@ -969,10 +970,61 @@ func newNativeGatewayLifecycleHarness(t *testing.T, sessionID string) nativeGate
return nativeGatewayLifecycleHarness{native: native, key: key, client: client, admission: admission, reporter: reporter} return nativeGatewayLifecycleHarness{native: native, key: key, client: client, admission: admission, reporter: reporter}
} }
func TestGatewayEgressReturnsProviderFreeClientAuthority(t *testing.T) {
h := newNativeGatewayLifecycleHarness(t, "session-client-authority")
var got map[string]any
if err := json.Unmarshal(h.client.authorityRaw, &got); err != nil {
t.Fatal(err)
}
want := map[string]any{
"version": "1",
"session_id": "session-client-authority",
"gateway_id": "gateway-1",
"audience": "versevdi-gateway",
"reconnect_sequence": float64(0),
"expires_at": h.admission.authority.ExpiresAt,
"capabilities": map[string]any{
"transport": "quic-tls13",
"framing": "datagram-v2",
"media": "encoded",
"audio": "encoded",
"source_rate_control": "server",
"client_decode": []any{"h264-opus"},
},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("raw client authority = %s", h.client.authorityRaw)
}
if err := h.client.Close(); err != nil {
t.Fatal(err)
}
h.waitReleased(t)
if !reflect.DeepEqual(h.admission.releaseAuthority, h.admission.authority) {
t.Fatalf("release authority = %#v, want provider-bearing %#v", h.admission.releaseAuthority, h.admission.authority)
}
}
func TestClientSessionAuthorityRejectsProviderBearingRC3(t *testing.T) {
var _ protocol.ClientSessionAuthority = Client{}.Authority
raw, err := protocol.EncodeSessionAuthority(protocol.SessionAuthority{
Version: "1", SessionID: "session-rc3", GatewayID: "gateway-1", Audience: "versevdi-gateway",
ExpiresAt: time.Now().Add(time.Minute).UTC().Format(time.RFC3339Nano), Capabilities: DefaultCapabilities(),
ProviderProfile: ProviderProfileApollo, ProviderIdentity: "apollo-fixture-1#sha256:fixture-apollo-1",
})
if err != nil {
t.Fatal(err)
}
if _, err := protocol.DecodeClientSessionAuthority(raw); err == nil {
t.Fatalf("accepted provider-bearing RC3 authority: %s", raw)
}
}
type independentGatewayClient struct { type independentGatewayClient struct {
connection *quic.Conn connection *quic.Conn
control *quic.Stream control *quic.Stream
media independentMediaReassembler authority protocol.ClientSessionAuthority
authorityRaw []byte
media independentMediaReassembler
} }
func dialIndependentGateway(ctx context.Context, address string, tlsConfig *tls.Config, request protocol.TunnelAdmissionRequest) (*independentGatewayClient, error) { func dialIndependentGateway(ctx context.Context, address string, tlsConfig *tls.Config, request protocol.TunnelAdmissionRequest) (*independentGatewayClient, error) {
@@ -994,17 +1046,20 @@ func dialIndependentGateway(ctx context.Context, address string, tlsConfig *tls.
if err == nil { if err == nil {
encoded, err = independentReadWire(stream, defaultHelloLimit) encoded, err = independentReadWire(stream, defaultHelloLimit)
} }
var authority protocol.ClientSessionAuthority
if err == nil { if err == nil {
_, err = protocol.DecodeSessionAuthority(encoded) authority, err = protocol.DecodeClientSessionAuthority(encoded)
} }
if err != nil { if err != nil {
_ = connection.CloseWithError(applicationError, "independent client admission failed") _ = connection.CloseWithError(applicationError, "independent client admission failed")
return nil, err return nil, err
} }
return &independentGatewayClient{ return &independentGatewayClient{
connection: connection, connection: connection,
control: stream, control: stream,
media: independentMediaReassembler{incomplete: make(map[independentMediaKey]*independentMediaUnit)}, authority: authority,
authorityRaw: encoded,
media: independentMediaReassembler{incomplete: make(map[independentMediaKey]*independentMediaUnit)},
}, nil }, nil
} }
@@ -1529,6 +1584,7 @@ func testTLS(t *testing.T) (*tls.Config, *tls.Config) {
type oneTimeAdmission struct { type oneTimeAdmission struct {
used atomic.Bool used atomic.Bool
authority protocol.SessionAuthority authority protocol.SessionAuthority
releaseAuthority protocol.SessionAuthority
releases atomic.Int64 releases atomic.Int64
released chan struct{} released chan struct{}
streamPolicy protocol.ProviderStreamPolicy streamPolicy protocol.ProviderStreamPolicy
@@ -1603,8 +1659,9 @@ func (a *oneTimeAdmission) ProviderWork(_ context.Context, authority protocol.Se
}, nil }, nil
} }
func (a *oneTimeAdmission) Release(context.Context, protocol.SessionAuthority) error { func (a *oneTimeAdmission) Release(_ context.Context, authority protocol.SessionAuthority) error {
if a.releases.Add(1) == 1 { if a.releases.Add(1) == 1 {
a.releaseAuthority = authority
close(a.released) close(a.released)
} }
return nil return nil
+7 -4
View File
@@ -291,8 +291,11 @@ func (s *Server) handleConnection(parent context.Context, connection *quic.Conn)
_ = writeStableError(stream, "provider_state_unavailable", err, true) _ = writeStableError(stream, "provider_state_unavailable", err, true)
return return
} }
authority.Capabilities = selected clientAuthority := protocol.ClientSessionAuthority{
authorityBytes, err := protocol.EncodeSessionAuthority(authority) Version: authority.Version, SessionID: authority.SessionID, GatewayID: authority.GatewayID, Audience: authority.Audience,
ReconnectSequence: authority.ReconnectSequence, ExpiresAt: authority.ExpiresAt, Capabilities: selected,
}
authorityBytes, err := protocol.EncodeClientSessionAuthority(clientAuthority)
if err != nil || writeWire(stream, authorityBytes, defaultHelloLimit) != nil { if err != nil || writeWire(stream, authorityBytes, defaultHelloLimit) != nil {
_ = providerSession.ReleaseAll(context.Background()) _ = providerSession.ReleaseAll(context.Background())
_ = providerSession.Terminate(context.Background()) _ = providerSession.Terminate(context.Background())
@@ -1010,7 +1013,7 @@ type Client struct {
controlReadMu sync.Mutex controlReadMu sync.Mutex
controlWriteMu sync.Mutex controlWriteMu sync.Mutex
pendingControl map[string][][]byte pendingControl map[string][][]byte
Authority protocol.SessionAuthority Authority protocol.ClientSessionAuthority
} }
func Dial(ctx context.Context, address string, tlsConfig *tls.Config, request protocol.TunnelAdmissionRequest) (*Client, error) { func Dial(ctx context.Context, address string, tlsConfig *tls.Config, request protocol.TunnelAdmissionRequest) (*Client, error) {
@@ -1045,7 +1048,7 @@ func Dial(ctx context.Context, address string, tlsConfig *tls.Config, request pr
_ = connection.CloseWithError(applicationError, "no authority") _ = connection.CloseWithError(applicationError, "no authority")
return nil, err return nil, err
} }
authority, authorityErr := protocol.DecodeSessionAuthority(response) authority, authorityErr := protocol.DecodeClientSessionAuthority(response)
if authorityErr != nil { if authorityErr != nil {
stable, stableErr := protocol.DecodeStableError(response) stable, stableErr := protocol.DecodeStableError(response)
if stableErr == nil { if stableErr == nil {
+1 -1
View File
@@ -3,7 +3,7 @@ module git.sechmachine.io.vn/sechmachine/VerseVDI-Data-Plane
go 1.26.5 go 1.26.5
require ( require (
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.3 git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.5
github.com/quic-go/quic-go v0.61.0 github.com/quic-go/quic-go v0.61.0
) )
+2
View File
@@ -20,6 +20,8 @@ git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.2 h1
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.2/go.mod h1:7PhFIDhjtr20btWoEb2GqB+7dBpzJt43olrnHVutWoc= git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.2/go.mod h1:7PhFIDhjtr20btWoEb2GqB+7dBpzJt43olrnHVutWoc=
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.3 h1:43EJHiKcbWdF0K1dvVznpWaUUWcvT5nogcGiSnOVu5s= git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.3 h1:43EJHiKcbWdF0K1dvVznpWaUUWcvT5nogcGiSnOVu5s=
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.3/go.mod h1:7PhFIDhjtr20btWoEb2GqB+7dBpzJt43olrnHVutWoc= git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.3/go.mod h1:7PhFIDhjtr20btWoEb2GqB+7dBpzJt43olrnHVutWoc=
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.5 h1:bJ3JeCm7dsQwWc7kMjcSgBrtATT1hi2xUSNdE73q4X4=
git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol v1.0.0-phase3d-macos-rc.5/go.mod h1:7PhFIDhjtr20btWoEb2GqB+7dBpzJt43olrnHVutWoc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=