mirror of https://github.com/hashicorp/consul
TLS Origination for Terminating Gateways (#7671)
parent
c1dc2f12f7
commit
137a2c32c6
|
@ -34,6 +34,7 @@ func TestGatewayServices(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
reply := args.Get(2).(*structs.IndexedGatewayServices)
|
reply := args.Get(2).(*structs.IndexedGatewayServices)
|
||||||
|
|
|
@ -731,6 +731,7 @@ func TestInternal_TerminatingGatewayServices(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "db",
|
Name: "db",
|
||||||
|
@ -740,6 +741,7 @@ func TestInternal_TerminatingGatewayServices(t *testing.T) {
|
||||||
CAFile: "ca.crt",
|
CAFile: "ca.crt",
|
||||||
CertFile: "client.crt",
|
CertFile: "client.crt",
|
||||||
KeyFile: "client.key",
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -766,6 +768,7 @@ func TestInternal_TerminatingGatewayServices(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Service: structs.NewServiceID("db", nil),
|
Service: structs.NewServiceID("db", nil),
|
||||||
|
@ -782,6 +785,7 @@ func TestInternal_TerminatingGatewayServices(t *testing.T) {
|
||||||
CAFile: "ca.crt",
|
CAFile: "ca.crt",
|
||||||
CertFile: "client.crt",
|
CertFile: "client.crt",
|
||||||
KeyFile: "client.key",
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
FromWildcard: true,
|
FromWildcard: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1037,12 +1037,16 @@ func (s *Store) serviceNodes(ws memdb.WatchSet, serviceName string, connect bool
|
||||||
// Gateways are tracked in a separate table, and we append them to the result set.
|
// Gateways are tracked in a separate table, and we append them to the result set.
|
||||||
// We append rather than replace since it allows users to migrate a service
|
// We append rather than replace since it allows users to migrate a service
|
||||||
// to the mesh with a mix of sidecars and gateways until all its instances have a sidecar.
|
// to the mesh with a mix of sidecars and gateways until all its instances have a sidecar.
|
||||||
|
var idx uint64
|
||||||
if connect {
|
if connect {
|
||||||
// Look up gateway nodes associated with the service
|
// Look up gateway nodes associated with the service
|
||||||
_, nodes, chs, err := s.serviceGatewayNodes(tx, ws, serviceName, structs.ServiceKindTerminatingGateway, entMeta)
|
gwIdx, nodes, chs, err := s.serviceGatewayNodes(tx, ws, serviceName, structs.ServiceKindTerminatingGateway, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed gateway nodes lookup: %v", err)
|
return 0, nil, fmt.Errorf("failed gateway nodes lookup: %v", err)
|
||||||
}
|
}
|
||||||
|
if idx < gwIdx {
|
||||||
|
idx = gwIdx
|
||||||
|
}
|
||||||
|
|
||||||
for _, ch := range chs {
|
for _, ch := range chs {
|
||||||
ws.Add(ch)
|
ws.Add(ch)
|
||||||
|
@ -1059,7 +1063,10 @@ func (s *Store) serviceNodes(ws memdb.WatchSet, serviceName string, connect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the table index.
|
// Get the table index.
|
||||||
idx := s.maxIndexForService(tx, serviceName, len(results) > 0, false, entMeta)
|
svcIdx := s.maxIndexForService(tx, serviceName, len(results) > 0, false, entMeta)
|
||||||
|
if idx < svcIdx {
|
||||||
|
idx = svcIdx
|
||||||
|
}
|
||||||
|
|
||||||
return idx, results, nil
|
return idx, results, nil
|
||||||
}
|
}
|
||||||
|
@ -2035,12 +2042,16 @@ func (s *Store) checkServiceNodesTxn(tx *memdb.Txn, ws memdb.WatchSet, serviceNa
|
||||||
// Gateways are tracked in a separate table, and we append them to the result set.
|
// Gateways are tracked in a separate table, and we append them to the result set.
|
||||||
// We append rather than replace since it allows users to migrate a service
|
// We append rather than replace since it allows users to migrate a service
|
||||||
// to the mesh with a mix of sidecars and gateways until all its instances have a sidecar.
|
// to the mesh with a mix of sidecars and gateways until all its instances have a sidecar.
|
||||||
|
var idx uint64
|
||||||
if connect {
|
if connect {
|
||||||
// Look up gateway nodes associated with the service
|
// Look up gateway nodes associated with the service
|
||||||
_, nodes, _, err := s.serviceGatewayNodes(tx, ws, serviceName, structs.ServiceKindTerminatingGateway, entMeta)
|
gwIdx, nodes, _, err := s.serviceGatewayNodes(tx, ws, serviceName, structs.ServiceKindTerminatingGateway, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed gateway nodes lookup: %v", err)
|
return 0, nil, fmt.Errorf("failed gateway nodes lookup: %v", err)
|
||||||
}
|
}
|
||||||
|
if idx < gwIdx {
|
||||||
|
idx = gwIdx
|
||||||
|
}
|
||||||
for i := 0; i < len(nodes); i++ {
|
for i := 0; i < len(nodes); i++ {
|
||||||
results = append(results, nodes[i])
|
results = append(results, nodes[i])
|
||||||
serviceNames[nodes[i].ServiceName] = struct{}{}
|
serviceNames[nodes[i].ServiceName] = struct{}{}
|
||||||
|
@ -2056,7 +2067,6 @@ func (s *Store) checkServiceNodesTxn(tx *memdb.Txn, ws memdb.WatchSet, serviceNa
|
||||||
// (~682 service instances). See
|
// (~682 service instances). See
|
||||||
// https://github.com/hashicorp/consul/issues/4984
|
// https://github.com/hashicorp/consul/issues/4984
|
||||||
watchOptimized := false
|
watchOptimized := false
|
||||||
idx := uint64(0)
|
|
||||||
if len(serviceNames) > 0 {
|
if len(serviceNames) > 0 {
|
||||||
// Assume optimization will work since it really should at this point. For
|
// Assume optimization will work since it really should at this point. For
|
||||||
// safety we'll sanity check this below for each service name.
|
// safety we'll sanity check this below for each service name.
|
||||||
|
@ -2527,6 +2537,7 @@ func (s *Store) terminatingConfigGatewayServices(tx *memdb.Txn, gateway structs.
|
||||||
KeyFile: svc.KeyFile,
|
KeyFile: svc.KeyFile,
|
||||||
CertFile: svc.CertFile,
|
CertFile: svc.CertFile,
|
||||||
CAFile: svc.CAFile,
|
CAFile: svc.CAFile,
|
||||||
|
SNI: svc.SNI,
|
||||||
}
|
}
|
||||||
|
|
||||||
gatewayServices = append(gatewayServices, mapping)
|
gatewayServices = append(gatewayServices, mapping)
|
||||||
|
@ -2684,10 +2695,22 @@ func (s *Store) serviceGatewayNodes(tx *memdb.Txn, ws memdb.WatchSet, service st
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, fmt.Errorf("failed service lookup: %s", err)
|
return 0, nil, nil, fmt.Errorf("failed service lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var exists bool
|
||||||
for svc := gwServices.Next(); svc != nil; svc = gwServices.Next() {
|
for svc := gwServices.Next(); svc != nil; svc = gwServices.Next() {
|
||||||
sn := svc.(*structs.ServiceNode)
|
sn := svc.(*structs.ServiceNode)
|
||||||
ret = append(ret, sn)
|
ret = append(ret, sn)
|
||||||
|
|
||||||
|
// Tracking existence to know whether we should check extinction index for service
|
||||||
|
exists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This prevents the index from sliding back in case all instances of the service are deregistered
|
||||||
|
svcIdx := s.maxIndexForService(tx, mapping.Gateway.ID, exists, false, &mapping.Service.EnterpriseMeta)
|
||||||
|
if maxIdx < svcIdx {
|
||||||
|
maxIdx = svcIdx
|
||||||
|
}
|
||||||
|
|
||||||
watchChans = append(watchChans, gwServices.WatchCh())
|
watchChans = append(watchChans, gwServices.WatchCh())
|
||||||
}
|
}
|
||||||
return maxIdx, ret, watchChans, nil
|
return maxIdx, ret, watchChans, nil
|
||||||
|
|
|
@ -2168,7 +2168,7 @@ func TestStateStore_ConnectServiceNodes_Gateways(t *testing.T) {
|
||||||
ws = memdb.NewWatchSet()
|
ws = memdb.NewWatchSet()
|
||||||
idx, nodes, err = s.ConnectServiceNodes(ws, "db", nil)
|
idx, nodes, err = s.ConnectServiceNodes(ws, "db", nil)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(idx, uint64(14))
|
assert.Equal(idx, uint64(17))
|
||||||
assert.Len(nodes, 2)
|
assert.Len(nodes, 2)
|
||||||
|
|
||||||
// Check sidecar
|
// Check sidecar
|
||||||
|
@ -2191,12 +2191,12 @@ func TestStateStore_ConnectServiceNodes_Gateways(t *testing.T) {
|
||||||
assert.True(watchFired(ws))
|
assert.True(watchFired(ws))
|
||||||
|
|
||||||
// Watch should fire when a gateway instance is de-registered
|
// Watch should fire when a gateway instance is de-registered
|
||||||
assert.Nil(s.DeleteService(29, "bar", "gateway", nil))
|
assert.Nil(s.DeleteService(19, "bar", "gateway", nil))
|
||||||
assert.True(watchFired(ws))
|
assert.True(watchFired(ws))
|
||||||
|
|
||||||
idx, nodes, err = s.ConnectServiceNodes(ws, "db", nil)
|
idx, nodes, err = s.ConnectServiceNodes(ws, "db", nil)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(idx, uint64(14))
|
assert.Equal(idx, uint64(19))
|
||||||
assert.Len(nodes, 2)
|
assert.Len(nodes, 2)
|
||||||
|
|
||||||
// Check the new gateway
|
// Check the new gateway
|
||||||
|
@ -2205,6 +2205,22 @@ func TestStateStore_ConnectServiceNodes_Gateways(t *testing.T) {
|
||||||
assert.Equal("gateway", nodes[1].ServiceName)
|
assert.Equal("gateway", nodes[1].ServiceName)
|
||||||
assert.Equal("gateway-2", nodes[1].ServiceID)
|
assert.Equal("gateway-2", nodes[1].ServiceID)
|
||||||
assert.Equal(443, nodes[1].ServicePort)
|
assert.Equal(443, nodes[1].ServicePort)
|
||||||
|
|
||||||
|
// Index should not slide back after deleting all instances of the gateway
|
||||||
|
assert.Nil(s.DeleteService(20, "foo", "gateway-2", nil))
|
||||||
|
assert.True(watchFired(ws))
|
||||||
|
|
||||||
|
idx, nodes, err = s.ConnectServiceNodes(ws, "db", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(idx, uint64(20))
|
||||||
|
assert.Len(nodes, 1)
|
||||||
|
|
||||||
|
// Ensure that remaining node is the proxy and not a gateway
|
||||||
|
assert.Equal(structs.ServiceKindConnectProxy, nodes[0].ServiceKind)
|
||||||
|
assert.Equal("foo", nodes[0].Node)
|
||||||
|
assert.Equal("proxy", nodes[0].ServiceName)
|
||||||
|
assert.Equal("proxy", nodes[0].ServiceID)
|
||||||
|
assert.Equal(8000, nodes[0].ServicePort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateStore_Service_Snapshot(t *testing.T) {
|
func TestStateStore_Service_Snapshot(t *testing.T) {
|
||||||
|
@ -3622,6 +3638,11 @@ func TestStateStore_CheckConnectServiceNodes_Gateways(t *testing.T) {
|
||||||
assert.Nil(s.EnsureService(22, "foo", &structs.NodeService{Kind: structs.ServiceKindTerminatingGateway, ID: "gateway-2", Service: "gateway", Port: 443}))
|
assert.Nil(s.EnsureService(22, "foo", &structs.NodeService{Kind: structs.ServiceKindTerminatingGateway, ID: "gateway-2", Service: "gateway", Port: 443}))
|
||||||
assert.True(watchFired(ws))
|
assert.True(watchFired(ws))
|
||||||
|
|
||||||
|
idx, nodes, err = s.CheckConnectServiceNodes(ws, "db", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(idx, uint64(22))
|
||||||
|
assert.Len(nodes, 3)
|
||||||
|
|
||||||
// Watch should fire when a gateway instance is de-registered
|
// Watch should fire when a gateway instance is de-registered
|
||||||
assert.Nil(s.DeleteService(23, "bar", "gateway", nil))
|
assert.Nil(s.DeleteService(23, "bar", "gateway", nil))
|
||||||
assert.True(watchFired(ws))
|
assert.True(watchFired(ws))
|
||||||
|
@ -3637,6 +3658,22 @@ func TestStateStore_CheckConnectServiceNodes_Gateways(t *testing.T) {
|
||||||
assert.Equal("gateway", nodes[1].Service.Service)
|
assert.Equal("gateway", nodes[1].Service.Service)
|
||||||
assert.Equal("gateway-2", nodes[1].Service.ID)
|
assert.Equal("gateway-2", nodes[1].Service.ID)
|
||||||
assert.Equal(443, nodes[1].Service.Port)
|
assert.Equal(443, nodes[1].Service.Port)
|
||||||
|
|
||||||
|
// Index should not slide back after deleting all instances of the gateway
|
||||||
|
assert.Nil(s.DeleteService(24, "foo", "gateway-2", nil))
|
||||||
|
assert.True(watchFired(ws))
|
||||||
|
|
||||||
|
idx, nodes, err = s.CheckConnectServiceNodes(ws, "db", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(idx, uint64(24))
|
||||||
|
assert.Len(nodes, 1)
|
||||||
|
|
||||||
|
// Ensure that remaining node is the proxy and not a gateway
|
||||||
|
assert.Equal(structs.ServiceKindConnectProxy, nodes[0].Service.Kind)
|
||||||
|
assert.Equal("foo", nodes[0].Node.Node)
|
||||||
|
assert.Equal("proxy", nodes[0].Service.Service)
|
||||||
|
assert.Equal("proxy", nodes[0].Service.ID)
|
||||||
|
assert.Equal(8000, nodes[0].Service.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCheckServiceNodes(b *testing.B) {
|
func BenchmarkCheckServiceNodes(b *testing.B) {
|
||||||
|
@ -4484,6 +4521,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "db",
|
Name: "db",
|
||||||
|
@ -4493,6 +4531,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "ca.crt",
|
CAFile: "ca.crt",
|
||||||
CertFile: "client.crt",
|
CertFile: "client.crt",
|
||||||
KeyFile: "client.key",
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil))
|
}, nil))
|
||||||
|
@ -4513,6 +4552,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
RaftIndex: structs.RaftIndex{
|
RaftIndex: structs.RaftIndex{
|
||||||
CreateIndex: 22,
|
CreateIndex: 22,
|
||||||
ModifyIndex: 22,
|
ModifyIndex: 22,
|
||||||
|
@ -4547,6 +4587,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
RaftIndex: structs.RaftIndex{
|
RaftIndex: structs.RaftIndex{
|
||||||
CreateIndex: 22,
|
CreateIndex: 22,
|
||||||
ModifyIndex: 22,
|
ModifyIndex: 22,
|
||||||
|
@ -4568,6 +4609,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "ca.crt",
|
CAFile: "ca.crt",
|
||||||
CertFile: "client.crt",
|
CertFile: "client.crt",
|
||||||
KeyFile: "client.key",
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
FromWildcard: true,
|
FromWildcard: true,
|
||||||
RaftIndex: structs.RaftIndex{
|
RaftIndex: structs.RaftIndex{
|
||||||
CreateIndex: 23,
|
CreateIndex: 23,
|
||||||
|
@ -4594,6 +4636,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) {
|
||||||
CAFile: "api/ca.crt",
|
CAFile: "api/ca.crt",
|
||||||
CertFile: "api/client.crt",
|
CertFile: "api/client.crt",
|
||||||
KeyFile: "api/client.key",
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
RaftIndex: structs.RaftIndex{
|
RaftIndex: structs.RaftIndex{
|
||||||
CreateIndex: 22,
|
CreateIndex: 22,
|
||||||
ModifyIndex: 22,
|
ModifyIndex: 22,
|
||||||
|
|
|
@ -93,6 +93,11 @@ type configSnapshotTerminatingGateway struct {
|
||||||
// ServiceGroups is a map of service id to the service instances of that
|
// ServiceGroups is a map of service id to the service instances of that
|
||||||
// service in the local datacenter.
|
// service in the local datacenter.
|
||||||
ServiceGroups map[structs.ServiceID]structs.CheckServiceNodes
|
ServiceGroups map[structs.ServiceID]structs.CheckServiceNodes
|
||||||
|
|
||||||
|
// GatewayServices is a map of service id to the config entry association
|
||||||
|
// between the gateway and a service. TLS configuration stored here is
|
||||||
|
// used for TLS origination from the gateway to the linked service.
|
||||||
|
GatewayServices map[structs.ServiceID]structs.GatewayService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *configSnapshotTerminatingGateway) IsEmpty() bool {
|
func (c *configSnapshotTerminatingGateway) IsEmpty() bool {
|
||||||
|
@ -105,7 +110,8 @@ func (c *configSnapshotTerminatingGateway) IsEmpty() bool {
|
||||||
len(c.ServiceGroups) == 0 &&
|
len(c.ServiceGroups) == 0 &&
|
||||||
len(c.WatchedServices) == 0 &&
|
len(c.WatchedServices) == 0 &&
|
||||||
len(c.ServiceResolvers) == 0 &&
|
len(c.ServiceResolvers) == 0 &&
|
||||||
len(c.WatchedResolvers) == 0
|
len(c.WatchedResolvers) == 0 &&
|
||||||
|
len(c.GatewayServices) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type configSnapshotMeshGateway struct {
|
type configSnapshotMeshGateway struct {
|
||||||
|
|
|
@ -543,6 +543,7 @@ func (s *state) initialConfigSnapshot() ConfigSnapshot {
|
||||||
snap.TerminatingGateway.ServiceLeaves = make(map[structs.ServiceID]*structs.IssuedCert)
|
snap.TerminatingGateway.ServiceLeaves = make(map[structs.ServiceID]*structs.IssuedCert)
|
||||||
snap.TerminatingGateway.ServiceGroups = make(map[structs.ServiceID]structs.CheckServiceNodes)
|
snap.TerminatingGateway.ServiceGroups = make(map[structs.ServiceID]structs.CheckServiceNodes)
|
||||||
snap.TerminatingGateway.ServiceResolvers = make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry)
|
snap.TerminatingGateway.ServiceResolvers = make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry)
|
||||||
|
snap.TerminatingGateway.GatewayServices = make(map[structs.ServiceID]structs.GatewayService)
|
||||||
case structs.ServiceKindMeshGateway:
|
case structs.ServiceKindMeshGateway:
|
||||||
snap.MeshGateway.WatchedServices = make(map[structs.ServiceID]context.CancelFunc)
|
snap.MeshGateway.WatchedServices = make(map[structs.ServiceID]context.CancelFunc)
|
||||||
snap.MeshGateway.WatchedDatacenters = make(map[string]context.CancelFunc)
|
snap.MeshGateway.WatchedDatacenters = make(map[string]context.CancelFunc)
|
||||||
|
@ -914,6 +915,9 @@ func (s *state) handleUpdateTerminatingGateway(u cache.UpdateEvent, snap *Config
|
||||||
// Make sure to add every service to this map, we use it to cancel watches below.
|
// Make sure to add every service to this map, we use it to cancel watches below.
|
||||||
svcMap[svc.Service] = struct{}{}
|
svcMap[svc.Service] = struct{}{}
|
||||||
|
|
||||||
|
// Store the gateway <-> service mapping for TLS origination
|
||||||
|
snap.TerminatingGateway.GatewayServices[svc.Service] = *svc
|
||||||
|
|
||||||
// Watch the health endpoint to discover endpoints for the service
|
// Watch the health endpoint to discover endpoints for the service
|
||||||
if _, ok := snap.TerminatingGateway.WatchedServices[svc.Service]; !ok {
|
if _, ok := snap.TerminatingGateway.WatchedServices[svc.Service]; !ok {
|
||||||
ctx, cancel := context.WithCancel(s.ctx)
|
ctx, cancel := context.WithCancel(s.ctx)
|
||||||
|
@ -1013,6 +1017,13 @@ func (s *state) handleUpdateTerminatingGateway(u cache.UpdateEvent, snap *Config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete gateway service mapping for services that were not in the update
|
||||||
|
for sid, _ := range snap.TerminatingGateway.GatewayServices {
|
||||||
|
if _, ok := svcMap[sid]; !ok {
|
||||||
|
delete(snap.TerminatingGateway.GatewayServices, sid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel service instance watches for services that were not in the update
|
// Cancel service instance watches for services that were not in the update
|
||||||
for sid, cancelFn := range snap.TerminatingGateway.WatchedServices {
|
for sid, cancelFn := range snap.TerminatingGateway.WatchedServices {
|
||||||
if _, ok := svcMap[sid]; !ok {
|
if _, ok := svcMap[sid]; !ok {
|
||||||
|
|
|
@ -921,6 +921,10 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Len(t, snap.TerminatingGateway.WatchedResolvers, 2)
|
require.Len(t, snap.TerminatingGateway.WatchedResolvers, 2)
|
||||||
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, db)
|
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, db)
|
||||||
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)
|
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)
|
||||||
|
|
||||||
|
require.Len(t, snap.TerminatingGateway.GatewayServices, 2)
|
||||||
|
require.Contains(t, snap.TerminatingGateway.GatewayServices, db)
|
||||||
|
require.Contains(t, snap.TerminatingGateway.GatewayServices, billing)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
verificationStage{
|
verificationStage{
|
||||||
|
@ -1048,6 +1052,9 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Len(t, snap.TerminatingGateway.WatchedResolvers, 1)
|
require.Len(t, snap.TerminatingGateway.WatchedResolvers, 1)
|
||||||
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)
|
require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)
|
||||||
|
|
||||||
|
require.Len(t, snap.TerminatingGateway.GatewayServices, 1)
|
||||||
|
require.Contains(t, snap.TerminatingGateway.GatewayServices, billing)
|
||||||
|
|
||||||
// There was no update event for billing's leaf/endpoints, so length is 0
|
// There was no update event for billing's leaf/endpoints, so length is 0
|
||||||
require.Len(t, snap.TerminatingGateway.ServiceGroups, 0)
|
require.Len(t, snap.TerminatingGateway.ServiceGroups, 0)
|
||||||
require.Len(t, snap.TerminatingGateway.ServiceLeaves, 0)
|
require.Len(t, snap.TerminatingGateway.ServiceLeaves, 0)
|
||||||
|
|
|
@ -1496,6 +1496,18 @@ func testConfigSnapshotTerminatingGateway(t testing.T, populateServices bool) *C
|
||||||
web: webNodes,
|
web: webNodes,
|
||||||
api: apiNodes,
|
api: apiNodes,
|
||||||
},
|
},
|
||||||
|
GatewayServices: map[structs.ServiceID]structs.GatewayService{
|
||||||
|
web: {
|
||||||
|
Service: web,
|
||||||
|
CAFile: "ca.cert.pem",
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
Service: api,
|
||||||
|
CAFile: "ca.cert.pem",
|
||||||
|
CertFile: "api.cert.pem",
|
||||||
|
KeyFile: "api.key.pem",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
snap.TerminatingGateway.ServiceLeaves = map[structs.ServiceID]*structs.IssuedCert{
|
snap.TerminatingGateway.ServiceLeaves = map[structs.ServiceID]*structs.IssuedCert{
|
||||||
structs.NewServiceID("web", nil): {
|
structs.NewServiceID("web", nil): {
|
||||||
|
|
|
@ -200,6 +200,9 @@ type LinkedService struct {
|
||||||
// from the gateway to the linked service
|
// from the gateway to the linked service
|
||||||
KeyFile string `json:",omitempty"`
|
KeyFile string `json:",omitempty"`
|
||||||
|
|
||||||
|
// SNI is the optional name to specify during the TLS handshake with a linked service
|
||||||
|
SNI string `json:",omitempty"`
|
||||||
|
|
||||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,8 +253,9 @@ func (e *TerminatingGatewayConfigEntry) Validate() error {
|
||||||
}
|
}
|
||||||
seen[cid] = true
|
seen[cid] = true
|
||||||
|
|
||||||
// If any TLS config flag was specified, all must be
|
// If either client cert config file was specified then the CA file, client cert, and key file must be specified
|
||||||
if (svc.CAFile != "" || svc.CertFile != "" || svc.KeyFile != "") &&
|
// Specifying only a CAFile is allowed for one-way TLS
|
||||||
|
if (svc.CertFile != "" || svc.KeyFile != "") &&
|
||||||
!(svc.CAFile != "" && svc.CertFile != "" && svc.KeyFile != "") {
|
!(svc.CAFile != "" && svc.CertFile != "" && svc.KeyFile != "") {
|
||||||
|
|
||||||
return fmt.Errorf("Service %q must have a CertFile, CAFile, and KeyFile specified for TLS origination", svc.Name)
|
return fmt.Errorf("Service %q must have a CertFile, CAFile, and KeyFile specified for TLS origination", svc.Name)
|
||||||
|
@ -299,6 +303,7 @@ type GatewayService struct {
|
||||||
CAFile string
|
CAFile string
|
||||||
CertFile string
|
CertFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
|
SNI string
|
||||||
FromWildcard bool
|
FromWildcard bool
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
@ -312,18 +317,22 @@ func (g *GatewayService) IsSame(o *GatewayService) bool {
|
||||||
g.Port == o.Port &&
|
g.Port == o.Port &&
|
||||||
g.CAFile == o.CAFile &&
|
g.CAFile == o.CAFile &&
|
||||||
g.CertFile == o.CertFile &&
|
g.CertFile == o.CertFile &&
|
||||||
g.KeyFile == o.KeyFile
|
g.KeyFile == o.KeyFile &&
|
||||||
|
g.SNI == o.SNI &&
|
||||||
|
g.FromWildcard == o.FromWildcard
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GatewayService) Clone() *GatewayService {
|
func (g *GatewayService) Clone() *GatewayService {
|
||||||
return &GatewayService{
|
return &GatewayService{
|
||||||
Gateway: g.Gateway,
|
Gateway: g.Gateway,
|
||||||
Service: g.Service,
|
Service: g.Service,
|
||||||
GatewayKind: g.GatewayKind,
|
GatewayKind: g.GatewayKind,
|
||||||
Port: g.Port,
|
Port: g.Port,
|
||||||
CAFile: g.CAFile,
|
CAFile: g.CAFile,
|
||||||
CertFile: g.CertFile,
|
CertFile: g.CertFile,
|
||||||
KeyFile: g.KeyFile,
|
KeyFile: g.KeyFile,
|
||||||
RaftIndex: g.RaftIndex,
|
SNI: g.SNI,
|
||||||
|
FromWildcard: g.FromWildcard,
|
||||||
|
RaftIndex: g.RaftIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,8 +315,8 @@ func TestTerminatingConfigEntry_Validate(t *testing.T) {
|
||||||
Name: "terminating-gw-west",
|
Name: "terminating-gw-west",
|
||||||
Services: []LinkedService{
|
Services: []LinkedService{
|
||||||
{
|
{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
CAFile: "ca.crt",
|
CertFile: "client.crt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -324,20 +324,6 @@ func TestTerminatingConfigEntry_Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "not all TLS options provided-2",
|
name: "not all TLS options provided-2",
|
||||||
entry: TerminatingGatewayConfigEntry{
|
|
||||||
Kind: "terminating-gateway",
|
|
||||||
Name: "terminating-gw-west",
|
|
||||||
Services: []LinkedService{
|
|
||||||
{
|
|
||||||
Name: "web",
|
|
||||||
CertFile: "client.crt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not all TLS options provided-3",
|
|
||||||
entry: TerminatingGatewayConfigEntry{
|
entry: TerminatingGatewayConfigEntry{
|
||||||
Kind: "terminating-gateway",
|
Kind: "terminating-gateway",
|
||||||
Name: "terminating-gw-west",
|
Name: "terminating-gw-west",
|
||||||
|
@ -350,51 +336,6 @@ func TestTerminatingConfigEntry_Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "not all TLS options provided-4",
|
|
||||||
entry: TerminatingGatewayConfigEntry{
|
|
||||||
Kind: "terminating-gateway",
|
|
||||||
Name: "terminating-gw-west",
|
|
||||||
Services: []LinkedService{
|
|
||||||
{
|
|
||||||
Name: "web",
|
|
||||||
CAFile: "ca.crt",
|
|
||||||
KeyFile: "tls.key",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not all TLS options provided-5",
|
|
||||||
entry: TerminatingGatewayConfigEntry{
|
|
||||||
Kind: "terminating-gateway",
|
|
||||||
Name: "terminating-gw-west",
|
|
||||||
Services: []LinkedService{
|
|
||||||
{
|
|
||||||
Name: "web",
|
|
||||||
CAFile: "ca.crt",
|
|
||||||
CertFile: "client.crt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not all TLS options provided-6",
|
|
||||||
entry: TerminatingGatewayConfigEntry{
|
|
||||||
Kind: "terminating-gateway",
|
|
||||||
Name: "terminating-gw-west",
|
|
||||||
Services: []LinkedService{
|
|
||||||
{
|
|
||||||
Name: "web",
|
|
||||||
KeyFile: "tls.key",
|
|
||||||
CertFile: "client.crt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "all TLS options provided",
|
name: "all TLS options provided",
|
||||||
entry: TerminatingGatewayConfigEntry{
|
entry: TerminatingGatewayConfigEntry{
|
||||||
|
@ -410,6 +351,19 @@ func TestTerminatingConfigEntry_Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "only providing ca file is allowed",
|
||||||
|
entry: TerminatingGatewayConfigEntry{
|
||||||
|
Kind: "terminating-gateway",
|
||||||
|
Name: "terminating-gw-west",
|
||||||
|
Services: []LinkedService{
|
||||||
|
{
|
||||||
|
Name: "web",
|
||||||
|
CAFile: "ca.crt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
|
|
|
@ -658,12 +658,14 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
ca_file = "/etc/payments/ca.pem",
|
ca_file = "/etc/payments/ca.pem",
|
||||||
cert_file = "/etc/payments/cert.pem",
|
cert_file = "/etc/payments/cert.pem",
|
||||||
key_file = "/etc/payments/tls.key",
|
key_file = "/etc/payments/tls.key",
|
||||||
|
sni = "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "*",
|
name = "*",
|
||||||
ca_file = "/etc/all/ca.pem",
|
ca_file = "/etc/all/ca.pem",
|
||||||
cert_file = "/etc/all/cert.pem",
|
cert_file = "/etc/all/cert.pem",
|
||||||
key_file = "/etc/all/tls.key",
|
key_file = "/etc/all/tls.key",
|
||||||
|
sni = "my-alt-domain",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`,
|
`,
|
||||||
|
@ -676,12 +678,14 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
CAFile = "/etc/payments/ca.pem",
|
CAFile = "/etc/payments/ca.pem",
|
||||||
CertFile = "/etc/payments/cert.pem",
|
CertFile = "/etc/payments/cert.pem",
|
||||||
KeyFile = "/etc/payments/tls.key",
|
KeyFile = "/etc/payments/tls.key",
|
||||||
|
SNI = "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name = "*",
|
Name = "*",
|
||||||
CAFile = "/etc/all/ca.pem",
|
CAFile = "/etc/all/ca.pem",
|
||||||
CertFile = "/etc/all/cert.pem",
|
CertFile = "/etc/all/cert.pem",
|
||||||
KeyFile = "/etc/all/tls.key",
|
KeyFile = "/etc/all/tls.key",
|
||||||
|
SNI = "my-alt-domain",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`,
|
`,
|
||||||
|
@ -694,12 +698,14 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
CAFile: "/etc/payments/ca.pem",
|
CAFile: "/etc/payments/ca.pem",
|
||||||
CertFile: "/etc/payments/cert.pem",
|
CertFile: "/etc/payments/cert.pem",
|
||||||
KeyFile: "/etc/payments/tls.key",
|
KeyFile: "/etc/payments/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "*",
|
Name: "*",
|
||||||
CAFile: "/etc/all/ca.pem",
|
CAFile: "/etc/all/ca.pem",
|
||||||
CertFile: "/etc/all/cert.pem",
|
CertFile: "/etc/all/cert.pem",
|
||||||
KeyFile: "/etc/all/tls.key",
|
KeyFile: "/etc/all/tls.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -2139,22 +2139,28 @@ func TestGatewayService_IsSame(t *testing.T) {
|
||||||
ca := "ca.pem"
|
ca := "ca.pem"
|
||||||
cert := "client.pem"
|
cert := "client.pem"
|
||||||
key := "tls.key"
|
key := "tls.key"
|
||||||
|
sni := "mydomain"
|
||||||
|
wildcard := false
|
||||||
|
|
||||||
g := &GatewayService{
|
g := &GatewayService{
|
||||||
Gateway: gateway,
|
Gateway: gateway,
|
||||||
Service: svc,
|
Service: svc,
|
||||||
GatewayKind: kind,
|
GatewayKind: kind,
|
||||||
CAFile: ca,
|
CAFile: ca,
|
||||||
CertFile: cert,
|
CertFile: cert,
|
||||||
KeyFile: key,
|
KeyFile: key,
|
||||||
|
SNI: sni,
|
||||||
|
FromWildcard: wildcard,
|
||||||
}
|
}
|
||||||
other := &GatewayService{
|
other := &GatewayService{
|
||||||
Gateway: gateway,
|
Gateway: gateway,
|
||||||
Service: svc,
|
Service: svc,
|
||||||
GatewayKind: kind,
|
GatewayKind: kind,
|
||||||
CAFile: ca,
|
CAFile: ca,
|
||||||
CertFile: cert,
|
CertFile: cert,
|
||||||
KeyFile: key,
|
KeyFile: key,
|
||||||
|
SNI: sni,
|
||||||
|
FromWildcard: wildcard,
|
||||||
}
|
}
|
||||||
check := func(twiddle, restore func()) {
|
check := func(twiddle, restore func()) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
@ -2178,6 +2184,8 @@ func TestGatewayService_IsSame(t *testing.T) {
|
||||||
check(func() { other.CAFile = "/certs/cert.pem" }, func() { other.CAFile = ca })
|
check(func() { other.CAFile = "/certs/cert.pem" }, func() { other.CAFile = ca })
|
||||||
check(func() { other.CertFile = "/certs/cert.pem" }, func() { other.CertFile = cert })
|
check(func() { other.CertFile = "/certs/cert.pem" }, func() { other.CertFile = cert })
|
||||||
check(func() { other.KeyFile = "/certs/cert.pem" }, func() { other.KeyFile = key })
|
check(func() { other.KeyFile = "/certs/cert.pem" }, func() { other.KeyFile = key })
|
||||||
|
check(func() { other.SNI = "alt-domain" }, func() { other.SNI = sni })
|
||||||
|
check(func() { other.FromWildcard = true }, func() { other.FromWildcard = wildcard })
|
||||||
|
|
||||||
if !g.IsSame(other) {
|
if !g.IsSame(other) {
|
||||||
t.Fatalf("should be equal, was %#v VS %#v", g, other)
|
t.Fatalf("should be equal, was %#v VS %#v", g, other)
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (s *Server) clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, _ string
|
||||||
case structs.ServiceKindConnectProxy:
|
case structs.ServiceKindConnectProxy:
|
||||||
return s.clustersFromSnapshotConnectProxy(cfgSnap)
|
return s.clustersFromSnapshotConnectProxy(cfgSnap)
|
||||||
case structs.ServiceKindTerminatingGateway:
|
case structs.ServiceKindTerminatingGateway:
|
||||||
return s.clustersFromSnapshotTerminatingGateway(cfgSnap)
|
return s.makeGatewayServiceClusters(cfgSnap)
|
||||||
case structs.ServiceKindMeshGateway:
|
case structs.ServiceKindMeshGateway:
|
||||||
return s.clustersFromSnapshotMeshGateway(cfgSnap)
|
return s.clustersFromSnapshotMeshGateway(cfgSnap)
|
||||||
case structs.ServiceKindIngressGateway:
|
case structs.ServiceKindIngressGateway:
|
||||||
|
@ -119,12 +119,6 @@ func makeExposeClusterName(destinationPort int) string {
|
||||||
return fmt.Sprintf("exposed_cluster_%d", destinationPort)
|
return fmt.Sprintf("exposed_cluster_%d", destinationPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clustersFromSnapshotTerminatingGateway returns the xDS API representation of the "clusters"
|
|
||||||
// for a terminating gateway. This will include 1 cluster per service and service subset.
|
|
||||||
func (s *Server) clustersFromSnapshotTerminatingGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
|
||||||
return s.clustersFromServicesAndResolvers(cfgSnap, cfgSnap.TerminatingGateway.ServiceGroups, cfgSnap.TerminatingGateway.ServiceResolvers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clustersFromSnapshotMeshGateway returns the xDS API representation of the "clusters"
|
// clustersFromSnapshotMeshGateway returns the xDS API representation of the "clusters"
|
||||||
// for a mesh gateway. This will include 1 cluster per remote datacenter as well as
|
// for a mesh gateway. This will include 1 cluster per remote datacenter as well as
|
||||||
// 1 cluster for each service subset.
|
// 1 cluster for each service subset.
|
||||||
|
@ -141,7 +135,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
}
|
}
|
||||||
clusterName := connect.DatacenterSNI(dc, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.DatacenterSNI(dc, cfgSnap.Roots.TrustDomain)
|
||||||
|
|
||||||
cluster, err := s.makeGatewayCluster(clusterName, cfgSnap)
|
cluster, err := s.makeGatewayCluster(cfgSnap, clusterName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -153,7 +147,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
for _, dc := range datacenters {
|
for _, dc := range datacenters {
|
||||||
clusterName := cfgSnap.ServerSNIFn(dc, "")
|
clusterName := cfgSnap.ServerSNIFn(dc, "")
|
||||||
|
|
||||||
cluster, err := s.makeGatewayCluster(clusterName, cfgSnap)
|
cluster, err := s.makeGatewayCluster(cfgSnap, clusterName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -164,7 +158,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
for _, srv := range cfgSnap.MeshGateway.ConsulServers {
|
for _, srv := range cfgSnap.MeshGateway.ConsulServers {
|
||||||
clusterName := cfgSnap.ServerSNIFn(cfgSnap.Datacenter, srv.Node.Node)
|
clusterName := cfgSnap.ServerSNIFn(cfgSnap.Datacenter, srv.Node.Node)
|
||||||
|
|
||||||
cluster, err := s.makeGatewayCluster(clusterName, cfgSnap)
|
cluster, err := s.makeGatewayCluster(cfgSnap, clusterName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -173,7 +167,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the per-service/subset clusters
|
// generate the per-service/subset clusters
|
||||||
c, err := s.clustersFromServicesAndResolvers(cfgSnap, cfgSnap.MeshGateway.ServiceGroups, cfgSnap.MeshGateway.ServiceResolvers)
|
c, err := s.makeGatewayServiceClusters(cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -182,10 +176,20 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
return clusters, nil
|
return clusters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) clustersFromServicesAndResolvers(
|
func (s *Server) makeGatewayServiceClusters(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
||||||
cfgSnap *proxycfg.ConfigSnapshot,
|
var services map[structs.ServiceID]structs.CheckServiceNodes
|
||||||
services map[structs.ServiceID]structs.CheckServiceNodes,
|
var resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
|
||||||
resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry) ([]proto.Message, error) {
|
|
||||||
|
switch cfgSnap.Kind {
|
||||||
|
case structs.ServiceKindTerminatingGateway:
|
||||||
|
services = cfgSnap.TerminatingGateway.ServiceGroups
|
||||||
|
resolvers = cfgSnap.TerminatingGateway.ServiceResolvers
|
||||||
|
case structs.ServiceKindMeshGateway:
|
||||||
|
services = cfgSnap.MeshGateway.ServiceGroups
|
||||||
|
resolvers = cfgSnap.MeshGateway.ServiceResolvers
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported gateway kind %q", cfgSnap.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
clusters := make([]proto.Message, 0, len(services))
|
clusters := make([]proto.Message, 0, len(services))
|
||||||
|
|
||||||
|
@ -196,28 +200,34 @@ func (s *Server) clustersFromServicesAndResolvers(
|
||||||
// Create the cluster for default/unnamed services
|
// Create the cluster for default/unnamed services
|
||||||
var cluster *envoy.Cluster
|
var cluster *envoy.Cluster
|
||||||
var err error
|
var err error
|
||||||
if hasResolver {
|
|
||||||
cluster, err = s.makeGatewayClusterWithConnectTimeout(clusterName, cfgSnap, resolver.ConnectTimeout)
|
if !hasResolver {
|
||||||
} else {
|
// Use a zero value resolver with no timeout and no subsets
|
||||||
cluster, err = s.makeGatewayCluster(clusterName, cfgSnap)
|
resolver = &structs.ServiceResolverConfigEntry{}
|
||||||
}
|
}
|
||||||
|
cluster, err = s.makeGatewayClusterWithConnectTimeout(cfgSnap, clusterName, resolver.ConnectTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make %s cluster: %v", cfgSnap.Kind, err)
|
return nil, fmt.Errorf("failed to make %s cluster: %v", cfgSnap.Kind, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfgSnap.Kind == structs.ServiceKindTerminatingGateway {
|
||||||
|
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
|
||||||
|
}
|
||||||
clusters = append(clusters, cluster)
|
clusters = append(clusters, cluster)
|
||||||
|
|
||||||
// if there is a service-resolver for this service then also setup subset clusters for it
|
// If there is a service-resolver for this service then also setup a cluster for each subset
|
||||||
if hasResolver {
|
for subsetName := range resolver.Subsets {
|
||||||
// generate 1 cluster for each service subset
|
clusterName := connect.ServiceSNI(svc.ID, subsetName, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
||||||
for subsetName := range resolver.Subsets {
|
|
||||||
clusterName := connect.ServiceSNI(svc.ID, subsetName, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
|
||||||
|
|
||||||
cluster, err := s.makeGatewayClusterWithConnectTimeout(clusterName, cfgSnap, resolver.ConnectTimeout)
|
cluster, err := s.makeGatewayClusterWithConnectTimeout(cfgSnap, clusterName, resolver.ConnectTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make %s cluster: %v", cfgSnap.Kind, err)
|
return nil, fmt.Errorf("failed to make %s cluster: %v", cfgSnap.Kind, err)
|
||||||
}
|
|
||||||
clusters = append(clusters, cluster)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfgSnap.Kind == structs.ServiceKindTerminatingGateway {
|
||||||
|
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
|
||||||
|
}
|
||||||
|
clusters = append(clusters, cluster)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +359,7 @@ func (s *Server) makeUpstreamClusterForPreparedQuery(upstream structs.Upstream,
|
||||||
|
|
||||||
// Enable TLS upstream with the configured client certificate.
|
// Enable TLS upstream with the configured client certificate.
|
||||||
c.TlsContext = &envoyauth.UpstreamTlsContext{
|
c.TlsContext = &envoyauth.UpstreamTlsContext{
|
||||||
CommonTlsContext: makeCommonTLSContext(cfgSnap, cfgSnap.Leaf()),
|
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
|
||||||
Sni: sni,
|
Sni: sni,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +470,7 @@ func (s *Server) makeUpstreamClustersForDiscoveryChain(
|
||||||
|
|
||||||
// Enable TLS upstream with the configured client certificate.
|
// Enable TLS upstream with the configured client certificate.
|
||||||
c.TlsContext = &envoyauth.UpstreamTlsContext{
|
c.TlsContext = &envoyauth.UpstreamTlsContext{
|
||||||
CommonTlsContext: makeCommonTLSContext(cfgSnap, cfgSnap.Leaf()),
|
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
|
||||||
Sni: sni,
|
Sni: sni,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,15 +538,16 @@ func makeClusterFromUserConfig(configJSON string) (*envoy.Cluster, error) {
|
||||||
return &c, err
|
return &c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) makeGatewayCluster(clusterName string, cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
func (s *Server) makeGatewayCluster(cfgSnap *proxycfg.ConfigSnapshot, clusterName string) (*envoy.Cluster, error) {
|
||||||
return s.makeGatewayClusterWithConnectTimeout(clusterName, cfgSnap, 0)
|
return s.makeGatewayClusterWithConnectTimeout(cfgSnap, clusterName, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeGatewayClusterWithConnectTimeout initializes a gateway cluster
|
// makeGatewayClusterWithConnectTimeout initializes a gateway cluster
|
||||||
// with the specified connect timeout. If the timeout is 0, the connect timeout
|
// with the specified connect timeout. If the timeout is 0, the connect timeout
|
||||||
// defaults to use the configured gateway timeout.
|
// defaults to use the configured gateway timeout.
|
||||||
func (s *Server) makeGatewayClusterWithConnectTimeout(clusterName string, cfgSnap *proxycfg.ConfigSnapshot,
|
func (s *Server) makeGatewayClusterWithConnectTimeout(cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
connectTimeout time.Duration) (*envoy.Cluster, error) {
|
clusterName string, connectTimeout time.Duration) (*envoy.Cluster, error) {
|
||||||
|
|
||||||
cfg, err := ParseGatewayConfig(cfgSnap.Proxy.Config)
|
cfg, err := ParseGatewayConfig(cfgSnap.Proxy.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't hard fail on a config typo, just warn. The parse func returns
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
|
@ -548,7 +559,7 @@ func (s *Server) makeGatewayClusterWithConnectTimeout(clusterName string, cfgSna
|
||||||
connectTimeout = time.Duration(cfg.ConnectTimeoutMs) * time.Millisecond
|
connectTimeout = time.Duration(cfg.ConnectTimeoutMs) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
return &envoy.Cluster{
|
cluster := envoy.Cluster{
|
||||||
Name: clusterName,
|
Name: clusterName,
|
||||||
ConnectTimeout: connectTimeout,
|
ConnectTimeout: connectTimeout,
|
||||||
ClusterDiscoveryType: &envoy.Cluster_Type{Type: envoy.Cluster_EDS},
|
ClusterDiscoveryType: &envoy.Cluster_Type{Type: envoy.Cluster_EDS},
|
||||||
|
@ -561,7 +572,21 @@ func (s *Server) makeGatewayClusterWithConnectTimeout(clusterName string, cfgSna
|
||||||
},
|
},
|
||||||
// Having an empty config enables outlier detection with default config.
|
// Having an empty config enables outlier detection with default config.
|
||||||
OutlierDetection: &envoycluster.OutlierDetection{},
|
OutlierDetection: &envoycluster.OutlierDetection{},
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return &cluster, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// injectTerminatingGatewayTLSContext adds an UpstreamTlsContext to a cluster for TLS origination
|
||||||
|
func injectTerminatingGatewayTLSContext(cfgSnap *proxycfg.ConfigSnapshot, cluster *envoy.Cluster, service structs.ServiceID) {
|
||||||
|
if mapping, ok := cfgSnap.TerminatingGateway.GatewayServices[service]; ok && mapping.CAFile != "" {
|
||||||
|
cluster.TlsContext = &envoyauth.UpstreamTlsContext{
|
||||||
|
CommonTlsContext: makeCommonTLSContextFromFiles(mapping.CAFile, mapping.CertFile, mapping.KeyFile),
|
||||||
|
|
||||||
|
// TODO (gateways) (freddy) If mapping.SNI is empty, does Envoy behave any differently if TlsContext.Sni is excluded?
|
||||||
|
Sni: mapping.SNI,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeThresholdsIfNeeded(limits UpstreamLimits) []*envoycluster.CircuitBreakers_Thresholds {
|
func makeThresholdsIfNeeded(limits UpstreamLimits) []*envoycluster.CircuitBreakers_Thresholds {
|
||||||
|
|
|
@ -367,7 +367,7 @@ func makeListenerFromUserConfig(configJSON string) (*envoy.Listener, error) {
|
||||||
// specify custom listener params in config but still get our certs delivered
|
// specify custom listener params in config but still get our certs delivered
|
||||||
// dynamically and intentions enforced without coming up with some complicated
|
// dynamically and intentions enforced without coming up with some complicated
|
||||||
// templating/merging solution.
|
// templating/merging solution.
|
||||||
func injectConnectFilters(cfgSnap *proxycfg.ConfigSnapshot, token string, listener *envoy.Listener) error {
|
func injectConnectFilters(cfgSnap *proxycfg.ConfigSnapshot, token string, listener *envoy.Listener, setTLS bool) error {
|
||||||
authFilter, err := makeExtAuthFilter(token)
|
authFilter, err := makeExtAuthFilter(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -378,7 +378,7 @@ func injectConnectFilters(cfgSnap *proxycfg.ConfigSnapshot, token string, listen
|
||||||
append([]envoylistener.Filter{authFilter}, listener.FilterChains[idx].Filters...)
|
append([]envoylistener.Filter{authFilter}, listener.FilterChains[idx].Filters...)
|
||||||
|
|
||||||
listener.FilterChains[idx].TlsContext = &envoyauth.DownstreamTlsContext{
|
listener.FilterChains[idx].TlsContext = &envoyauth.DownstreamTlsContext{
|
||||||
CommonTlsContext: makeCommonTLSContext(cfgSnap, cfgSnap.Leaf()),
|
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
|
||||||
RequireClientCertificate: &types.BoolValue{Value: true},
|
RequireClientCertificate: &types.BoolValue{Value: true},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,7 +439,7 @@ func (s *Server) makePublicListener(cfgSnap *proxycfg.ConfigSnapshot, token stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = injectConnectFilters(cfgSnap, token, l)
|
err = injectConnectFilters(cfgSnap, token, l, true)
|
||||||
return l, err
|
return l, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,7 +642,7 @@ func (s *Server) sniFilterChainTerminatingGateway(listener, cluster, token strin
|
||||||
tcpProxy,
|
tcpProxy,
|
||||||
},
|
},
|
||||||
TlsContext: &envoyauth.DownstreamTlsContext{
|
TlsContext: &envoyauth.DownstreamTlsContext{
|
||||||
CommonTlsContext: makeCommonTLSContext(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]),
|
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]),
|
||||||
RequireClientCertificate: &types.BoolValue{Value: true},
|
RequireClientCertificate: &types.BoolValue{Value: true},
|
||||||
},
|
},
|
||||||
}, err
|
}, err
|
||||||
|
@ -1011,7 +1011,7 @@ func makeFilter(name string, cfg proto.Message) (envoylistener.Filter, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCommonTLSContext(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoyauth.CommonTlsContext {
|
func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoyauth.CommonTlsContext {
|
||||||
// Concatenate all the root PEMs into one.
|
// Concatenate all the root PEMs into one.
|
||||||
// TODO(banks): verify this actually works with Envoy (docs are not clear).
|
// TODO(banks): verify this actually works with Envoy (docs are not clear).
|
||||||
rootPEMS := ""
|
rootPEMS := ""
|
||||||
|
@ -1050,3 +1050,42 @@ func makeCommonTLSContext(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.Issued
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeCommonTLSContextFromFiles(caFile, certFile, keyFile string) *envoyauth.CommonTlsContext {
|
||||||
|
ctx := envoyauth.CommonTlsContext{
|
||||||
|
TlsParams: &envoyauth.TlsParameters{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify certificate of peer if caFile is specified
|
||||||
|
if caFile != "" {
|
||||||
|
ctx.ValidationContextType = &envoyauth.CommonTlsContext_ValidationContext{
|
||||||
|
ValidationContext: &envoyauth.CertificateValidationContext{
|
||||||
|
TrustedCa: &envoycore.DataSource{
|
||||||
|
Specifier: &envoycore.DataSource_Filename{
|
||||||
|
Filename: caFile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present certificate for mTLS if cert and key files are specified
|
||||||
|
if certFile != "" && keyFile != "" {
|
||||||
|
ctx.TlsCertificates = []*envoyauth.TlsCertificate{
|
||||||
|
{
|
||||||
|
CertificateChain: &envoycore.DataSource{
|
||||||
|
Specifier: &envoycore.DataSource_Filename{
|
||||||
|
Filename: certFile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PrivateKey: &envoycore.DataSource{
|
||||||
|
Specifier: &envoycore.DataSource_Filename{
|
||||||
|
Filename: keyFile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctx
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,28 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"filename": "api.cert.pem"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"filename": "api.key.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +51,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +79,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,6 +107,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,28 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"filename": "api.cert.pem"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"filename": "api.key.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +51,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +79,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,6 +107,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,28 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"filename": "api.cert.pem"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"filename": "api.key.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +51,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"connectTimeout": "5s",
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"filename": "ca.cert.pem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,9 @@ type LinkedService struct {
|
||||||
// KeyFile is the optional path to a private key to use for TLS connections
|
// KeyFile is the optional path to a private key to use for TLS connections
|
||||||
// from the gateway to the linked service
|
// from the gateway to the linked service
|
||||||
KeyFile string `json:",omitempty"`
|
KeyFile string `json:",omitempty"`
|
||||||
|
|
||||||
|
// SNI is the optional name to specify during the TLS handshake with a linked service
|
||||||
|
SNI string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *TerminatingGatewayConfigEntry) GetKind() string {
|
func (g *TerminatingGatewayConfigEntry) GetKind() string {
|
||||||
|
|
|
@ -185,6 +185,7 @@ func TestAPI_ConfigEntries_TerminatingGateway(t *testing.T) {
|
||||||
CAFile: "/etc/web/ca.crt",
|
CAFile: "/etc/web/ca.crt",
|
||||||
CertFile: "/etc/web/client.crt",
|
CertFile: "/etc/web/client.crt",
|
||||||
KeyFile: "/etc/web/tls.key",
|
KeyFile: "/etc/web/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +213,7 @@ func TestAPI_ConfigEntries_TerminatingGateway(t *testing.T) {
|
||||||
CAFile: "/etc/certs/ca.crt",
|
CAFile: "/etc/certs/ca.crt",
|
||||||
CertFile: "/etc/certs/client.crt",
|
CertFile: "/etc/certs/client.crt",
|
||||||
KeyFile: "/etc/certs/tls.key",
|
KeyFile: "/etc/certs/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, wm, err = configEntries.Set(terminating2, nil)
|
_, wm, err = configEntries.Set(terminating2, nil)
|
||||||
|
|
|
@ -686,7 +686,8 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
"Name": "web",
|
"Name": "web",
|
||||||
"CAFile": "/etc/ca.pem",
|
"CAFile": "/etc/ca.pem",
|
||||||
"CertFile": "/etc/cert.pem",
|
"CertFile": "/etc/cert.pem",
|
||||||
"KeyFile": "/etc/tls.key"
|
"KeyFile": "/etc/tls.key",
|
||||||
|
"SNI": "mydomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "api"
|
"Name": "api"
|
||||||
|
@ -707,6 +708,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||||
CAFile: "/etc/ca.pem",
|
CAFile: "/etc/ca.pem",
|
||||||
CertFile: "/etc/cert.pem",
|
CertFile: "/etc/cert.pem",
|
||||||
KeyFile: "/etc/tls.key",
|
KeyFile: "/etc/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "api",
|
Name: "api",
|
||||||
|
|
|
@ -258,6 +258,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
ca_file = "/etc/ca.crt"
|
ca_file = "/etc/ca.crt"
|
||||||
cert_file = "/etc/client.crt"
|
cert_file = "/etc/client.crt"
|
||||||
key_file = "/etc/tls.key"
|
key_file = "/etc/tls.key"
|
||||||
|
sni = "mydomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "*"
|
name = "*"
|
||||||
|
@ -276,6 +277,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
CAFile = "/etc/ca.crt"
|
CAFile = "/etc/ca.crt"
|
||||||
CertFile = "/etc/client.crt"
|
CertFile = "/etc/client.crt"
|
||||||
KeyFile = "/etc/tls.key"
|
KeyFile = "/etc/tls.key"
|
||||||
|
SNI = "mydomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name = "*"
|
Name = "*"
|
||||||
|
@ -294,7 +296,8 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"namespace": "biz",
|
"namespace": "biz",
|
||||||
"ca_file": "/etc/ca.crt",
|
"ca_file": "/etc/ca.crt",
|
||||||
"cert_file": "/etc/client.crt",
|
"cert_file": "/etc/client.crt",
|
||||||
"key_file": "/etc/tls.key"
|
"key_file": "/etc/tls.key",
|
||||||
|
"sni": "mydomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "*",
|
"name": "*",
|
||||||
|
@ -314,7 +317,8 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"Namespace": "biz",
|
"Namespace": "biz",
|
||||||
"CAFile": "/etc/ca.crt",
|
"CAFile": "/etc/ca.crt",
|
||||||
"CertFile": "/etc/client.crt",
|
"CertFile": "/etc/client.crt",
|
||||||
"KeyFile": "/etc/tls.key"
|
"KeyFile": "/etc/tls.key",
|
||||||
|
"SNI": "mydomain"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "*",
|
"Name": "*",
|
||||||
|
@ -334,6 +338,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
CAFile: "/etc/ca.crt",
|
CAFile: "/etc/ca.crt",
|
||||||
CertFile: "/etc/client.crt",
|
CertFile: "/etc/client.crt",
|
||||||
KeyFile: "/etc/tls.key",
|
KeyFile: "/etc/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "*",
|
Name: "*",
|
||||||
|
@ -352,6 +357,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
CAFile: "/etc/ca.crt",
|
CAFile: "/etc/ca.crt",
|
||||||
CertFile: "/etc/client.crt",
|
CertFile: "/etc/client.crt",
|
||||||
KeyFile: "/etc/tls.key",
|
KeyFile: "/etc/tls.key",
|
||||||
|
SNI: "mydomain",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "*",
|
Name: "*",
|
||||||
|
|
Loading…
Reference in New Issue