Browse Source

[NET-8091] Add file-system-certificate config entry for API gateway (#20873)

* Define file-system-certificate config entry

* Collect file-system-certificate(s) referenced by api-gateway onto snapshot

* Add file-system-certificate to config entry kind allow lists

* Remove inapplicable validation

This validation makes sense for inline certificates since Consul server is holding the certificate; however, for file system certificates, Consul server never actually sees the certificate.

* Support file-system-certificate as source for listener TLS certificate

* Add more required mappings for the new config entry type

* Construct proper TLS context based on certificate kind

* Add support or SDS in xdscommon

* Remove unused param

* Adds back verification of certs for inline-certificates

* Undo tangential changes to TLS config consumption

* Remove stray curly braces

* Undo some more tangential changes

* Improve function name for generating API gateway secrets

* Add changelog entry

* Update .changelog/20873.txt

Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com>

* Add some nil-checking, remove outdated TODO

* Update test assertions to include file-system-certificate

* Add documentation for file-system-certificate config entry

Add new doc to nav

* Fix grammar mistake

* Rename watchmaps, remove outdated TODO

---------

Co-authored-by: Melisa Griffin <melisa.griffin@hashicorp.com>
Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com>
pull/20984/head
Nathan Coleman 7 months ago committed by GitHub
parent
commit
5e9f02d4be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      .changelog/20873.txt
  2. 11
      agent/consul/fsm/decode_downgrade.go
  3. 5
      agent/consul/fsm/fsm.go
  4. 2
      agent/consul/gateways/controller_gateways.go
  5. 1
      agent/consul/state/config_entry.go
  6. 35
      agent/consul/state/config_entry_events.go
  7. 3
      agent/consul/state/events.go
  8. 35
      agent/consul/state/memdb.go
  9. 16
      agent/consul/usagemetrics/usagemetrics_test.go
  10. 2
      agent/proxycfg-glue/config_entry.go
  11. 67
      agent/proxycfg/api_gateway.go
  12. 3
      agent/proxycfg/proxycfg.deepcopy.go
  13. 11
      agent/proxycfg/snapshot.go
  14. 1
      agent/proxycfg/state.go
  15. 36
      agent/structs/config_entry.go
  16. 73
      agent/structs/config_entry_file_system_certificate.go
  17. 15
      agent/structs/config_entry_gateways.go
  18. 1
      agent/structs/deep-copy.sh
  19. 14
      agent/structs/structs.deepcopy.go
  20. 105
      agent/xds/listeners_apigateway.go
  21. 23
      agent/xds/listeners_ingress.go
  22. 38
      agent/xds/secrets.go
  23. 17
      api/config_entry.go
  24. 44
      api/config_entry_file_system_certificate.go
  25. 6
      envoyextensions/xdscommon/xdscommon.go
  26. 18
      proto/private/pbconfigentry/config_entry.gen.go
  27. 16
      proto/private/pbconfigentry/config_entry.go
  28. 10
      proto/private/pbconfigentry/config_entry.pb.binary.go
  29. 3970
      proto/private/pbconfigentry/config_entry.pb.go
  30. 15
      proto/private/pbconfigentry/config_entry.proto
  31. 84
      proto/private/pbsubscribe/subscribe.pb.go
  32. 3
      proto/private/pbsubscribe/subscribe.proto
  33. 140
      website/content/api-docs/config.mdx
  34. 44
      website/content/docs/connect/config-entries/api-gateway.mdx
  35. 127
      website/content/docs/connect/config-entries/file-system-certificate.mdx
  36. 9
      website/content/docs/connect/gateways/api-gateway/configuration/index.mdx
  37. 4
      website/data/docs-nav-data.json

3
.changelog/20873.txt

@ -0,0 +1,3 @@
```release-note:feature
gateways: api-gateway can leverage listener TLS certificates available on the gateway's local filesystem by specifying the public certificate and private key path in the new file-system-certificate configuration entry
```

11
agent/consul/fsm/decode_downgrade.go

@ -619,6 +619,8 @@ func MakeShadowConfigEntry(kind, name string) (structs.ConfigEntry, error) {
return &ShadowAPIGatewayConfigEntry{APIGatewayConfigEntry: &structs.APIGatewayConfigEntry{Name: name}}, nil return &ShadowAPIGatewayConfigEntry{APIGatewayConfigEntry: &structs.APIGatewayConfigEntry{Name: name}}, nil
case structs.BoundAPIGateway: case structs.BoundAPIGateway:
return &ShadowBoundAPIGatewayConfigEntry{BoundAPIGatewayConfigEntry: &structs.BoundAPIGatewayConfigEntry{Name: name}}, nil return &ShadowBoundAPIGatewayConfigEntry{BoundAPIGatewayConfigEntry: &structs.BoundAPIGatewayConfigEntry{Name: name}}, nil
case structs.FileSystemCertificate:
return &ShadowFileSystemCertificateConfigEntry{FileSystemCertificateConfigEntry: &structs.FileSystemCertificateConfigEntry{Name: name}}, nil
case structs.InlineCertificate: case structs.InlineCertificate:
return &ShadowInlineCertificateConfigEntry{InlineCertificateConfigEntry: &structs.InlineCertificateConfigEntry{Name: name}}, nil return &ShadowInlineCertificateConfigEntry{InlineCertificateConfigEntry: &structs.InlineCertificateConfigEntry{Name: name}}, nil
case structs.HTTPRoute: case structs.HTTPRoute:
@ -931,6 +933,15 @@ func (s ShadowBoundAPIGatewayConfigEntry) GetRealConfigEntry() structs.ConfigEnt
return s.BoundAPIGatewayConfigEntry return s.BoundAPIGatewayConfigEntry
} }
type ShadowFileSystemCertificateConfigEntry struct {
ShadowBase
*structs.FileSystemCertificateConfigEntry
}
func (s ShadowFileSystemCertificateConfigEntry) GetRealConfigEntry() structs.ConfigEntry {
return s.FileSystemCertificateConfigEntry
}
type ShadowInlineCertificateConfigEntry struct { type ShadowInlineCertificateConfigEntry struct {
ShadowBase ShadowBase
*structs.InlineCertificateConfigEntry *structs.InlineCertificateConfigEntry

5
agent/consul/fsm/fsm.go

@ -403,6 +403,11 @@ func (c *FSM) registerStreamSnapshotHandlers() {
}, true) }, true)
panicIfErr(err) panicIfErr(err)
err = c.deps.Publisher.RegisterHandler(state.EventTopicFileSystemCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
return c.State().FileSystemCertificateSnapshot(req, buf)
}, true)
panicIfErr(err)
err = c.deps.Publisher.RegisterHandler(state.EventTopicInlineCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) { err = c.deps.Publisher.RegisterHandler(state.EventTopicInlineCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
return c.State().InlineCertificateSnapshot(req, buf) return c.State().InlineCertificateSnapshot(req, buf)
}, true) }, true)

2
agent/consul/gateways/controller_gateways.go

@ -61,7 +61,7 @@ func (r *apiGatewayReconciler) Reconcile(ctx context.Context, req controller.Req
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileHTTPRoute, r.cleanupRoute) return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileHTTPRoute, r.cleanupRoute)
case structs.TCPRoute: case structs.TCPRoute:
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileTCPRoute, r.cleanupRoute) return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileTCPRoute, r.cleanupRoute)
case structs.InlineCertificate: case structs.InlineCertificate, structs.FileSystemCertificate:
return r.enqueueCertificateReferencedGateways(r.fsm.State(), ctx, req) return r.enqueueCertificateReferencedGateways(r.fsm.State(), ctx, req)
case structs.JWTProvider: case structs.JWTProvider:
return r.enqueueJWTProviderReferencedGatewaysAndHTTPRoutes(r.fsm.State(), ctx, req) return r.enqueueJWTProviderReferencedGatewaysAndHTTPRoutes(r.fsm.State(), ctx, req)

1
agent/consul/state/config_entry.go

@ -646,6 +646,7 @@ func validateProposedConfigEntryInGraph(
case structs.ExportedServices: case structs.ExportedServices:
case structs.APIGateway: // TODO Consider checkGatewayClash case structs.APIGateway: // TODO Consider checkGatewayClash
case structs.BoundAPIGateway: case structs.BoundAPIGateway:
case structs.FileSystemCertificate:
case structs.InlineCertificate: case structs.InlineCertificate:
case structs.HTTPRoute: case structs.HTTPRoute:
case structs.TCPRoute: case structs.TCPRoute:

35
agent/consul/state/config_entry_events.go

@ -15,20 +15,21 @@ import (
// Adding events for a new config entry kind? Remember to update ConfigEntryFromStructs and ConfigEntryToStructs. // Adding events for a new config entry kind? Remember to update ConfigEntryFromStructs and ConfigEntryToStructs.
var configEntryKindToTopic = map[string]stream.Topic{ var configEntryKindToTopic = map[string]stream.Topic{
structs.MeshConfig: EventTopicMeshConfig, structs.MeshConfig: EventTopicMeshConfig,
structs.ServiceResolver: EventTopicServiceResolver, structs.ServiceResolver: EventTopicServiceResolver,
structs.IngressGateway: EventTopicIngressGateway, structs.IngressGateway: EventTopicIngressGateway,
structs.ServiceIntentions: EventTopicServiceIntentions, structs.ServiceIntentions: EventTopicServiceIntentions,
structs.ServiceDefaults: EventTopicServiceDefaults, structs.ServiceDefaults: EventTopicServiceDefaults,
structs.APIGateway: EventTopicAPIGateway, structs.APIGateway: EventTopicAPIGateway,
structs.TCPRoute: EventTopicTCPRoute, structs.TCPRoute: EventTopicTCPRoute,
structs.HTTPRoute: EventTopicHTTPRoute, structs.HTTPRoute: EventTopicHTTPRoute,
structs.InlineCertificate: EventTopicInlineCertificate, structs.FileSystemCertificate: EventTopicFileSystemCertificate,
structs.BoundAPIGateway: EventTopicBoundAPIGateway, structs.InlineCertificate: EventTopicInlineCertificate,
structs.RateLimitIPConfig: EventTopicIPRateLimit, structs.BoundAPIGateway: EventTopicBoundAPIGateway,
structs.SamenessGroup: EventTopicSamenessGroup, structs.RateLimitIPConfig: EventTopicIPRateLimit,
structs.JWTProvider: EventTopicJWTProvider, structs.SamenessGroup: EventTopicSamenessGroup,
structs.ExportedServices: EventTopicExportedServices, structs.JWTProvider: EventTopicJWTProvider,
structs.ExportedServices: EventTopicExportedServices,
} }
// EventSubjectConfigEntry is a stream.Subject used to route and receive events // EventSubjectConfigEntry is a stream.Subject used to route and receive events
@ -147,6 +148,12 @@ func (s *Store) HTTPRouteSnapshot(req stream.SubscribeRequest, buf stream.Snapsh
return s.configEntrySnapshot(structs.HTTPRoute, req, buf) return s.configEntrySnapshot(structs.HTTPRoute, req, buf)
} }
// FileSystemCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
// inline-certificate config entries.
func (s *Store) FileSystemCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
return s.configEntrySnapshot(structs.FileSystemCertificate, req, buf)
}
// InlineCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of // InlineCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
// inline-certificate config entries. // inline-certificate config entries.
func (s *Store) InlineCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) { func (s *Store) InlineCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {

3
agent/consul/state/events.go

@ -46,7 +46,8 @@ func PBToStreamSubscribeRequest(req *pbsubscribe.SubscribeRequest, entMeta acl.E
case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway, case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway,
EventTopicServiceIntentions, EventTopicServiceDefaults, EventTopicAPIGateway, EventTopicServiceIntentions, EventTopicServiceDefaults, EventTopicAPIGateway,
EventTopicTCPRoute, EventTopicHTTPRoute, EventTopicJWTProvider, EventTopicInlineCertificate, EventTopicTCPRoute, EventTopicHTTPRoute, EventTopicJWTProvider, EventTopicInlineCertificate,
EventTopicBoundAPIGateway, EventTopicSamenessGroup, EventTopicExportedServices: EventTopicBoundAPIGateway, EventTopicSamenessGroup, EventTopicExportedServices,
EventTopicFileSystemCertificate:
subject = EventSubjectConfigEntry{ subject = EventSubjectConfigEntry{
Name: named.Key, Name: named.Key,
EnterpriseMeta: &entMeta, EnterpriseMeta: &entMeta,

35
agent/consul/state/memdb.go

@ -196,23 +196,24 @@ func (db *readDB) ReadTxn() AbortTxn {
} }
var ( var (
EventTopicServiceHealth = pbsubscribe.Topic_ServiceHealth EventTopicServiceHealth = pbsubscribe.Topic_ServiceHealth
EventTopicServiceHealthConnect = pbsubscribe.Topic_ServiceHealthConnect EventTopicServiceHealthConnect = pbsubscribe.Topic_ServiceHealthConnect
EventTopicMeshConfig = pbsubscribe.Topic_MeshConfig EventTopicMeshConfig = pbsubscribe.Topic_MeshConfig
EventTopicServiceResolver = pbsubscribe.Topic_ServiceResolver EventTopicServiceResolver = pbsubscribe.Topic_ServiceResolver
EventTopicIngressGateway = pbsubscribe.Topic_IngressGateway EventTopicIngressGateway = pbsubscribe.Topic_IngressGateway
EventTopicServiceIntentions = pbsubscribe.Topic_ServiceIntentions EventTopicServiceIntentions = pbsubscribe.Topic_ServiceIntentions
EventTopicServiceDefaults = pbsubscribe.Topic_ServiceDefaults EventTopicServiceDefaults = pbsubscribe.Topic_ServiceDefaults
EventTopicServiceList = pbsubscribe.Topic_ServiceList EventTopicServiceList = pbsubscribe.Topic_ServiceList
EventTopicAPIGateway = pbsubscribe.Topic_APIGateway EventTopicAPIGateway = pbsubscribe.Topic_APIGateway
EventTopicTCPRoute = pbsubscribe.Topic_TCPRoute EventTopicTCPRoute = pbsubscribe.Topic_TCPRoute
EventTopicHTTPRoute = pbsubscribe.Topic_HTTPRoute EventTopicHTTPRoute = pbsubscribe.Topic_HTTPRoute
EventTopicInlineCertificate = pbsubscribe.Topic_InlineCertificate EventTopicFileSystemCertificate = pbsubscribe.Topic_FileSystemCertificate
EventTopicBoundAPIGateway = pbsubscribe.Topic_BoundAPIGateway EventTopicInlineCertificate = pbsubscribe.Topic_InlineCertificate
EventTopicIPRateLimit = pbsubscribe.Topic_IPRateLimit EventTopicBoundAPIGateway = pbsubscribe.Topic_BoundAPIGateway
EventTopicSamenessGroup = pbsubscribe.Topic_SamenessGroup EventTopicIPRateLimit = pbsubscribe.Topic_IPRateLimit
EventTopicJWTProvider = pbsubscribe.Topic_JWTProvider EventTopicSamenessGroup = pbsubscribe.Topic_SamenessGroup
EventTopicExportedServices = pbsubscribe.Topic_ExportedServices EventTopicJWTProvider = pbsubscribe.Topic_JWTProvider
EventTopicExportedServices = pbsubscribe.Topic_ExportedServices
) )
func processDBChanges(tx ReadTxn, changes Changes) ([]stream.Event, error) { func processDBChanges(tx ReadTxn, changes Changes) ([]stream.Event, error) {

16
agent/consul/usagemetrics/usagemetrics_test.go

@ -334,6 +334,14 @@ var baseCases = map[string]testCase{
{Name: "kind", Value: "bound-api-gateway"}, {Name: "kind", Value: "bound-api-gateway"},
}, },
}, },
"consul.usage.test.state.config_entries;datacenter=dc1;kind=file-system-certificate": {
Name: "consul.usage.test.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "file-system-certificate"},
},
},
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": { "consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
Name: "consul.usage.test.state.config_entries", Name: "consul.usage.test.state.config_entries",
Value: 0, Value: 0,
@ -607,6 +615,14 @@ var baseCases = map[string]testCase{
{Name: "kind", Value: "bound-api-gateway"}, {Name: "kind", Value: "bound-api-gateway"},
}, },
}, },
"consul.usage.test.state.config_entries;datacenter=dc1;kind=file-system-certificate": {
Name: "consul.usage.test.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "file-system-certificate"},
},
},
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": { "consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
Name: "consul.usage.test.state.config_entries", Name: "consul.usage.test.state.config_entries",
Value: 0, Value: 0,

2
agent/proxycfg-glue/config_entry.go

@ -69,6 +69,8 @@ func newConfigEntryRequest(req *structs.ConfigEntryQuery, deps ServerDataSourceD
topic = pbsubscribe.Topic_HTTPRoute topic = pbsubscribe.Topic_HTTPRoute
case structs.TCPRoute: case structs.TCPRoute:
topic = pbsubscribe.Topic_TCPRoute topic = pbsubscribe.Topic_TCPRoute
case structs.FileSystemCertificate:
topic = pbsubscribe.Topic_FileSystemCertificate
case structs.InlineCertificate: case structs.InlineCertificate:
topic = pbsubscribe.Topic_InlineCertificate topic = pbsubscribe.Topic_InlineCertificate
case structs.BoundAPIGateway: case structs.BoundAPIGateway:

67
agent/proxycfg/api_gateway.go

@ -63,7 +63,8 @@ func (h *handlerAPIGateway) initialize(ctx context.Context) (ConfigSnapshot, err
snap.APIGateway.BoundListeners = make(map[string]structs.BoundAPIGatewayListener) snap.APIGateway.BoundListeners = make(map[string]structs.BoundAPIGatewayListener)
snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]() snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]()
snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]() snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]()
snap.APIGateway.Certificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]() snap.APIGateway.InlineCertificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]()
snap.APIGateway.FileSystemCertificates = watch.NewMap[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry]()
snap.APIGateway.Upstreams = make(listenerRouteUpstreams) snap.APIGateway.Upstreams = make(listenerRouteUpstreams)
snap.APIGateway.UpstreamsSet = make(routeUpstreamSet) 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 // 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 // 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 // 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 { func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
if u.Err != nil { if u.Err != nil {
return fmt.Errorf("error filling agent cache: %v", u.Err) 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 { if err := h.handleGatewayConfigUpdate(ctx, u, snap, u.CorrelationID); err != nil {
return err 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: case inlineCertificateConfigWatchID:
// Handle change in an attached inline-certificate config entry // Handle change in an attached inline-certificate config entry
if err := h.handleInlineCertConfigUpdate(ctx, u, snap); err != nil { 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 { for _, ref := range listener.Certificates {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
seenRefs[ref] = struct{}{} seenRefs[ref] = struct{}{}
snap.APIGateway.Certificates.InitWatch(ref, cancel)
err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID) if ref.Kind == structs.FileSystemCertificate {
if err != nil { snap.APIGateway.FileSystemCertificates.InitWatch(ref, cancel)
// TODO May want to continue
return err err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, fileSystemCertificateConfigWatchID)
if err != nil {
return err
}
} else {
snap.APIGateway.InlineCertificates.InitWatch(ref, cancel)
err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID)
if err != nil {
return err
}
} }
} }
} }
@ -234,9 +250,16 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd
return true return true
}) })
snap.APIGateway.Certificates.ForEachKey(func(ref structs.ResourceReference) bool { snap.APIGateway.InlineCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
if _, ok := seenRefs[ref]; !ok { if _, ok := seenRefs[ref]; !ok {
snap.APIGateway.Certificates.CancelWatch(ref) snap.APIGateway.InlineCertificates.CancelWatch(ref)
}
return true
})
snap.APIGateway.FileSystemCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
if _, ok := seenRefs[ref]; !ok {
snap.APIGateway.FileSystemCertificates.CancelWatch(ref)
} }
return true return true
}) })
@ -265,6 +288,30 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd
return h.watchIngressLeafCert(ctx, snap) 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 == nil || resp.Entry == nil {
return nil
}
cfg, ok := resp.Entry.(*structs.FileSystemCertificateConfigEntry)
if !ok || cfg == nil {
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.FileSystemCertificates.Set(ref, cfg)
return nil
}
// handleInlineCertConfigUpdate stores the certificate for the gateway // handleInlineCertConfigUpdate stores the certificate for the gateway
func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error { func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
resp, ok := u.Result.(*structs.ConfigEntryResponse) resp, ok := u.Result.(*structs.ConfigEntryResponse)
@ -285,7 +332,7 @@ func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u Up
EnterpriseMeta: *cfg.GetEnterpriseMeta(), EnterpriseMeta: *cfg.GetEnterpriseMeta(),
} }
snap.APIGateway.Certificates.Set(ref, cfg) snap.APIGateway.InlineCertificates.Set(ref, cfg)
return nil return nil
} }

3
agent/proxycfg/proxycfg.deepcopy.go

@ -421,7 +421,8 @@ func (o *configSnapshotAPIGateway) DeepCopy() *configSnapshotAPIGateway {
} }
cp.HTTPRoutes = o.HTTPRoutes.DeepCopy() cp.HTTPRoutes = o.HTTPRoutes.DeepCopy()
cp.TCPRoutes = o.TCPRoutes.DeepCopy() cp.TCPRoutes = o.TCPRoutes.DeepCopy()
cp.Certificates = o.Certificates.DeepCopy() cp.InlineCertificates = o.InlineCertificates.DeepCopy()
cp.FileSystemCertificates = o.FileSystemCertificates.DeepCopy()
if o.Listeners != nil { if o.Listeners != nil {
cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners)) cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners))
for k2, v2 := range o.Listeners { for k2, v2 := range o.Listeners {

11
agent/proxycfg/snapshot.go

@ -9,6 +9,8 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/consul/discoverychain"
@ -17,7 +19,6 @@ import (
"github.com/hashicorp/consul/agent/xds/config" "github.com/hashicorp/consul/agent/xds/config"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/proto/private/pbpeering" "github.com/hashicorp/consul/proto/private/pbpeering"
"github.com/hashicorp/go-hclog"
) )
// TODO(ingress): Can we think of a better for this bag of data? // TODO(ingress): Can we think of a better for this bag of data?
@ -735,9 +736,11 @@ type configSnapshotAPIGateway struct {
// UpstreamsSet is the unique set of UpstreamID the gateway routes to. // UpstreamsSet is the unique set of UpstreamID the gateway routes to.
UpstreamsSet routeUpstreamSet UpstreamsSet routeUpstreamSet
HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry] HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry]
TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry] TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry]
Certificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry]
InlineCertificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry]
FileSystemCertificates watch.Map[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry]
// LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's // LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's
// leaf cert watch with different parameters. // leaf cert watch with different parameters.

1
agent/proxycfg/state.go

@ -38,6 +38,7 @@ const (
gatewayConfigWatchID = "gateway-config" gatewayConfigWatchID = "gateway-config"
apiGatewayConfigWatchID = "api-gateway-config" apiGatewayConfigWatchID = "api-gateway-config"
boundGatewayConfigWatchID = "bound-gateway-config" boundGatewayConfigWatchID = "bound-gateway-config"
fileSystemCertificateConfigWatchID = "file-system-certificate-config"
inlineCertificateConfigWatchID = "inline-certificate-config" inlineCertificateConfigWatchID = "inline-certificate-config"
routeConfigWatchID = "route-config" routeConfigWatchID = "route-config"
externalServiceIDPrefix = "external-service:" externalServiceIDPrefix = "external-service:"

36
agent/structs/config_entry.go

@ -27,22 +27,23 @@ import (
) )
const ( const (
ServiceDefaults string = "service-defaults" ServiceDefaults string = "service-defaults"
ProxyDefaults string = "proxy-defaults" ProxyDefaults string = "proxy-defaults"
ServiceRouter string = "service-router" ServiceRouter string = "service-router"
ServiceSplitter string = "service-splitter" ServiceSplitter string = "service-splitter"
ServiceResolver string = "service-resolver" ServiceResolver string = "service-resolver"
IngressGateway string = "ingress-gateway" IngressGateway string = "ingress-gateway"
TerminatingGateway string = "terminating-gateway" TerminatingGateway string = "terminating-gateway"
ServiceIntentions string = "service-intentions" ServiceIntentions string = "service-intentions"
MeshConfig string = "mesh" MeshConfig string = "mesh"
ExportedServices string = "exported-services" ExportedServices string = "exported-services"
SamenessGroup string = "sameness-group" SamenessGroup string = "sameness-group"
APIGateway string = "api-gateway" APIGateway string = "api-gateway"
BoundAPIGateway string = "bound-api-gateway" BoundAPIGateway string = "bound-api-gateway"
InlineCertificate string = "inline-certificate" FileSystemCertificate string = "file-system-certificate"
HTTPRoute string = "http-route" InlineCertificate string = "inline-certificate"
TCPRoute string = "tcp-route" HTTPRoute string = "http-route"
TCPRoute string = "tcp-route"
// TODO: decide if we want to highlight 'ip' keyword in the name of RateLimitIPConfig // TODO: decide if we want to highlight 'ip' keyword in the name of RateLimitIPConfig
RateLimitIPConfig string = "control-plane-request-limit" RateLimitIPConfig string = "control-plane-request-limit"
JWTProvider string = "jwt-provider" JWTProvider string = "jwt-provider"
@ -71,6 +72,7 @@ var AllConfigEntryKinds = []string{
BoundAPIGateway, BoundAPIGateway,
HTTPRoute, HTTPRoute,
TCPRoute, TCPRoute,
FileSystemCertificate,
InlineCertificate, InlineCertificate,
RateLimitIPConfig, RateLimitIPConfig,
JWTProvider, JWTProvider,
@ -832,6 +834,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
return &APIGatewayConfigEntry{Name: name}, nil return &APIGatewayConfigEntry{Name: name}, nil
case BoundAPIGateway: case BoundAPIGateway:
return &BoundAPIGatewayConfigEntry{Name: name}, nil return &BoundAPIGatewayConfigEntry{Name: name}, nil
case FileSystemCertificate:
return &FileSystemCertificateConfigEntry{Name: name}, nil
case InlineCertificate: case InlineCertificate:
return &InlineCertificateConfigEntry{Name: name}, nil return &InlineCertificateConfigEntry{Name: name}, nil
case HTTPRoute: case HTTPRoute:

73
agent/structs/config_entry_file_system_certificate.go

@ -0,0 +1,73 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package structs
import (
"github.com/hashicorp/consul/acl"
)
// FileSystemCertificateConfigEntry manages the configuration for a certificate
// and private key located in the local file system.
type FileSystemCertificateConfigEntry struct {
// Kind of config entry. This will be set to structs.FileSystemCertificate.
Kind string
// Name is used to match the config entry with its associated file system certificate.
Name string
// Certificate is the optional path to a client certificate to use for TLS connections.
Certificate string
// PrivateKey is the optional path to a private key to use for TLS connections.
PrivateKey string
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex `hash:"ignore"`
}
func (e *FileSystemCertificateConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *FileSystemCertificateConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *FileSystemCertificateConfigEntry) GetKind() string { return FileSystemCertificate }
func (e *FileSystemCertificateConfigEntry) GetName() string { return e.Name }
func (e *FileSystemCertificateConfigEntry) Normalize() error {
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
func (e *FileSystemCertificateConfigEntry) GetMeta() map[string]string { return e.Meta }
func (e *FileSystemCertificateConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
return &e.EnterpriseMeta
}
func (e *FileSystemCertificateConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
func (e *FileSystemCertificateConfigEntry) Validate() error {
return validateConfigEntryMeta(e.Meta)
}
func (e *FileSystemCertificateConfigEntry) Hosts() ([]string, error) {
return []string{}, nil
}
func (e *FileSystemCertificateConfigEntry) CanRead(authz acl.Authorizer) error {
var authzContext acl.AuthorizerContext
e.FillAuthzContext(&authzContext)
return authz.ToAllowAuthorizer().MeshReadAllowed(&authzContext)
}
func (e *FileSystemCertificateConfigEntry) CanWrite(authz acl.Authorizer) error {
var authzContext acl.AuthorizerContext
e.FillAuthzContext(&authzContext)
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
}

15
agent/structs/config_entry_gateways.go

@ -870,7 +870,8 @@ func (e *APIGatewayConfigEntry) validateListeners() error {
ListenerProtocolTCP: true, ListenerProtocolTCP: true,
} }
allowedCertificateKinds := map[string]bool{ allowedCertificateKinds := map[string]bool{
InlineCertificate: true, FileSystemCertificate: true,
InlineCertificate: true,
} }
for _, listener := range e.Listeners { for _, listener := range e.Listeners {
@ -889,7 +890,7 @@ func (e *APIGatewayConfigEntry) validateListeners() error {
} }
for _, certificate := range listener.TLS.Certificates { for _, certificate := range listener.TLS.Certificates {
if !allowedCertificateKinds[certificate.Kind] { if !allowedCertificateKinds[certificate.Kind] {
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind) return fmt.Errorf("unsupported certificate kind: %q, must be 'file-system-certificate' or 'inline-certificate'", certificate.Kind)
} }
if certificate.Name == "" { if certificate.Name == "" {
return fmt.Errorf("certificate reference must have a name") return fmt.Errorf("certificate reference must have a name")
@ -978,6 +979,16 @@ func (a *APIGatewayTLSConfiguration) IsEmpty() bool {
return len(a.Certificates) == 0 && len(a.MaxVersion) == 0 && len(a.MinVersion) == 0 && len(a.CipherSuites) == 0 return len(a.Certificates) == 0 && len(a.MaxVersion) == 0 && len(a.MinVersion) == 0 && len(a.CipherSuites) == 0
} }
func (a *APIGatewayTLSConfiguration) ToGatewayTLSConfig() GatewayTLSConfig {
return GatewayTLSConfig{
Enabled: true,
SDS: nil,
TLSMinVersion: a.MinVersion,
TLSMaxVersion: a.MaxVersion,
CipherSuites: a.CipherSuites,
}
}
// ServiceRouteReferences is a map with a key of ServiceName type for a routed to service from a // ServiceRouteReferences is a map with a key of ServiceName type for a routed to service from a
// bound gateway listener with a value being a slice of resource references of the routes that reference the service // bound gateway listener with a value being a slice of resource references of the routes that reference the service
type ServiceRouteReferences map[ServiceName][]ResourceReference type ServiceRouteReferences map[ServiceName][]ResourceReference

1
agent/structs/deep-copy.sh

@ -24,6 +24,7 @@ deep-copy \
-type DiscoverySplit \ -type DiscoverySplit \
-type ExposeConfig \ -type ExposeConfig \
-type ExportedServicesConfigEntry \ -type ExportedServicesConfigEntry \
-type FileSystemCertificateConfigEntry \
-type GatewayService \ -type GatewayService \
-type GatewayServiceTLSConfig \ -type GatewayServiceTLSConfig \
-type HTTPHeaderModifiers \ -type HTTPHeaderModifiers \

14
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 package structs
@ -318,6 +318,18 @@ func (o *ExportedServicesConfigEntry) DeepCopy() *ExportedServicesConfigEntry {
return &cp 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 // DeepCopy generates a deep copy of *GatewayService
func (o *GatewayService) DeepCopy() *GatewayService { func (o *GatewayService) DeepCopy() *GatewayService {
var cp GatewayService = *o var cp GatewayService = *o

105
agent/xds/listeners_apigateway.go

@ -6,22 +6,19 @@ package xds
import ( import (
"fmt" "fmt"
"golang.org/x/exp/maps"
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_http_jwt_authn_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" envoy_http_jwt_authn_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3"
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"golang.org/x/exp/maps"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/xds/naming"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds/naming"
"github.com/hashicorp/consul/types" "github.com/hashicorp/consul/types"
) )
@ -35,13 +32,19 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
listenerKey := readyListener.listenerKey listenerKey := readyListener.listenerKey
boundListener := readyListener.boundListenerCfg boundListener := readyListener.boundListenerCfg
var certs []structs.InlineCertificateConfigEntry // Collect the referenced certificate config entries
var certs []structs.ConfigEntry
for _, certRef := range boundListener.Certificates { for _, certRef := range boundListener.Certificates {
cert, ok := cfgSnap.APIGateway.Certificates.Get(certRef) switch certRef.Kind {
if !ok { case structs.InlineCertificate:
continue if cert, ok := cfgSnap.APIGateway.InlineCertificates.Get(certRef); ok {
certs = append(certs, cert)
}
case structs.FileSystemCertificate:
if cert, ok := cfgSnap.APIGateway.FileSystemCertificates.Get(certRef); ok {
certs = append(certs, cert)
}
} }
certs = append(certs, *cert)
} }
isAPIGatewayWithTLS := len(boundListener.Certificates) > 0 isAPIGatewayWithTLS := len(boundListener.Certificates) > 0
@ -111,7 +114,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
if isAPIGatewayWithTLS { if isAPIGatewayWithTLS {
// construct SNI filter chains // construct SNI filter chains
l.FilterChains, err = makeInlineOverrideFilterChains( l.FilterChains, err = s.makeInlineOverrideFilterChains(
cfgSnap, cfgSnap,
cfgSnap.APIGateway.TLSConfig, cfgSnap.APIGateway.TLSConfig,
listenerKey.Protocol, listenerKey.Protocol,
@ -225,7 +228,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
sniFilterChains := []*envoy_listener_v3.FilterChain{} sniFilterChains := []*envoy_listener_v3.FilterChain{}
if isAPIGatewayWithTLS { if isAPIGatewayWithTLS {
sniFilterChains, err = makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, certs) sniFilterChains, err = s.makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, certs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -393,11 +396,11 @@ func resolveAPIListenerTLSConfig(listenerTLSCfg structs.APIGatewayTLSConfigurati
// when we have multiple certificates on a single listener, we need // when we have multiple certificates on a single listener, we need
// to duplicate the filter chains with multiple TLS contexts // to duplicate the filter chains with multiple TLS contexts
func makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot, func (s *ResourceGenerator) makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
tlsCfg structs.GatewayTLSConfig, tlsCfg structs.GatewayTLSConfig,
protocol string, protocol string,
filterOpts listenerFilterOpts, filterOpts listenerFilterOpts,
certs []structs.InlineCertificateConfigEntry, certs []structs.ConfigEntry,
) ([]*envoy_listener_v3.FilterChain, error) { ) ([]*envoy_listener_v3.FilterChain, error) {
var chains []*envoy_listener_v3.FilterChain var chains []*envoy_listener_v3.FilterChain
@ -438,50 +441,72 @@ func makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
// we only need to prune out overlapping hosts if we have more than // we only need to prune out overlapping hosts if we have more than
// one certificate // one certificate
for _, cert := range certs { for _, cert := range certs {
hosts, err := cert.Hosts() switch tce := cert.(type) {
if err != nil { case *structs.InlineCertificateConfigEntry:
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts) hosts, err := tce.Hosts()
} if err != nil {
for _, host := range hosts { return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
if _, ok := allCertHosts[host]; ok { }
overlappingHosts[host] = struct{}{} for _, host := range hosts {
if _, ok := allCertHosts[host]; ok {
overlappingHosts[host] = struct{}{}
}
allCertHosts[host] = struct{}{}
} }
allCertHosts[host] = struct{}{} default:
// do nothing for FileSystemCertificates because we don't actually have the certificate available
} }
} }
} }
constructTLSContext := func(certConfig structs.ConfigEntry) (*envoy_tls_v3.CommonTlsContext, error) {
switch tce := certConfig.(type) {
case *structs.InlineCertificateConfigEntry:
return makeInlineTLSContextFromGatewayTLSConfig(tlsCfg, tce), nil
case *structs.FileSystemCertificateConfigEntry:
return makeFileSystemTLSContextFromGatewayTLSConfig(tlsCfg, tce), nil
default:
return nil, fmt.Errorf("unsupported config entry kind %s", tce.GetKind())
}
}
for _, cert := range certs { for _, cert := range certs {
var hosts []string var hosts []string
// if we only have one cert, we just use it for all ingress // if we only have one cert, we just use it for all ingress
if multipleCerts { if multipleCerts {
// otherwise, we need an SNI per cert and to fallback to our ingress switch tce := cert.(type) {
// gateway certificate signed by our Consul CA case *structs.InlineCertificateConfigEntry:
certHosts, err := cert.Hosts() certHosts, err := tce.Hosts()
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts) return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
} }
// filter out any overlapping hosts so we don't have collisions in our filter chains // filter out any overlapping hosts so we don't have collisions in our filter chains
for _, host := range certHosts { for _, host := range certHosts {
if _, ok := overlappingHosts[host]; !ok { if _, ok := overlappingHosts[host]; !ok {
hosts = append(hosts, host) hosts = append(hosts, host)
}
} }
}
if len(hosts) == 0 { if len(hosts) == 0 {
// all of our hosts are overlapping, so we just skip this filter and it'll be // all of our hosts are overlapping, so we just skip this filter and it'll be
// handled by the default filter chain // handled by the default filter chain
continue continue
}
} }
} }
if err := constructChain(cert.Name, hosts, makeInlineTLSContextFromGatewayTLSConfig(tlsCfg, cert)); err != nil { tlsContext, err := constructTLSContext(cert)
if err != nil {
continue
}
if err := constructChain(cert.GetName(), hosts, tlsContext); err != nil {
return nil, err return nil, err
} }
} }
if multipleCerts { if len(certs) > 1 {
// if we have more than one cert, add a default handler that uses the leaf cert from connect // if we have more than one cert, add a default handler that uses the leaf cert from connect
if err := constructChain("default", nil, makeCommonTLSContext(cfgSnap.Leaf(), cfgSnap.RootPEMs(), makeTLSParametersFromGatewayTLSConfig(tlsCfg))); err != nil { if err := constructChain("default", nil, makeCommonTLSContext(cfgSnap.Leaf(), cfgSnap.RootPEMs(), makeTLSParametersFromGatewayTLSConfig(tlsCfg))); err != nil {
return nil, err return nil, err

23
agent/xds/listeners_ingress.go

@ -9,7 +9,6 @@ import (
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/hashicorp/consul/agent/xds/naming"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
@ -17,6 +16,7 @@ import (
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds/naming"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/types" "github.com/hashicorp/consul/types"
) )
@ -391,7 +391,26 @@ func makeTLSParametersFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *env
return makeTLSParametersFromTLSConfig(tlsCfg.TLSMinVersion, tlsCfg.TLSMaxVersion, tlsCfg.CipherSuites) return makeTLSParametersFromTLSConfig(tlsCfg.TLSMinVersion, tlsCfg.TLSMaxVersion, tlsCfg.CipherSuites)
} }
func makeInlineTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert structs.InlineCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext { func makeFileSystemTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert *structs.FileSystemCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext {
return &envoy_tls_v3.CommonTlsContext{
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
TlsCertificateSdsSecretConfigs: []*envoy_tls_v3.SdsSecretConfig{
{
// Delivering via SDS is required in order to get file system watches today.
// https://github.com/envoyproxy/envoy/issues/10387
Name: cert.Name, // Reference the secret returned in xds/secrets.go by name here
SdsConfig: &envoy_core_v3.ConfigSource{
ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_Ads{
Ads: &envoy_core_v3.AggregatedConfigSource{},
},
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
},
},
},
}
}
func makeInlineTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert *structs.InlineCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext {
return &envoy_tls_v3.CommonTlsContext{ return &envoy_tls_v3.CommonTlsContext{
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg), TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
TlsCertificates: []*envoy_tls_v3.TlsCertificate{{ TlsCertificates: []*envoy_tls_v3.TlsCertificate{{

38
agent/xds/secrets.go

@ -7,6 +7,8 @@ import (
"errors" "errors"
"fmt" "fmt"
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
@ -21,13 +23,45 @@ func (s *ResourceGenerator) secretsFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot
} }
switch cfgSnap.Kind { switch cfgSnap.Kind {
case structs.ServiceKindAPIGateway:
return s.secretsFromSnapshotAPIGateway(cfgSnap), nil // return any attached certs
case structs.ServiceKindConnectProxy, case structs.ServiceKindConnectProxy,
structs.ServiceKindTerminatingGateway, structs.ServiceKindTerminatingGateway,
structs.ServiceKindMeshGateway, structs.ServiceKindMeshGateway,
structs.ServiceKindIngressGateway, structs.ServiceKindIngressGateway:
structs.ServiceKindAPIGateway:
return nil, nil return nil, nil
default: default:
return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
} }
} }
// secretsFromSnapshotAPIGateway returns the "secrets" for an api-gateway service
func (s *ResourceGenerator) secretsFromSnapshotAPIGateway(cfgSnap *proxycfg.ConfigSnapshot) []proto.Message {
var resources []proto.Message
cfgSnap.APIGateway.FileSystemCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
cert, ok := cfgSnap.APIGateway.FileSystemCertificates.Get(ref)
if !ok || cert == nil {
return true
}
resources = append(resources, &envoy_tls_v3.Secret{
Name: ref.Name,
Type: &envoy_tls_v3.Secret_TlsCertificate{
TlsCertificate: &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,
},
},
},
},
})
return true
})
return resources
}

17
api/config_entry.go

@ -29,13 +29,14 @@ const (
SamenessGroup string = "sameness-group" SamenessGroup string = "sameness-group"
RateLimitIPConfig string = "control-plane-request-limit" RateLimitIPConfig string = "control-plane-request-limit"
ProxyConfigGlobal string = "global" ProxyConfigGlobal string = "global"
MeshConfigMesh string = "mesh" MeshConfigMesh string = "mesh"
APIGateway string = "api-gateway" APIGateway string = "api-gateway"
TCPRoute string = "tcp-route" TCPRoute string = "tcp-route"
InlineCertificate string = "inline-certificate" FileSystemCertificate string = "file-system-certificate"
HTTPRoute string = "http-route" InlineCertificate string = "inline-certificate"
JWTProvider string = "jwt-provider" HTTPRoute string = "http-route"
JWTProvider string = "jwt-provider"
) )
const ( const (
@ -447,6 +448,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
case TCPRoute: case TCPRoute:
return &TCPRouteConfigEntry{Kind: kind, Name: name}, nil return &TCPRouteConfigEntry{Kind: kind, Name: name}, nil
case FileSystemCertificate:
return &FileSystemCertificateConfigEntry{Kind: kind, Name: name}, nil
case InlineCertificate: case InlineCertificate:
return &InlineCertificateConfigEntry{Kind: kind, Name: name}, nil return &InlineCertificateConfigEntry{Kind: kind, Name: name}, nil
case HTTPRoute: case HTTPRoute:

44
api/config_entry_file_system_certificate.go

@ -0,0 +1,44 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package api
type FileSystemCertificateConfigEntry struct {
// Kind of the config entry. This should be set to api.FileSystemCertificate.
Kind string
Name string
// Certificate is the path to a client certificate to use for TLS connections.
Certificate string `json:",omitempty" alias:"certificate"`
// PrivateKey is the path to a private key to use for TLS connections.
PrivateKey string `json:",omitempty" alias:"private_key"`
Meta map[string]string `json:",omitempty"`
// CreateIndex is the Raft index this entry was created at. This is a
// read-only field.
CreateIndex uint64
// ModifyIndex is used for the Check-And-Set operations and can also be fed
// back into the WaitIndex of the QueryOptions in order to perform blocking
// queries.
ModifyIndex uint64
// Partition is the partition the config entry is associated with.
// Partitioning is a Consul Enterprise feature.
Partition string `json:",omitempty"`
// Namespace is the namespace the config entry is associated with.
// Namespacing is a Consul Enterprise feature.
Namespace string `json:",omitempty"`
}
func (a *FileSystemCertificateConfigEntry) GetKind() string { return FileSystemCertificate }
func (a *FileSystemCertificateConfigEntry) GetName() string { return a.Name }
func (a *FileSystemCertificateConfigEntry) GetPartition() string { return a.Partition }
func (a *FileSystemCertificateConfigEntry) GetNamespace() string { return a.Namespace }
func (a *FileSystemCertificateConfigEntry) GetMeta() map[string]string { return a.Meta }
func (a *FileSystemCertificateConfigEntry) GetCreateIndex() uint64 { return a.CreateIndex }
func (a *FileSystemCertificateConfigEntry) GetModifyIndex() uint64 { return a.ModifyIndex }

6
envoyextensions/xdscommon/xdscommon.go

@ -8,6 +8,7 @@ import (
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -111,7 +112,7 @@ func IndexResources(logger hclog.Logger, resources map[string][]proto.Message) *
} }
func GetResourceName(res proto.Message) string { func GetResourceName(res proto.Message) string {
// NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS // NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS/SDS
switch x := res.(type) { switch x := res.(type) {
case *envoy_listener_v3.Listener: // LDS case *envoy_listener_v3.Listener: // LDS
return x.Name return x.Name
@ -121,6 +122,8 @@ func GetResourceName(res proto.Message) string {
return x.Name return x.Name
case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS
return x.ClusterName return x.ClusterName
case *envoy_tls_v3.Secret: // SDS
return x.Name
default: default:
return "" return ""
} }
@ -133,6 +136,7 @@ func EmptyIndexedResources() *IndexedResources {
RouteType: make(map[string]proto.Message), RouteType: make(map[string]proto.Message),
ClusterType: make(map[string]proto.Message), ClusterType: make(map[string]proto.Message),
EndpointType: make(map[string]proto.Message), EndpointType: make(map[string]proto.Message),
SecretType: make(map[string]proto.Message),
}, },
ChildIndex: map[string]map[string][]string{ ChildIndex: map[string]map[string][]string{
ListenerType: make(map[string][]string), ListenerType: make(map[string][]string),

18
proto/private/pbconfigentry/config_entry.gen.go

@ -408,6 +408,24 @@ func ExposePathFromStructs(t *structs.ExposePath, s *ExposePath) {
s.Protocol = t.Protocol s.Protocol = t.Protocol
s.ParsedFromCheck = t.ParsedFromCheck s.ParsedFromCheck = t.ParsedFromCheck
} }
func FileSystemCertificateToStructs(s *FileSystemCertificate, t *structs.FileSystemCertificateConfigEntry) {
if s == nil {
return
}
t.Certificate = s.Certificate
t.PrivateKey = s.PrivateKey
t.Meta = s.Meta
t.Hash = s.Hash
}
func FileSystemCertificateFromStructs(t *structs.FileSystemCertificateConfigEntry, s *FileSystemCertificate) {
if s == nil {
return
}
s.Certificate = t.Certificate
s.PrivateKey = t.PrivateKey
s.Meta = t.Meta
s.Hash = t.Hash
}
func GatewayServiceTLSConfigToStructs(s *GatewayServiceTLSConfig, t *structs.GatewayServiceTLSConfig) { func GatewayServiceTLSConfigToStructs(s *GatewayServiceTLSConfig, t *structs.GatewayServiceTLSConfig) {
if s == nil { if s == nil {
return return

16
proto/private/pbconfigentry/config_entry.go

@ -85,6 +85,14 @@ func ConfigEntryToStructs(s *ConfigEntry) structs.ConfigEntry {
pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex) pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)
pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta) pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)
return &target return &target
case Kind_KindFileSystemCertificate:
var target structs.FileSystemCertificateConfigEntry
target.Name = s.Name
FileSystemCertificateToStructs(s.GetFileSystemCertificate(), &target)
pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)
pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)
return &target
case Kind_KindInlineCertificate: case Kind_KindInlineCertificate:
var target structs.InlineCertificateConfigEntry var target structs.InlineCertificateConfigEntry
target.Name = s.Name target.Name = s.Name
@ -213,6 +221,14 @@ func ConfigEntryFromStructs(s structs.ConfigEntry) *ConfigEntry {
configEntry.Entry = &ConfigEntry_HTTPRoute{ configEntry.Entry = &ConfigEntry_HTTPRoute{
HTTPRoute: &route, HTTPRoute: &route,
} }
case *structs.FileSystemCertificateConfigEntry:
var cert FileSystemCertificate
FileSystemCertificateFromStructs(v, &cert)
configEntry.Kind = Kind_KindFileSystemCertificate
configEntry.Entry = &ConfigEntry_FileSystemCertificate{
FileSystemCertificate: &cert,
}
case *structs.InlineCertificateConfigEntry: case *structs.InlineCertificateConfigEntry:
var cert InlineCertificate var cert InlineCertificate
InlineCertificateFromStructs(v, &cert) InlineCertificateFromStructs(v, &cert)

10
proto/private/pbconfigentry/config_entry.pb.binary.go

@ -657,6 +657,16 @@ func (msg *BoundAPIGatewayListener) UnmarshalBinary(b []byte) error {
return proto.Unmarshal(b, msg) return proto.Unmarshal(b, msg)
} }
// MarshalBinary implements encoding.BinaryMarshaler
func (msg *FileSystemCertificate) MarshalBinary() ([]byte, error) {
return proto.Marshal(msg)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (msg *FileSystemCertificate) UnmarshalBinary(b []byte) error {
return proto.Unmarshal(b, msg)
}
// MarshalBinary implements encoding.BinaryMarshaler // MarshalBinary implements encoding.BinaryMarshaler
func (msg *InlineCertificate) MarshalBinary() ([]byte, error) { func (msg *InlineCertificate) MarshalBinary() ([]byte, error) {
return proto.Marshal(msg) return proto.Marshal(msg)

3970
proto/private/pbconfigentry/config_entry.pb.go

File diff suppressed because it is too large Load Diff

15
proto/private/pbconfigentry/config_entry.proto

@ -54,6 +54,7 @@ enum Kind {
KindSamenessGroup = 11; KindSamenessGroup = 11;
KindJWTProvider = 12; KindJWTProvider = 12;
KindExportedServices = 13; KindExportedServices = 13;
KindFileSystemCertificate = 14;
} }
message ConfigEntry { message ConfigEntry {
@ -77,6 +78,7 @@ message ConfigEntry {
SamenessGroup SamenessGroup = 15; SamenessGroup SamenessGroup = 15;
JWTProvider JWTProvider = 16; JWTProvider JWTProvider = 16;
ExportedServices ExportedServices = 17; ExportedServices ExportedServices = 17;
FileSystemCertificate FileSystemCertificate = 18;
} }
} }
@ -867,6 +869,19 @@ message BoundAPIGatewayListener {
repeated ResourceReference Routes = 3; repeated ResourceReference Routes = 3;
} }
// mog annotation:
//
// target=github.com/hashicorp/consul/agent/structs.FileSystemCertificateConfigEntry
// output=config_entry.gen.go
// name=Structs
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
message FileSystemCertificate {
map<string, string> Meta = 1;
string Certificate = 2;
string PrivateKey = 3;
uint64 Hash = 4;
}
// mog annotation: // mog annotation:
// //
// target=github.com/hashicorp/consul/agent/structs.InlineCertificateConfigEntry // target=github.com/hashicorp/consul/agent/structs.InlineCertificateConfigEntry

84
proto/private/pbsubscribe/subscribe.pb.go

@ -80,6 +80,8 @@ const (
Topic_JWTProvider Topic = 16 Topic_JWTProvider Topic = 16
// ExportedServices topic contains events for changes to exported-services. // ExportedServices topic contains events for changes to exported-services.
Topic_ExportedServices Topic = 17 Topic_ExportedServices Topic = 17
// FileSystemCertificate topic contains events for changes to file-system-certificates.
Topic_FileSystemCertificate Topic = 18
) )
// Enum value maps for Topic. // Enum value maps for Topic.
@ -103,26 +105,28 @@ var (
15: "SamenessGroup", 15: "SamenessGroup",
16: "JWTProvider", 16: "JWTProvider",
17: "ExportedServices", 17: "ExportedServices",
18: "FileSystemCertificate",
} }
Topic_value = map[string]int32{ Topic_value = map[string]int32{
"Unknown": 0, "Unknown": 0,
"ServiceHealth": 1, "ServiceHealth": 1,
"ServiceHealthConnect": 2, "ServiceHealthConnect": 2,
"MeshConfig": 3, "MeshConfig": 3,
"ServiceResolver": 4, "ServiceResolver": 4,
"IngressGateway": 5, "IngressGateway": 5,
"ServiceIntentions": 6, "ServiceIntentions": 6,
"ServiceList": 7, "ServiceList": 7,
"ServiceDefaults": 8, "ServiceDefaults": 8,
"APIGateway": 9, "APIGateway": 9,
"TCPRoute": 10, "TCPRoute": 10,
"HTTPRoute": 11, "HTTPRoute": 11,
"InlineCertificate": 12, "InlineCertificate": 12,
"BoundAPIGateway": 13, "BoundAPIGateway": 13,
"IPRateLimit": 14, "IPRateLimit": 14,
"SamenessGroup": 15, "SamenessGroup": 15,
"JWTProvider": 16, "JWTProvider": 16,
"ExportedServices": 17, "ExportedServices": 17,
"FileSystemCertificate": 18,
} }
) )
@ -1004,7 +1008,7 @@ var file_private_pbsubscribe_subscribe_proto_rawDesc = []byte{
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45,
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a,
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0xdb, 0x02, 0x0a, 0x05, 0x54, 0x6f, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0xf6, 0x02, 0x0a, 0x05, 0x54, 0x6f,
0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00,
0x12, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74,
0x68, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x68, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65,
@ -1026,26 +1030,28 @@ var file_private_pbsubscribe_subscribe_proto_rawDesc = []byte{
0x0d, 0x53, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x10, 0x0f, 0x0d, 0x53, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x10, 0x0f,
0x12, 0x0f, 0x0a, 0x0b, 0x4a, 0x57, 0x54, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x10, 0x12, 0x0f, 0x0a, 0x0b, 0x4a, 0x57, 0x54, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x10,
0x10, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x10, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x10, 0x11, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x76, 0x69, 0x63, 0x65, 0x73, 0x10, 0x11, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x69, 0x6c, 0x65, 0x53,
0x6f, 0x67, 0x4f, 0x70, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x12, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x4f, 0x70, 0x12,
0x10, 0x01, 0x32, 0x61, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0e, 0x0a,
0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x01, 0x32, 0x61, 0x0a,
0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x62, 0x17, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x62, 0x73,
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
0x69, 0x62, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x08, 0xe2, 0x86, 0x04, 0x04, 0x08, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x02, 0x10, 0x09, 0x30, 0x01, 0x42, 0x9a, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x45,
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42, 0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x08, 0xe2, 0x86, 0x04, 0x04, 0x08, 0x02, 0x10, 0x09, 0x30, 0x01,
0x62, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x42, 0x9a, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x65, 0x42, 0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x6f,
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75,
0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f,
0x62, 0x65, 0xca, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02, 0x70, 0x62, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x58,
0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x58, 0xaa, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xca, 0x02, 0x09,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02, 0x15, 0x53, 0x75, 0x62, 0x73,
0x62, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x63, 0x72, 0x69, 0x62, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0xea, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

3
proto/private/pbsubscribe/subscribe.proto

@ -109,6 +109,9 @@ enum Topic {
// ExportedServices topic contains events for changes to exported-services. // ExportedServices topic contains events for changes to exported-services.
ExportedServices = 17; ExportedServices = 17;
// FileSystemCertificate topic contains events for changes to file-system-certificates.
FileSystemCertificate = 18;
} }
message NamedSubject { message NamedSubject {

140
website/content/api-docs/config.mdx

@ -39,23 +39,24 @@ The corresponding CLI command is [`consul config write`](/consul/commands/config
The ACL required depends on the config entry being written: The ACL required depends on the config entry being written:
| Config Entry Kind | Required ACLs | | Config Entry Kind | Required ACLs |
| ------------------- | -------------------------------- | | ----------------------- | -------------------------------- |
| api-gateway | `mesh:write` or `operator:write` | | api-gateway | `mesh:write` or `operator:write` |
| bound-api-gateway | Not writable. | | bound-api-gateway | Not writable. |
| exported-services | `mesh:write` or `operator:write` | | exported-services | `mesh:write` or `operator:write` |
| http-route | `mesh:write` or `operator:write` | | file-system-certificate | `mesh:write` or `operator:write` |
| ingress-gateway | `mesh:write` or `operator:write` | | http-route | `mesh:write` or `operator:write` |
| inline-certificate | `mesh:write` or `operator:write` | | ingress-gateway | `mesh:write` or `operator:write` |
| mesh | `mesh:write` or `operator:write` | | inline-certificate | `mesh:write` or `operator:write` |
| proxy-defaults | `mesh:write` or `operator:write` | | mesh | `mesh:write` or `operator:write` |
| service-defaults | `service:write` | | proxy-defaults | `mesh:write` or `operator:write` |
| service-intentions | `intentions:write` | | service-defaults | `service:write` |
| service-resolver | `service:write` | | service-intentions | `intentions:write` |
| service-router | `service:write` | | service-resolver | `service:write` |
| service-splitter | `service:write` | | service-router | `service:write` |
| tcp-route | `mesh:write` or `operator:write` | | service-splitter | `service:write` |
| terminating-gateway | `mesh:write` or `operator:write` | | tcp-route | `mesh:write` or `operator:write` |
| terminating-gateway | `mesh:write` or `operator:write` |
### Query Parameters ### Query Parameters
@ -114,23 +115,24 @@ The corresponding CLI command is [`consul config read`](/consul/commands/config/
The ACL required depends on the config entry kind being read: The ACL required depends on the config entry kind being read:
| Config Entry Kind | Required ACLs | | Config Entry Kind | Required ACLs |
| ------------------- | -------------------------------- | | ----------------------- | -------------------------------- |
| api-gateway | `service:read` | | api-gateway | `service:read` |
| bound-api-gateway | `service:read` | | bound-api-gateway | `service:read` |
| exported-services | `mesh:read` or `operator:read` | | exported-services | `mesh:read` or `operator:read` |
| http-route | `mesh:read` or `operator:read` | | file-system-certificate | `mesh:read` or `operator:read` |
| ingress-gateway | `service:read` | | http-route | `mesh:read` or `operator:read` |
| inline-certificate | `mesh:read` or `operator:read` | | ingress-gateway | `service:read` |
| mesh | No ACL required | | inline-certificate | `mesh:read` or `operator:read` |
| proxy-defaults | No ACL required | | mesh | No ACL required |
| service-defaults | `service:read` | | proxy-defaults | No ACL required |
| service-intentions | `intentions:read` | | service-defaults | `service:read` |
| service-resolver | `service:read` | | service-intentions | `intentions:read` |
| service-router | `service:read` | | service-resolver | `service:read` |
| service-splitter | `service:read` | | service-router | `service:read` |
| tcp-route | `mesh:read` or `operator:read` | | service-splitter | `service:read` |
| terminating-gateway | `service:read` | | tcp-route | `mesh:read` or `operator:read` |
| terminating-gateway | `service:read` |
### Path Parameters ### Path Parameters
@ -192,23 +194,24 @@ The table below shows this endpoint's support for
The ACL required depends on the config entry kind being read: The ACL required depends on the config entry kind being read:
| Config Entry Kind | Required ACLs | | Config Entry Kind | Required ACLs |
| ------------------- | -------------------------------- | | ----------------------- | -------------------------------- |
| api-gateway | `service:read` | | api-gateway | `service:read` |
| bound-api-gateway | `service:read` | | bound-api-gateway | `service:read` |
| exported-services | `mesh:read` or `operator:read` | | exported-services | `mesh:read` or `operator:read` |
| http-route | `mesh:read` or `operator:read` | | file-system-certificate | `mesh:read` or `operator:read` |
| ingress-gateway | `service:read` | | http-route | `mesh:read` or `operator:read` |
| inline-certificate | `mesh:read` or `operator:read` | | ingress-gateway | `service:read` |
| mesh | No ACL required | | inline-certificate | `mesh:read` or `operator:read` |
| proxy-defaults | No ACL required | | mesh | No ACL required |
| service-defaults | `service:read` | | proxy-defaults | No ACL required |
| service-intentions | `intentions:read` | | service-defaults | `service:read` |
| service-resolver | `service:read` | | service-intentions | `intentions:read` |
| service-router | `service:read` | | service-resolver | `service:read` |
| service-splitter | `service:read` | | service-router | `service:read` |
| tcp-route | `mesh:read` or `operator:read` | | service-splitter | `service:read` |
| terminating-gateway | `service:read` | | tcp-route | `mesh:read` or `operator:read` |
| terminating-gateway | `service:read` |
The corresponding CLI command is [`consul config list`](/consul/commands/config/list). The corresponding CLI command is [`consul config list`](/consul/commands/config/list).
@ -276,23 +279,24 @@ The table below shows this endpoint's support for
The ACL required depends on the config entry kind being deleted: The ACL required depends on the config entry kind being deleted:
| Config Entry Kind | Required ACLs | | Config Entry Kind | Required ACLs |
| ------------------- | -------------------------------- | | ----------------------- | -------------------------------- |
| api-gateway | `mesh:write` or `operator:write` | | api-gateway | `mesh:write` or `operator:write` |
| bound-api-gateway | Not writable. | | bound-api-gateway | Not writable. |
| exported-services | `mesh:write` or `operator:write` | | exported-services | `mesh:write` or `operator:write` |
| http-route | `mesh:write` or `operator:write` | | file-system-certificate | `mesh:write` or `operator:write` |
| ingress-gateway | `mesh:write` or `operator:write` | | http-route | `mesh:write` or `operator:write` |
| inline-certificate | `mesh:write` or `operator:write` | | ingress-gateway | `mesh:write` or `operator:write` |
| mesh | `mesh:write` or `operator:write` | | inline-certificate | `mesh:write` or `operator:write` |
| proxy-defaults | `mesh:write` or `operator:write` | | mesh | `mesh:write` or `operator:write` |
| service-defaults | `service:write` | | proxy-defaults | `mesh:write` or `operator:write` |
| service-intentions | `intentions:write` | | service-defaults | `service:write` |
| service-resolver | `service:write` | | service-intentions | `intentions:write` |
| service-router | `service:write` | | service-resolver | `service:write` |
| service-splitter | `service:write` | | service-router | `service:write` |
| tcp-route | `mesh:write` or `operator:write` | | service-splitter | `service:write` |
| terminating-gateway | `mesh:write` or `operator:write` | | tcp-route | `mesh:write` or `operator:write` |
| terminating-gateway | `mesh:write` or `operator:write` |
The corresponding CLI command is [`consul config delete`](/consul/commands/config/delete). The corresponding CLI command is [`consul config delete`](/consul/commands/config/delete).

44
website/content/docs/connect/config-entries/api-gateway.mdx

@ -31,19 +31,19 @@ The following list outlines field hierarchy, language-specific data types, and r
- [`MaxVersion`](#listeners-tls-maxversion): string | no default - [`MaxVersion`](#listeners-tls-maxversion): string | no default
- [`CipherSuites`](#listeners-tls-ciphersuites): list of strings | Envoy default cipher suites - [`CipherSuites`](#listeners-tls-ciphersuites): list of strings | Envoy default cipher suites
- [`Certificates`](#listeners-tls-certificates): list of objects | no default - [`Certificates`](#listeners-tls-certificates): list of objects | no default
- [`Kind`](#listeners-tls-certificates-kind): string | must be `"inline-certificate"` - [`Kind`](#listeners-tls-certificates-kind): string | no default
- [`Name`](#listeners-tls-certificates-name): string | no default - [`Name`](#listeners-tls-certificates-name): string | no default
- [`Namespace`](#listeners-tls-certificates-namespace): string | no default <EnterpriseAlert inline /> - [`Namespace`](#listeners-tls-certificates-namespace): string | no default <EnterpriseAlert inline />
- [`Partition`](#listeners-tls-certificates-partition): string | no default <EnterpriseAlert inline /> - [`Partition`](#listeners-tls-certificates-partition): string | no default <EnterpriseAlert inline />
- [`default`](#listeners-default): map - [`default`](#listeners-default): map
- [`JWT`](#listeners-default-jwt): map - [`JWT`](#listeners-default-jwt): map
- [`Providers`](#listeners-default-jwt-providers): list - [`Providers`](#listeners-default-jwt-providers): list
- [`Name`](#listeners-default-jwt-providers): string - [`Name`](#listeners-default-jwt-providers): string
- [`VerifyClaims`](#listeners-default-jwt-providers): map - [`VerifyClaims`](#listeners-default-jwt-providers): map
- [`Path`](#listeners-default-jwt-providers): list - [`Path`](#listeners-default-jwt-providers): list
- [`Value`](#listeners-default-jwt-providers): string - [`Value`](#listeners-default-jwt-providers): string
- [`override`](#listeners-override): map - [`override`](#listeners-override): map
- [`JWT`](#listeners-override-jwt): map - [`JWT`](#listeners-override-jwt): map
- [`Providers`](#listeners-override-jwt-providers): list - [`Providers`](#listeners-override-jwt-providers): list
- [`Name`](#listeners-override-jwt-providers): string - [`Name`](#listeners-override-jwt-providers): string
- [`VerifyClaims`](#listeners-override-jwt-providers): map - [`VerifyClaims`](#listeners-override-jwt-providers): map
@ -81,8 +81,8 @@ Listeners = [
] ]
Certificates = [ Certificates = [
{ {
Kind = "inline-certificate" Kind = "file-system-certificate"
Name = "<name of inline-certificate>" Name = "<name of file-system-certificate>"
Namespace = "<enterprise: namespace of the certificate>" Namespace = "<enterprise: namespace of the certificate>"
Partition = "<enterprise: partition of the certificate>" Partition = "<enterprise: partition of the certificate>"
} }
@ -97,7 +97,7 @@ Listeners = [
Value = "<value of claim>" Value = "<value of claim>"
} }
] ]
} }
} }
override = { override = {
JWT = { JWT = {
@ -108,7 +108,7 @@ Listeners = [
Value = "<value of claim>" Value = "<value of claim>"
} }
] ]
} }
} }
} }
] ]
@ -136,8 +136,8 @@ Listeners = [
], ],
"Certificates": [ "Certificates": [
{ {
"Kind": "inline-certificate", "Kind": "file-system-certificate",
"Name": "<name of inline-certificate>", "Name": "<name of file-system-certificate>",
"Namespace": "<enterprise: namespace of the certificate>", "Namespace": "<enterprise: namespace of the certificate>",
"Partition": "<enterprise: partition of the certificate>" "Partition": "<enterprise: partition of the certificate>"
} }
@ -349,7 +349,7 @@ Specifies a list of cipher suites that the listener supports when negotiating co
### `Listeners[].TLS.Certificates[]` ### `Listeners[].TLS.Certificates[]`
The list of references to inline certificates that the listener uses for TLS termination. The list of references to file system or inline certificates that the listener uses for TLS termination.
#### Values #### Values
@ -362,17 +362,17 @@ The list of references to inline certificates that the listener uses for TLS ter
### `Listeners[].TLS.Certificates[].Kind` ### `Listeners[].TLS.Certificates[].Kind`
The list of references to inline-certificates that the listener uses for TLS termination. The list of references to certificates that the listener uses for TLS termination.
#### Values #### Values
- Default: None - Default: None
- This field is required and must be set to `"inline-certificate"`. - This field is required.
- Data type: string - The data type is one of the following string values: `"file-system-certificate"` or `"inline-certificate"`.
### `Listeners[].TLS.Certificates[].Name` ### `Listeners[].TLS.Certificates[].Name`
The list of references to inline certificates that the listener uses for TLS termination. Specifies the name of the file system or inline certificate that the listener uses for TLS termination.
#### Values #### Values
@ -400,7 +400,7 @@ Specifies the Enterprise [admin partition](/consul/docs/enterprise/admin-partiti
### `Listeners[].default` ### `Listeners[].default`
Specifies a block of default configurations to apply to the gateway listener. All routes attached to the listener inherit the default configurations. You can specify override configurations that have precedence over default configurations in the [`override` block](#listeners-override) as well as in the `JWT` block in the [HTTP route configuration entry](/consul/docs/connect/config-entries/http-route). Specifies a block of default configurations to apply to the gateway listener. All routes attached to the listener inherit the default configurations. You can specify override configurations that have precedence over default configurations in the [`override` block](#listeners-override) as well as in the `JWT` block in the [HTTP route configuration entry](/consul/docs/connect/config-entries/http-route).
#### Values #### Values
@ -409,7 +409,7 @@ Specifies a block of default configurations to apply to the gateway listener. Al
### `Listeners[].default{}.JWT` ### `Listeners[].default{}.JWT`
Specifies a block of default JWT verification configurations to apply to the gateway listener. Specify configurations that have precedence over the defaults in either the [`override.JWT` block](#listeners-override) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt) in the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways. Specifies a block of default JWT verification configurations to apply to the gateway listener. Specify configurations that have precedence over the defaults in either the [`override.JWT` block](#listeners-override) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt) in the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
#### Values #### Values
@ -418,7 +418,7 @@ Specifies a block of default JWT verification configurations to apply to the gat
### `Listeners[].default{}.JWT{}.Providers` ### `Listeners[].default{}.JWT{}.Providers`
Specifies a list of default JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. Specify configurations that have precedence over the defaults in either the [`override.JWT.Providers` block](#listeners-override-providers) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt-providers) of the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways. Specifies a list of default JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. Specify configurations that have precedence over the defaults in either the [`override.JWT.Providers` block](#listeners-override-providers) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt-providers) of the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
#### Values #### Values
@ -432,7 +432,7 @@ The following table describes the parameters you can specify in a member of the
| `Name` | Specifies the name of the provider. | String | None | | `Name` | Specifies the name of the provider. | String | None |
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None | | `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration. Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
### `Listeners[].override` ### `Listeners[].override`
@ -454,7 +454,7 @@ Specifies a block of JWT verification configurations to apply to the gateway lis
### `Listeners[].override{}.JWT{}.Providers` ### `Listeners[].override{}.JWT{}.Providers`
Specifies a list of JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. The override settings have precedence over `Listeners[].defaults{}.JWT{}.Providers` as well as any listener-specific configuration. Specifies a list of JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. The override settings have precedence over `Listeners[].defaults{}.JWT{}.Providers` as well as any listener-specific configuration.
#### Values #### Values
@ -468,7 +468,7 @@ The following table describes the parameters you can specify in a member of the
| `Name` | Specifies the name of the provider. | String | None | | `Name` | Specifies the name of the provider. | String | None |
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None | | `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration. Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
## Examples ## Examples
@ -530,7 +530,7 @@ Listeners = [
{ {
"name": "listener-one", "name": "listener-one",
"port": 9001, "port": 9001,
"protocol": "http", "protocol": "http",
"override": { "override": {
"JWT": { "JWT": {
"Providers": [{ "Providers": [{
@ -559,4 +559,4 @@ Listeners = [
``` ```
</Tab> </Tab>
</Tabs> </Tabs>

127
website/content/docs/connect/config-entries/file-system-certificate.mdx

@ -0,0 +1,127 @@
---
layout: docs
page_title: File System Certificate Configuration Reference
description: Learn how to configure a file system certificate bound to an API Gateway on VMs.
---
# File system certificate configuration reference
This topic provides reference information for the gateway file system certificate
configuration entry. For information about certificate configuration for Kubernetes environments, refer to [Gateway Resource Configuration](/consul/docs/connect/gateways/api-gateway/configuration/gateway).
## Configuration model
The following list outlines field hierarchy, language-specific data types, and
requirements in a `file-system-certificate` configuration entry. Click on a property name
to view additional details, including default values.
- [`Kind`](#kind): string | must be `"file-system-certificate"`
- [`Name`](#name): string | no default
- [`Namespace`](#namespace): string | no default <EnterpriseAlert inline />
- [`Partition`](#partition): string | no default <EnterpriseAlert inline />
- [`Meta`](#meta): map | no default
- [`Certificate`](#certificate): string | no default
- [`PrivateKey`](#privatekey): string | no default
## Complete configuration
When every field is defined, a `file-system-certificate` configuration entry has the following form:
<CodeTabs>
```HCL
Kind = "file-system-certificate"
Name = "<name of certificate>"
Meta = {
"<any key>" = "<any value>"
}
Certificate = "<filepath to public certificate>"
PrivateKey = "<filepath to private key>"
```
```JSON
{
"Kind": "file-system-certificate",
"Name": "<name of certificate>",
"Meta": {
"any key": "any value"
}
"Certificate": "<filepath to public certificate>",
"PrivateKey": "<filepath to private key>"
}
```
</CodeTabs>
## Specification
### `Kind`
Specifies the type of configuration entry to implement.
#### Values
- Default: none
- This field is required.
- Data type: string that must equal `"file-system-certificate"`
### `Name`
Specifies a name for the configuration entry. The name is metadata that you can
use to reference the configuration entry when performing Consul operations, such
as applying a configuration entry to a specific cluster.
#### Values
- Default: none
- This field is required.
- Data type: string
### `Namespace` <EnterpriseAlert inline />
Specifies the Enterprise [namespace](/consul/docs/enterprise/namespaces) to apply to the configuration entry.
#### Values
- Default: `"default"` in Enterprise
- Data type: string
### `Partition` <EnterpriseAlert inline />
Specifies the Enterprise [admin partition](/consul/docs/enterprise/admin-partitions) to apply to the configuration entry.
#### Values
- Default: `"default"` in Enterprise
- Data type: string
### `Meta`
Specifies an arbitrary set of key-value pairs to associate with the gateway.
#### Values
- Default: none
- Data type: map containing one or more keys and string values.
### `Certificate`
Specifies the filepath to a public certificate to use for TLS. This filepath must be accessible to the API gateway proxy at runtime.
#### Values
- Default: none
- This field is required.
- Data type: string value of the filepath to a public certificate
### `PrivateKey`
Specifies the filepath to a private key to use for TLS. This filepath must be accessible to the API gateway proxy at runtime.
#### Values
- Default: none
- This field is required.
- Data type: string value of the filepath to a private key

9
website/content/docs/connect/gateways/api-gateway/configuration/index.mdx

@ -7,7 +7,7 @@ description: >-
# Consul API gateway configuration overview # Consul API gateway configuration overview
This topic provides an overview of the configuration items you can use to create API gateways, configure listeners, define routes, and apply additional resources that may be necessary to operate Consul API gateways in your environment. This topic provides an overview of the configuration items you can use to create API gateways, configure listeners, define routes, and apply additional resources that may be necessary to operate Consul API gateways in your environment.
## Configurations for virtual machines ## Configurations for virtual machines
@ -18,9 +18,10 @@ Apply the following configuration items if your network runs on virtual machines
| [`api-gateway`](/consul/docs/connect/config-entries/api-gateway) | Defines the main infrastructure resource for declaring an API gateway and listeners on the gateway. | [Deploy API gateway listeners on virtual machines](/consul/docs/connect/gateways/api-gateway/deploy/listeners-vms) | | [`api-gateway`](/consul/docs/connect/config-entries/api-gateway) | Defines the main infrastructure resource for declaring an API gateway and listeners on the gateway. | [Deploy API gateway listeners on virtual machines](/consul/docs/connect/gateways/api-gateway/deploy/listeners-vms) |
| [`http-route`](/consul/docs/connect/config-entries/http-route) | Enables HTTP traffic to reach services in the mesh from a listener on the gateway.| <nobr>[Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms)</nobr> | | [`http-route`](/consul/docs/connect/config-entries/http-route) | Enables HTTP traffic to reach services in the mesh from a listener on the gateway.| <nobr>[Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms)</nobr> |
| [`tcp-route`](/consul/docs/connect/config-entries/tcp-route) | Enables TCP traffic to reach services in the mesh from a listener on the gateway.| [Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms) | | [`tcp-route`](/consul/docs/connect/config-entries/tcp-route) | Enables TCP traffic to reach services in the mesh from a listener on the gateway.| [Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms) |
| <nobr>[`inline-certificate`](/consul/docs/connect/config-entries/inline-certificate)</nobr> | Provides gateway artificats with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) | | <nobr>[`file-system-certificate`](/consul/docs/connect/config-entries/file-system-certificate)</nobr> | Provides gateway with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) |
| <nobr>[`inline-certificate`](/consul/docs/connect/config-entries/inline-certificate)</nobr> | Provides gateway with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) |
| [`service-intentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh | | [`service-intentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
## Configurations for Kubernetes ## Configurations for Kubernetes
Apply the following configuration items if your network runs on Kubernetes: Apply the following configuration items if your network runs on Kubernetes:
@ -32,7 +33,7 @@ Apply the following configuration items if your network runs on Kubernetes:
| [`GatewayClassConfig`](/consul/docs/connect/gateways/api-gateway/configuration/gatewayclassconfig) | Describes additional gateway-related configuration parameters for the `GatewayClass` resource. | [Deploy listeners on Kubernetes](/consul/docs/connect/gateways/api-gateway/deploy/listeners-k8s) | | [`GatewayClassConfig`](/consul/docs/connect/gateways/api-gateway/configuration/gatewayclassconfig) | Describes additional gateway-related configuration parameters for the `GatewayClass` resource. | [Deploy listeners on Kubernetes](/consul/docs/connect/gateways/api-gateway/deploy/listeners-k8s) |
| [`Routes`](/consul/docs/connect/gateways/api-gateway/configuration/routes) | Specifies paths from the gateway listener to backend services. | <nobr>[Define routes on Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/routes-k8s)</nobr><p>[Reroute traffic in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/reroute-http-requests)</p><p>[Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services)</p> | | [`Routes`](/consul/docs/connect/gateways/api-gateway/configuration/routes) | Specifies paths from the gateway listener to backend services. | <nobr>[Define routes on Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/routes-k8s)</nobr><p>[Reroute traffic in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/reroute-http-requests)</p><p>[Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services)</p> |
| [`MeshServices`](/consul/docs/connect/gateways/api-gateway/configuration/meshservices) | Enables routes to reference services in Consul. | [Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services) | | [`MeshServices`](/consul/docs/connect/gateways/api-gateway/configuration/meshservices) | Enables routes to reference services in Consul. | [Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services) |
| [`ServiceIntentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh | | [`ServiceIntentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
<!-- Reuse later for a topic about creating custom api gateway classes <!-- Reuse later for a topic about creating custom api gateway classes
You can create a basic Gateway object using the default [`gatewayClassName`](/consul/docs/connect/gateways/api-gateway/configuration/gateway#gatewayclassname) (`consul`). If you want to create custom Gateways suitable for your environment, complete the following steps: You can create a basic Gateway object using the default [`gatewayClassName`](/consul/docs/connect/gateways/api-gateway/configuration/gateway#gatewayclassname) (`consul`). If you want to create custom Gateways suitable for your environment, complete the following steps:

4
website/data/docs-nav-data.json

@ -465,6 +465,10 @@
"title": "TCP route", "title": "TCP route",
"path": "connect/config-entries/tcp-route" "path": "connect/config-entries/tcp-route"
}, },
{
"title": "File system certificate",
"path": "connect/config-entries/file-system-certificate"
},
{ {
"title": "Inline certificate", "title": "Inline certificate",
"path": "connect/config-entries/inline-certificate" "path": "connect/config-entries/inline-certificate"

Loading…
Cancel
Save