feat(gateway): enforce phase 3d tls trust
Verify Data Plane / gateway (push) Successful in 4m35s

This commit is contained in:
sechmachine
2026-08-11 20:15:15 +07:00
parent a1d68d33e5
commit e764b0d96d
5 changed files with 694 additions and 30 deletions
+11 -29
View File
@@ -2,14 +2,11 @@ package main
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"strings"
"syscall"
@@ -17,6 +14,7 @@ import (
"net/http"
"git.sechmachine.io.vn/sechmachine/VerseVDI-Data-Plane/cmd/internal/gatewaytls"
"git.sechmachine.io.vn/sechmachine/VerseVDI-Data-Plane/gateway"
protocol "git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol/gen/go/protocol"
)
@@ -28,20 +26,20 @@ func main() {
}
func run() error {
var listen, advertiseAddress, controlPlane, certFile, keyFile, clientCAFile string
var gatewayID, instanceIdentity, certificateIdentity, publicIdentity string
var listen, advertiseAddress, controlPlane, certFile, keyFile, streamingCAFile, controlCAFile string
var gatewayID, instanceIdentity, publicIdentity string
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(&certFile, "cert", "", "gateway certificate PEM")
flag.StringVar(&keyFile, "key", "", "gateway private key PEM")
flag.StringVar(&clientCAFile, "client-ca", "", "Connection Server/client CA PEM")
flag.StringVar(&streamingCAFile, "streaming-ca", "", "CA PEM for QUIC streaming clients")
flag.StringVar(&controlCAFile, "control-ca", "", "CA bundle or exact Server leaf PEM")
flag.StringVar(&gatewayID, "gateway-id", "", "stable gateway identifier")
flag.StringVar(&instanceIdentity, "instance-identity", "", "gateway instance identity")
flag.StringVar(&certificateIdentity, "certificate-identity", "", "gateway certificate identity")
flag.StringVar(&publicIdentity, "public-identity", "gateway", "gateway public identity")
flag.StringVar(&publicIdentity, "public-identity", "", "gateway DNS identity from the certificate SAN")
flag.Parse()
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} {
for name, value := range map[string]string{"control-plane": controlPlane, "advertise-address": advertiseAddress, "cert": certFile, "key": keyFile, "streaming-ca": streamingCAFile, "control-ca": controlCAFile, "gateway-id": gatewayID, "instance-identity": instanceIdentity, "public-identity": publicIdentity} {
if value == "" {
return fmt.Errorf("-%s is required", name)
}
@@ -49,20 +47,20 @@ func run() error {
if err := validateAdvertisedAddress(advertiseAddress); err != nil {
return err
}
serverTLS, clientTLS, err := loadTLS(certFile, keyFile, clientCAFile)
tlsMaterial, err := gatewaytls.Load(certFile, keyFile, streamingCAFile, controlCAFile, gatewayID, publicIdentity)
if err != nil {
return err
}
transport := &http.Transport{TLSClientConfig: clientTLS}
transport := &http.Transport{TLSClientConfig: tlsMaterial.ControlTLS}
controlPlaneClient := gateway.NewControlPlaneClient(controlPlane, &http.Client{Transport: transport, Timeout: 5 * time.Second})
provider := gateway.NewApolloAdapter(gateway.NewNativeApolloBackend(), gateway.ProviderIdentity{})
capabilities := gateway.DefaultCapabilities()
features := gateway.DefaultFeatures()
server, err := gateway.NewServer(gateway.ServerConfig{ListenAddress: listen, TLSConfig: serverTLS, GatewayID: gatewayID, Features: features, Capabilities: capabilities, ProviderCapabilities: capabilities, Admission: controlPlaneClient, ProviderStateReporter: controlPlaneClient, ClipboardAuditReporter: controlPlaneClient, Provider: provider, PacerKbps: 100000})
server, err := gateway.NewServer(gateway.ServerConfig{ListenAddress: listen, TLSConfig: tlsMaterial.ServerTLS, GatewayID: gatewayID, Features: features, Capabilities: capabilities, ProviderCapabilities: capabilities, Admission: controlPlaneClient, ProviderStateReporter: controlPlaneClient, ClipboardAuditReporter: controlPlaneClient, Provider: provider, PacerKbps: 100000})
if err != nil {
return err
}
registration := protocol.GatewayRegistration{Version: "1", GatewayID: gatewayID, InstanceIdentity: instanceIdentity, CertificateIdentity: certificateIdentity, PublicIdentity: publicIdentity, Address: advertiseAddress, ProviderIdentity: "server-derived", ProtocolMinVersion: 1, ProtocolMaxVersion: 1, ConnectionCapacity: 8, BandwidthCapacityKbps: 100000, Features: features, Capabilities: capabilities}
registration := protocol.GatewayRegistration{Version: "1", GatewayID: gatewayID, InstanceIdentity: instanceIdentity, CertificateIdentity: tlsMaterial.CertificateIdentity, PublicIdentity: publicIdentity, Address: advertiseAddress, ProviderIdentity: "server-derived", ProtocolMinVersion: 1, ProtocolMaxVersion: 1, ConnectionCapacity: 8, BandwidthCapacityKbps: 100000, Features: features, Capabilities: capabilities}
if _, err := controlPlaneClient.Register(context.Background(), registration); err != nil {
_ = server.Close()
return err
@@ -183,22 +181,6 @@ func providerStateName(value uint64) string {
}
}
func loadTLS(certFile, keyFile, clientCAFile string) (*tls.Config, *tls.Config, error) {
certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, nil, err
}
caBytes, err := os.ReadFile(clientCAFile)
if err != nil {
return nil, nil, err
}
clientCAs := x509.NewCertPool()
if !clientCAs.AppendCertsFromPEM(caBytes) {
return nil, nil, errors.New("client CA PEM contains no certificate")
}
return &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{certificate}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs}, &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{certificate}, RootCAs: clientCAs}, nil
}
func parseProviderIdentity(value string) (gateway.ProviderIdentity, error) {
uniqueID, fingerprint, ok := strings.Cut(value, "#")
if !ok || uniqueID == "" || fingerprint == "" {