fix(data-plane): require advertised gateway endpoint

This commit is contained in:
sechmachine
2026-07-29 06:30:30 +07:00
parent d9f348ae69
commit 5f3b9132a1
2 changed files with 26 additions and 3 deletions
+4
View File
@@ -32,6 +32,10 @@ Connection Server authority into a client.
- The Protocol repository is the sole wire-contract authority; consumers pin - The Protocol repository is the sole wire-contract authority; consumers pin
an immutable release without a filesystem `replace`. an immutable release without a filesystem `replace`.
The gateway requires an explicit `-advertise-address host:port`; it is the
only endpoint placed in the Server registration and must be client-reachable.
Apollo management and RTSP addresses remain private gateway-side inputs.
## Verification ## Verification
The baseline uses Go 1.26.5 and OpenSpec 1.5.0: The baseline uses Go 1.26.5 and OpenSpec 1.5.0:
+22 -3
View File
@@ -8,6 +8,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"net"
"os" "os"
"os/signal" "os/signal"
"strings" "strings"
@@ -27,10 +28,11 @@ func main() {
} }
func run() error { func run() error {
var listen, controlPlane, certFile, keyFile, clientCAFile string var listen, advertiseAddress, controlPlane, certFile, keyFile, clientCAFile string
var gatewayID, instanceIdentity, certificateIdentity, publicIdentity string var gatewayID, instanceIdentity, certificateIdentity, publicIdentity string
var providerManagement, providerRTSPAddress, providerRTSPURL, providerIdentity string var providerManagement, providerRTSPAddress, providerRTSPURL, providerIdentity string
flag.StringVar(&listen, "listen", "0.0.0.0:443", "gateway QUIC listen address") flag.StringVar(&listen, "listen", "0.0.0.0:443", "gateway QUIC listen address")
flag.StringVar(&advertiseAddress, "advertise-address", "", "client-visible gateway address host:port")
flag.StringVar(&controlPlane, "control-plane", "", "Connection Server HTTPS base URL") flag.StringVar(&controlPlane, "control-plane", "", "Connection Server HTTPS base URL")
flag.StringVar(&certFile, "cert", "", "gateway certificate PEM") flag.StringVar(&certFile, "cert", "", "gateway certificate PEM")
flag.StringVar(&keyFile, "key", "", "gateway private key PEM") flag.StringVar(&keyFile, "key", "", "gateway private key PEM")
@@ -44,11 +46,14 @@ func run() error {
flag.StringVar(&providerRTSPURL, "provider-rtsp-url", "", "internal Apollo RTSP URL") flag.StringVar(&providerRTSPURL, "provider-rtsp-url", "", "internal Apollo RTSP URL")
flag.StringVar(&providerIdentity, "provider-identity", "", "enrolled Apollo identity unique-id#fingerprint") flag.StringVar(&providerIdentity, "provider-identity", "", "enrolled Apollo identity unique-id#fingerprint")
flag.Parse() flag.Parse()
for name, value := range map[string]string{"control-plane": controlPlane, "cert": certFile, "key": keyFile, "client-ca": clientCAFile, "gateway-id": gatewayID, "instance-identity": instanceIdentity, "certificate-identity": certificateIdentity, "provider-management": providerManagement, "provider-rtsp-address": providerRTSPAddress, "provider-rtsp-url": providerRTSPURL, "provider-identity": providerIdentity} { for name, value := range map[string]string{"control-plane": controlPlane, "advertise-address": advertiseAddress, "cert": certFile, "key": keyFile, "client-ca": clientCAFile, "gateway-id": gatewayID, "instance-identity": instanceIdentity, "certificate-identity": certificateIdentity, "provider-management": providerManagement, "provider-rtsp-address": providerRTSPAddress, "provider-rtsp-url": providerRTSPURL, "provider-identity": providerIdentity} {
if value == "" { if value == "" {
return fmt.Errorf("-%s is required", name) return fmt.Errorf("-%s is required", name)
} }
} }
if err := validateAdvertisedAddress(advertiseAddress); err != nil {
return err
}
expectedIdentity, err := parseProviderIdentity(providerIdentity) expectedIdentity, err := parseProviderIdentity(providerIdentity)
if err != nil { if err != nil {
return err return err
@@ -66,7 +71,7 @@ func run() error {
if err != nil { if err != nil {
return err return err
} }
registration := protocol.GatewayRegistration{Version: "1", GatewayID: gatewayID, InstanceIdentity: instanceIdentity, CertificateIdentity: certificateIdentity, PublicIdentity: publicIdentity, Address: server.Addr().String(), ProviderIdentity: providerIdentity, ProtocolMinVersion: 1, ProtocolMaxVersion: 1, ConnectionCapacity: 8, BandwidthCapacityKbps: 100000, Features: []string{"quic-tls13", "datagram.media", "apollo"}, Capabilities: capabilities} registration := protocol.GatewayRegistration{Version: "1", GatewayID: gatewayID, InstanceIdentity: instanceIdentity, CertificateIdentity: certificateIdentity, PublicIdentity: publicIdentity, Address: advertiseAddress, ProviderIdentity: providerIdentity, ProtocolMinVersion: 1, ProtocolMaxVersion: 1, ConnectionCapacity: 8, BandwidthCapacityKbps: 100000, Features: []string{"quic-tls13", "datagram.media", "apollo"}, Capabilities: capabilities}
if _, err := controlPlaneClient.Register(context.Background(), registration); err != nil { if _, err := controlPlaneClient.Register(context.Background(), registration); err != nil {
_ = server.Close() _ = server.Close()
return err return err
@@ -77,6 +82,20 @@ func run() error {
return server.Serve(ctx) return server.Serve(ctx)
} }
func validateAdvertisedAddress(address string) error {
host, port, err := net.SplitHostPort(address)
if err != nil || host == "" || port == "" {
return fmt.Errorf("-advertise-address must be a host:port address")
}
if parsed := net.ParseIP(host); parsed != nil && parsed.IsUnspecified() {
return fmt.Errorf("-advertise-address must not be an unspecified address")
}
if net.JoinHostPort(host, port) != address {
return fmt.Errorf("-advertise-address must use canonical host:port syntax")
}
return nil
}
func heartbeatLoop(ctx context.Context, client *gateway.ControlPlaneClient, server *gateway.Server, registration protocol.GatewayRegistration) { func heartbeatLoop(ctx context.Context, client *gateway.ControlPlaneClient, server *gateway.Server, registration protocol.GatewayRegistration) {
ticker := time.NewTicker(2 * time.Second) ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop() defer ticker.Stop()