diff --git a/agent/proxycfg/api_gateway.go b/agent/proxycfg/api_gateway.go index 43798239a353..0c49adba79c5 100644 --- a/agent/proxycfg/api_gateway.go +++ b/agent/proxycfg/api_gateway.go @@ -64,6 +64,7 @@ func (h *handlerAPIGateway) initialize(ctx context.Context) (ConfigSnapshot, err snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]() snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]() snap.APIGateway.Certificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]() + snap.APIGateway.FSCertificates = watch.NewMap[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry]() snap.APIGateway.Upstreams = make(listenerRouteUpstreams) snap.APIGateway.UpstreamsSet = make(routeUpstreamSet) @@ -96,7 +97,8 @@ func (h *handlerAPIGateway) subscribeToConfigEntry(ctx context.Context, kind, na // handleUpdate responds to changes in the api-gateway. In general, we want // to crawl the various resources related to or attached to the gateway and // collect the list of things need to generate xDS. This list of resources -// includes the bound-api-gateway, http-routes, tcp-routes, and inline-certificates. +// includes the bound-api-gateway, http-routes, tcp-routes, +// file-system-certificates and inline-certificates. func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error { if u.Err != nil { return fmt.Errorf("error filling agent cache: %v", u.Err) @@ -113,6 +115,11 @@ func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, sna if err := h.handleGatewayConfigUpdate(ctx, u, snap, u.CorrelationID); err != nil { return err } + case fileSystemCertificateConfigWatchID: + // Handle change in an attached file-system-certificate config entry + if err := h.handleFileSystemCertConfigUpdate(ctx, u, snap); err != nil { + return err + } case inlineCertificateConfigWatchID: // Handle change in an attached inline-certificate config entry if err := h.handleInlineCertConfigUpdate(ctx, u, snap); err != nil { @@ -205,12 +212,21 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd for _, ref := range listener.Certificates { ctx, cancel := context.WithCancel(ctx) seenRefs[ref] = struct{}{} - snap.APIGateway.Certificates.InitWatch(ref, cancel) - err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID) - if err != nil { - // TODO May want to continue - return err + if ref.Kind == structs.FileSystemCertificate { + snap.APIGateway.FSCertificates.InitWatch(ref, cancel) + + err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, fileSystemCertificateConfigWatchID) + if err != nil { + return err + } + } else { + snap.APIGateway.Certificates.InitWatch(ref, cancel) + + err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID) + if err != nil { + return err + } } } } @@ -241,6 +257,13 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd return true }) + snap.APIGateway.FSCertificates.ForEachKey(func(ref structs.ResourceReference) bool { + if _, ok := seenRefs[ref]; !ok { + snap.APIGateway.FSCertificates.CancelWatch(ref) + } + return true + }) + snap.APIGateway.BoundGatewayConfigLoaded = true break case *structs.APIGatewayConfigEntry: @@ -265,6 +288,30 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd return h.watchIngressLeafCert(ctx, snap) } +func (h *handlerAPIGateway) handleFileSystemCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error { + resp, ok := u.Result.(*structs.ConfigEntryResponse) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } else if resp.Entry == nil { + return nil + } + + cfg, ok := resp.Entry.(*structs.FileSystemCertificateConfigEntry) + if !ok { + return fmt.Errorf("invalid type for config entry: %T", resp.Entry) + } + + ref := structs.ResourceReference{ + Kind: cfg.GetKind(), + Name: cfg.GetName(), + EnterpriseMeta: *cfg.GetEnterpriseMeta(), + } + + snap.APIGateway.FSCertificates.Set(ref, cfg) + + return nil +} + // handleInlineCertConfigUpdate stores the certificate for the gateway func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error { resp, ok := u.Result.(*structs.ConfigEntryResponse) diff --git a/agent/proxycfg/proxycfg.deepcopy.go b/agent/proxycfg/proxycfg.deepcopy.go index 68a5bd0db5fc..c7dbb49564fd 100644 --- a/agent/proxycfg/proxycfg.deepcopy.go +++ b/agent/proxycfg/proxycfg.deepcopy.go @@ -422,6 +422,7 @@ func (o *configSnapshotAPIGateway) DeepCopy() *configSnapshotAPIGateway { cp.HTTPRoutes = o.HTTPRoutes.DeepCopy() cp.TCPRoutes = o.TCPRoutes.DeepCopy() cp.Certificates = o.Certificates.DeepCopy() + cp.FSCertificates = o.FSCertificates.DeepCopy() if o.Listeners != nil { cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners)) for k2, v2 := range o.Listeners { diff --git a/agent/proxycfg/snapshot.go b/agent/proxycfg/snapshot.go index 8f407c1afcd5..8433cf0e1bb1 100644 --- a/agent/proxycfg/snapshot.go +++ b/agent/proxycfg/snapshot.go @@ -735,9 +735,10 @@ type configSnapshotAPIGateway struct { // UpstreamsSet is the unique set of UpstreamID the gateway routes to. UpstreamsSet routeUpstreamSet - HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry] - TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry] - Certificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry] + HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry] + TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry] + Certificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry] + FSCertificates watch.Map[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry] // LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's // leaf cert watch with different parameters. diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index a113903c150c..f8bfe3b2c295 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -38,6 +38,7 @@ const ( gatewayConfigWatchID = "gateway-config" apiGatewayConfigWatchID = "api-gateway-config" boundGatewayConfigWatchID = "bound-gateway-config" + fileSystemCertificateConfigWatchID = "file-system-certificate-config" inlineCertificateConfigWatchID = "inline-certificate-config" routeConfigWatchID = "route-config" externalServiceIDPrefix = "external-service:" diff --git a/agent/structs/deep-copy.sh b/agent/structs/deep-copy.sh index cbc1bdc42ae5..1bc4ededd6c9 100755 --- a/agent/structs/deep-copy.sh +++ b/agent/structs/deep-copy.sh @@ -24,6 +24,7 @@ deep-copy \ -type DiscoverySplit \ -type ExposeConfig \ -type ExportedServicesConfigEntry \ + -type FileSystemCertificateConfigEntry \ -type GatewayService \ -type GatewayServiceTLSConfig \ -type HTTPHeaderModifiers \ diff --git a/agent/structs/structs.deepcopy.go b/agent/structs/structs.deepcopy.go index 98b118eb2184..9c9a7c8bc991 100644 --- a/agent/structs/structs.deepcopy.go +++ b/agent/structs/structs.deepcopy.go @@ -1,4 +1,4 @@ -// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type APIGatewayListener -type BoundAPIGatewayListener -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type ExportedServicesConfigEntry -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HTTPRouteConfigEntry -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type InlineCertificateConfigEntry -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type TCPRouteConfigEntry -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT. +// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type APIGatewayListener -type BoundAPIGatewayListener -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type ExportedServicesConfigEntry -type FileSystemCertificateConfigEntry -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HTTPRouteConfigEntry -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type InlineCertificateConfigEntry -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type TCPRouteConfigEntry -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT. package structs @@ -318,6 +318,18 @@ func (o *ExportedServicesConfigEntry) DeepCopy() *ExportedServicesConfigEntry { return &cp } +// DeepCopy generates a deep copy of *FileSystemCertificateConfigEntry +func (o *FileSystemCertificateConfigEntry) DeepCopy() *FileSystemCertificateConfigEntry { + var cp FileSystemCertificateConfigEntry = *o + if o.Meta != nil { + cp.Meta = make(map[string]string, len(o.Meta)) + for k2, v2 := range o.Meta { + cp.Meta[k2] = v2 + } + } + return &cp +} + // DeepCopy generates a deep copy of *GatewayService func (o *GatewayService) DeepCopy() *GatewayService { var cp GatewayService = *o diff --git a/agent/xds/listeners_apigateway.go b/agent/xds/listeners_apigateway.go index b31cb8e59606..df121bd02a12 100644 --- a/agent/xds/listeners_apigateway.go +++ b/agent/xds/listeners_apigateway.go @@ -35,13 +35,20 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro listenerKey := readyListener.listenerKey boundListener := readyListener.boundListenerCfg - var certs []structs.InlineCertificateConfigEntry + // Collect the referenced certificate config entries + var fsCerts []structs.FileSystemCertificateConfigEntry + var inlineCerts []structs.InlineCertificateConfigEntry for _, certRef := range boundListener.Certificates { - cert, ok := cfgSnap.APIGateway.Certificates.Get(certRef) - if !ok { - continue + switch certRef.Kind { + case structs.InlineCertificate: + if cert, ok := cfgSnap.APIGateway.Certificates.Get(certRef); ok { + inlineCerts = append(inlineCerts, *cert) + } + case structs.FileSystemCertificate: + if cert, ok := cfgSnap.APIGateway.FSCertificates.Get(certRef); ok { + fsCerts = append(fsCerts, *cert) + } } - certs = append(certs, *cert) } isAPIGatewayWithTLS := len(boundListener.Certificates) > 0 @@ -125,7 +132,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro accessLogs: &cfgSnap.Proxy.AccessLogs, logger: s.Logger, }, - certs, + inlineCerts, ) if err != nil { return nil, err @@ -225,7 +232,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro sniFilterChains := []*envoy_listener_v3.FilterChain{} if isAPIGatewayWithTLS { - sniFilterChains, err = makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, certs) + sniFilterChains, err = makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, inlineCerts) if err != nil { return nil, err } diff --git a/agent/xds/listeners_ingress.go b/agent/xds/listeners_ingress.go index ced7e18d21fe..6f5154e630db 100644 --- a/agent/xds/listeners_ingress.go +++ b/agent/xds/listeners_ingress.go @@ -391,6 +391,24 @@ func makeTLSParametersFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *env return makeTLSParametersFromTLSConfig(tlsCfg.TLSMinVersion, tlsCfg.TLSMaxVersion, tlsCfg.CipherSuites) } +func makeFileSystemTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert structs.FileSystemCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext { + return &envoy_tls_v3.CommonTlsContext{ + TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg), + TlsCertificates: []*envoy_tls_v3.TlsCertificate{{ + CertificateChain: &envoy_core_v3.DataSource{ + Specifier: &envoy_core_v3.DataSource_Filename{ + Filename: cert.Certificate, + }, + }, + PrivateKey: &envoy_core_v3.DataSource{ + Specifier: &envoy_core_v3.DataSource_Filename{ + Filename: cert.PrivateKey, + }, + }, + }}, + } +} + func makeInlineTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert structs.InlineCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext { return &envoy_tls_v3.CommonTlsContext{ TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),