feat(gateway): fetch sealed Apollo session work
Verify Data Plane / gateway (push) Successful in 3m33s
Verify Data Plane / gateway (push) Successful in 3m33s
This commit is contained in:
+57
-20
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user