feat(gateway): fetch sealed Apollo session work
Verify Data Plane / gateway (push) Successful in 3m33s

This commit is contained in:
sechmachine
2026-07-29 11:27:31 +07:00
parent 8a302cd8cc
commit 844e548c95
9 changed files with 217 additions and 44 deletions
+57 -20
View File
@@ -2,12 +2,17 @@ package gateway
import (
"bufio"
"bytes"
"context"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"fmt"
"io"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
@@ -16,36 +21,35 @@ import (
)
// NativeApolloBackend keeps provider sockets inside the gateway process. The
// RTSP endpoint is configuration owned by the gateway and is never serialized
// into a client manifest or authority.
// session-scoped Server work is the sole source of provider endpoint and mTLS
// material; it is never serialized into a client manifest or authority.
type NativeApolloBackend struct {
ManagementURL string
RTSPAddress string
RTSPURL string
HTTPClient *http.Client
Dialer *net.Dialer
TLSConfig *tls.Config
Dialer *net.Dialer
mu sync.Mutex
pending map[string]net.Conn
}
func NewNativeApolloBackend(managementURL, rtspAddress, rtspURL string, client *http.Client) *NativeApolloBackend {
if client == nil {
client = &http.Client{Timeout: 5 * time.Second}
}
return &NativeApolloBackend{ManagementURL: managementURL, RTSPAddress: rtspAddress, RTSPURL: rtspURL, HTTPClient: client, Dialer: &net.Dialer{Timeout: 5 * time.Second}, pending: make(map[string]net.Conn)}
func NewNativeApolloBackend() *NativeApolloBackend {
return &NativeApolloBackend{Dialer: &net.Dialer{Timeout: 5 * time.Second}, pending: make(map[string]net.Conn)}
}
func (b *NativeApolloBackend) Management(ctx context.Context) ([]byte, error) {
if b.ManagementURL == "" {
func (b *NativeApolloBackend) Management(ctx context.Context, request LaunchRequest) ([]byte, error) {
work := request.ProviderWork
if err := work.Validate(); err != nil || work.ProviderProfile != ProviderProfileApollo {
return nil, ErrProviderMalformed
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, b.ManagementURL, nil)
tlsConfig, err := pinnedApolloTLSConfig(work)
if err != nil {
return nil, err
}
client := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}, Timeout: 5 * time.Second}
managementURL := "https://" + net.JoinHostPort(work.ManagementHost, strconv.FormatInt(work.ManagementPort, 10)) + "/serverinfo"
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, managementURL, nil)
if err != nil {
return nil, ErrProviderMalformed
}
response, err := b.HTTPClient.Do(request)
response, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
@@ -56,18 +60,51 @@ func (b *NativeApolloBackend) Management(ctx context.Context) ([]byte, error) {
return readBounded(response.Body, 64*1024)
}
func pinnedApolloTLSConfig(work protocol.ProviderSessionWork) (*tls.Config, error) {
identity, ok := providerIdentityFromKey(work.ProviderIdentity)
if !ok || !strings.HasPrefix(identity.Fingerprint, "sha256:") {
return nil, ErrProviderIdentity
}
pinned, err := hex.DecodeString(strings.TrimPrefix(identity.Fingerprint, "sha256:"))
if err != nil || len(pinned) != sha256.Size {
return nil, ErrProviderIdentity
}
certificate, err := tls.X509KeyPair([]byte(work.ClientCertificatePem), []byte(work.ClientPrivateKeyPem))
if err != nil {
return nil, ErrProviderIdentity
}
trust := x509.NewCertPool()
if !trust.AppendCertsFromPEM([]byte(work.ServerCertificatePem)) {
return nil, ErrProviderIdentity
}
return &tls.Config{
MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{certificate}, RootCAs: trust,
VerifyPeerCertificate: func(rawCertificates [][]byte, _ [][]*x509.Certificate) error {
if len(rawCertificates) == 0 {
return ErrProviderIdentity
}
digest := sha256.Sum256(rawCertificates[0])
if !bytes.Equal(digest[:], pinned) {
return ErrProviderIdentity
}
return nil
},
}, nil
}
func (b *NativeApolloBackend) Setup(ctx context.Context, request LaunchRequest) ([]byte, error) {
if b.RTSPAddress == "" || b.RTSPURL == "" || request.SessionID == "" {
work := request.ProviderWork
if err := work.Validate(); err != nil || request.SessionID == "" {
return nil, ErrProviderMalformed
}
conn, err := b.Dialer.DialContext(ctx, "tcp", b.RTSPAddress)
conn, err := b.Dialer.DialContext(ctx, "tcp", net.JoinHostPort(work.StreamHost, strconv.FormatInt(work.StreamPort, 10)))
if err != nil {
return nil, err
}
if deadline, ok := ctx.Deadline(); ok {
_ = conn.SetDeadline(deadline)
}
requestText := "SETUP " + b.RTSPURL + " RTSP/1.0\r\nCSeq: 1\r\nTransport: RTP/AVP/TCP;interleaved=0-1\r\nSession: " + request.SessionID + "\r\n\r\n"
requestText := "SETUP rtsp://" + work.StreamHost + "/streamid=video/0/0 RTSP/1.0\r\nCSeq: 1\r\nTransport: RTP/AVP/TCP;interleaved=0-1\r\nSession: " + request.SessionID + "\r\n\r\n"
if _, err := io.WriteString(conn, requestText); err != nil {
_ = conn.Close()
return nil, err