Skip to content

Commit 39369e0

Browse files
authored
fix(meshexternalservice): fix missing tls context (#12162)
## Motivation #11985 it seems that TLS context wasn't set correctly for MES. ## Implementation information There were two main problems: * We didn't set the TLS context on the egress. * We didn't correctly extract the metadata from the dataplane to check `SystemCaPath`. Both issues resulted in the TLS context not being set correctly. Added a change that retrieved correct metadata and fixed the test. I also added an e2e test that first verifies we cannot communicate with the TLS 1.3 server when using TLS 1.2. It then switches to TLS 1.3 to confirm that communication works. ## Supporting documentation <!-- Is there a MADR? An Issue? A related PR? --> Fix #11985 <!-- > Changelog: skip --> <!-- Uncomment the above section to explicitly set a [`> Changelog:` entry here](https://github.com/kumahq/kuma/blob/master/CONTRIBUTING.md#submitting-a-patch)? --> --------- Signed-off-by: Lukasz Dziedziak <[email protected]>
1 parent 66cc95f commit 39369e0

File tree

11 files changed

+297
-93
lines changed

11 files changed

+297
-93
lines changed

pkg/core/xds/metadata.go

+7
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ func (m *DataplaneMetadata) GetDNSPort() uint32 {
145145
return m.DNSPort
146146
}
147147

148+
func (m *DataplaneMetadata) GetSystemCaPath() string {
149+
if m == nil {
150+
return ""
151+
}
152+
return m.SystemCaPath
153+
}
154+
148155
func (m *DataplaneMetadata) GetDynamicMetadata(key string) string {
149156
if m == nil || m.DynamicMetadata == nil {
150157
return ""

pkg/xds/context/mesh_context_builder.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ func (m *meshContextBuilder) BuildIfChanged(ctx context.Context, meshName string
196196
zoneIngresses := resources.ZoneIngresses().Items
197197
zoneEgresses := resources.ZoneEgresses().Items
198198
externalServices := resources.ExternalServices().Items
199-
endpointMap := xds_topology.BuildEdsEndpointMap(mesh, m.zone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, zoneIngresses, zoneEgresses, externalServices)
199+
endpointMap := xds_topology.BuildEdsEndpointMap(ctx, mesh, m.zone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, zoneIngresses, zoneEgresses, externalServices, loader)
200200
esEndpointMap := xds_topology.BuildExternalServicesEndpointMap(ctx, mesh, externalServices, loader, m.zone)
201-
ingressEndpointMap := xds_topology.BuildIngressEndpointMap(mesh, m.zone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, externalServices, resources.Gateways().Items, zoneEgresses)
201+
ingressEndpointMap := xds_topology.BuildIngressEndpointMap(ctx, mesh, m.zone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, externalServices, resources.Gateways().Items, zoneEgresses, loader)
202202

203203
crossMeshEndpointMap := map[string]xds.EndpointMap{}
204204
for otherMeshName, gateways := range resources.gatewaysAndDataplanesForMesh(mesh) {

pkg/xds/generator/egress/external_services_generator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (g *ExternalServicesGenerator) Generate(
5959
services,
6060
endpointMap,
6161
proxy.ZoneEgressProxy.ZoneEgressResource.IsIPv6(),
62-
proxy.Metadata.GetDynamicMetadata(core_xds.FieldSystemCaPath),
62+
proxy.Metadata.GetSystemCaPath(),
6363
)
6464
if err != nil {
6565
return nil, err

pkg/xds/topology/ingress_outbound_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
package topology_test
22

33
import (
4+
"context"
5+
46
. "github.com/onsi/ginkgo/v2"
57
. "github.com/onsi/gomega"
68

79
mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
10+
"github.com/kumahq/kuma/pkg/core/datasource"
811
core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
912
"github.com/kumahq/kuma/pkg/core/resources/apis/meshservice/api/v1alpha1"
1013
"github.com/kumahq/kuma/pkg/core/resources/model"
14+
"github.com/kumahq/kuma/pkg/core/secrets/cipher"
15+
secret_manager "github.com/kumahq/kuma/pkg/core/secrets/manager"
16+
secret_store "github.com/kumahq/kuma/pkg/core/secrets/store"
1117
core_xds "github.com/kumahq/kuma/pkg/core/xds"
18+
"github.com/kumahq/kuma/pkg/plugins/resources/memory"
1219
"github.com/kumahq/kuma/pkg/test/resources/builders"
1320
test_model "github.com/kumahq/kuma/pkg/test/resources/model"
1421
"github.com/kumahq/kuma/pkg/test/resources/samples"
1522
"github.com/kumahq/kuma/pkg/xds/topology"
1623
)
1724

1825
var _ = Describe("IngressTrafficRoute", func() {
26+
var dataSourceLoader datasource.Loader
27+
28+
BeforeEach(func() {
29+
secretManager := secret_manager.NewSecretManager(secret_store.NewSecretStore(memory.NewStore()), cipher.None(), nil, false)
30+
dataSourceLoader = datasource.NewDataSourceLoader(secretManager)
31+
})
1932
Describe("BuildEndpointMap()", func() {
2033
type testCase struct {
2134
mesh *core_mesh.MeshResource
@@ -33,6 +46,7 @@ var _ = Describe("IngressTrafficRoute", func() {
3346
meshServicesByName[model.NewResourceIdentifier(ms)] = ms
3447
}
3548
endpoints := topology.BuildIngressEndpointMap(
49+
context.Background(),
3650
given.mesh,
3751
"east",
3852
meshServicesByName,
@@ -42,6 +56,7 @@ var _ = Describe("IngressTrafficRoute", func() {
4256
given.externalServices,
4357
nil,
4458
given.zoneEgress,
59+
dataSourceLoader,
4560
)
4661

4762
// then

pkg/xds/topology/outbound.go

+95-72
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func BuildEgressEndpointMap(
7777
}
7878

7979
func BuildIngressEndpointMap(
80+
ctx context.Context,
8081
mesh *core_mesh.MeshResource,
8182
localZone string,
8283
meshServicesByName map[model.ResourceIdentifier]*meshservice_api.MeshServiceResource,
@@ -86,15 +87,17 @@ func BuildIngressEndpointMap(
8687
externalServices []*core_mesh.ExternalServiceResource,
8788
gateways []*core_mesh.MeshGatewayResource,
8889
zoneEgresses []*core_mesh.ZoneEgressResource,
90+
loader datasource.Loader,
8991
) core_xds.EndpointMap {
9092
// Build EDS endpoint map just like for regular DPP, but without list of Ingress.
9193
// This way we only keep local endpoints.
92-
outbound := BuildEdsEndpointMap(mesh, localZone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, nil, zoneEgresses, externalServices)
94+
outbound := BuildEdsEndpointMap(ctx, mesh, localZone, meshServicesByName, meshMultiZoneServices, meshExternalServices, dataplanes, nil, zoneEgresses, externalServices, loader)
9395
fillLocalCrossMeshOutbounds(outbound, mesh, dataplanes, gateways, 1, localZone)
9496
return outbound
9597
}
9698

9799
func BuildEdsEndpointMap(
100+
ctx context.Context,
98101
mesh *core_mesh.MeshResource,
99102
localZone string,
100103
meshServicesByName map[model.ResourceIdentifier]*meshservice_api.MeshServiceResource,
@@ -104,6 +107,7 @@ func BuildEdsEndpointMap(
104107
zoneIngresses []*core_mesh.ZoneIngressResource,
105108
zoneEgresses []*core_mesh.ZoneEgressResource,
106109
externalServices []*core_mesh.ExternalServiceResource,
110+
loader datasource.Loader,
107111
) core_xds.EndpointMap {
108112
outbound := core_xds.EndpointMap{}
109113

@@ -127,7 +131,7 @@ func BuildEdsEndpointMap(
127131

128132
fillRemoteMeshServices(outbound, meshServices, zoneIngresses, mesh, localZone)
129133

130-
fillExternalServicesOutboundsThroughEgress(outbound, externalServices, meshExternalServices, zoneEgresses, mesh, localZone)
134+
fillExternalServicesOutboundsThroughEgress(ctx, outbound, externalServices, meshExternalServices, zoneEgresses, mesh, localZone, loader)
131135

132136
// it has to be last because it reuses endpoints for other cases
133137
fillMeshMultiZoneServices(outbound, meshServicesByName, meshMultiZoneServices)
@@ -657,70 +661,9 @@ func createMeshExternalServiceEndpoint(
657661
meshName := mesh.GetMeta().GetName()
658662
tls := mes.Spec.Tls
659663
if tls != nil && tls.Enabled {
660-
var caCert, clientCert, clientKey []byte
661-
es.TLSEnabled = tls.Enabled
662-
es.FallbackToSystemCa = true
663-
es.AllowRenegotiation = tls.AllowRenegotiation
664-
665-
var err error
666-
if tls.Verification != nil {
667-
if tls.Verification.CaCert != nil {
668-
caCert, err = loadBytes(ctx, tls.Verification.CaCert.ConvertToProto(), meshName, loader)
669-
if err != nil {
670-
return errors.Wrap(err, "could not load caCert")
671-
}
672-
es.CaCert = caCert
673-
}
674-
if tls.Verification.ClientKey != nil && tls.Verification.ClientCert != nil {
675-
clientCert, err = loadBytes(ctx, tls.Verification.ClientCert.ConvertToProto(), meshName, loader)
676-
if err != nil {
677-
return errors.Wrap(err, "could not load clientCert")
678-
}
679-
clientKey, err = loadBytes(ctx, tls.Verification.ClientKey.ConvertToProto(), meshName, loader)
680-
if err != nil {
681-
return errors.Wrap(err, "could not load clientKey")
682-
}
683-
es.ClientCert = clientCert
684-
es.ClientKey = clientKey
685-
}
686-
if pointer.Deref(tls.Verification.ServerName) != "" {
687-
es.ServerName = pointer.Deref(tls.Verification.ServerName)
688-
}
689-
for _, san := range pointer.Deref(tls.Verification.SubjectAltNames) {
690-
es.SANs = append(es.SANs, core_xds.SAN{
691-
MatchType: core_xds.MatchType(san.Type),
692-
Value: san.Value,
693-
})
694-
}
695-
if tls.Version != nil {
696-
if tls.Version.Min != nil {
697-
es.MinTlsVersion = pointer.To(common_tls.ToTlsVersion(tls.Version.Min))
698-
}
699-
if tls.Version.Max != nil {
700-
es.MaxTlsVersion = pointer.To(common_tls.ToTlsVersion(tls.Version.Max))
701-
}
702-
}
703-
// Server name and SNI we need to add
704-
// mes.Spec.Tls.Verification.SubjectAltNames
705-
if tls.Verification.Mode != nil {
706-
switch *tls.Verification.Mode {
707-
case meshexternalservice_api.TLSVerificationSkipSAN:
708-
es.ServerName = ""
709-
es.SANs = []core_xds.SAN{}
710-
es.SkipHostnameVerification = true
711-
case meshexternalservice_api.TLSVerificationSkipCA:
712-
es.CaCert = nil
713-
es.FallbackToSystemCa = false
714-
case meshexternalservice_api.TLSVerificationSkipAll:
715-
es.FallbackToSystemCa = false
716-
es.CaCert = nil
717-
es.ClientKey = nil
718-
es.ClientCert = nil
719-
es.ServerName = ""
720-
es.SANs = []core_xds.SAN{}
721-
es.SkipHostnameVerification = true
722-
}
723-
}
664+
err := setTlsConfiguration(ctx, tls, es, meshName, loader)
665+
if err != nil {
666+
return err
724667
}
725668
}
726669

@@ -742,6 +685,75 @@ func createMeshExternalServiceEndpoint(
742685
return nil
743686
}
744687

688+
func setTlsConfiguration(ctx context.Context, tls *meshexternalservice_api.Tls, es *core_xds.ExternalService, meshName string, loader datasource.Loader) error {
689+
var caCert, clientCert, clientKey []byte
690+
es.TLSEnabled = tls.Enabled
691+
es.FallbackToSystemCa = true
692+
es.AllowRenegotiation = tls.AllowRenegotiation
693+
694+
if tls.Version != nil {
695+
if tls.Version.Min != nil {
696+
es.MinTlsVersion = pointer.To(common_tls.ToTlsVersion(tls.Version.Min))
697+
}
698+
if tls.Version.Max != nil {
699+
es.MaxTlsVersion = pointer.To(common_tls.ToTlsVersion(tls.Version.Max))
700+
}
701+
}
702+
var err error
703+
if tls.Verification != nil {
704+
if tls.Verification.CaCert != nil {
705+
caCert, err = loadBytes(ctx, tls.Verification.CaCert.ConvertToProto(), meshName, loader)
706+
if err != nil {
707+
return errors.Wrap(err, "could not load caCert")
708+
}
709+
es.CaCert = caCert
710+
}
711+
if tls.Verification.ClientKey != nil && tls.Verification.ClientCert != nil {
712+
clientCert, err = loadBytes(ctx, tls.Verification.ClientCert.ConvertToProto(), meshName, loader)
713+
if err != nil {
714+
return errors.Wrap(err, "could not load clientCert")
715+
}
716+
clientKey, err = loadBytes(ctx, tls.Verification.ClientKey.ConvertToProto(), meshName, loader)
717+
if err != nil {
718+
return errors.Wrap(err, "could not load clientKey")
719+
}
720+
es.ClientCert = clientCert
721+
es.ClientKey = clientKey
722+
}
723+
if pointer.Deref(tls.Verification.ServerName) != "" {
724+
es.ServerName = pointer.Deref(tls.Verification.ServerName)
725+
}
726+
for _, san := range pointer.Deref(tls.Verification.SubjectAltNames) {
727+
es.SANs = append(es.SANs, core_xds.SAN{
728+
MatchType: core_xds.MatchType(san.Type),
729+
Value: san.Value,
730+
})
731+
}
732+
// Server name and SNI we need to add
733+
// mes.Spec.Tls.Verification.SubjectAltNames
734+
if tls.Verification.Mode != nil {
735+
switch *tls.Verification.Mode {
736+
case meshexternalservice_api.TLSVerificationSkipSAN:
737+
es.ServerName = ""
738+
es.SANs = []core_xds.SAN{}
739+
es.SkipHostnameVerification = true
740+
case meshexternalservice_api.TLSVerificationSkipCA:
741+
es.CaCert = nil
742+
es.FallbackToSystemCa = false
743+
case meshexternalservice_api.TLSVerificationSkipAll:
744+
es.FallbackToSystemCa = false
745+
es.CaCert = nil
746+
es.ClientKey = nil
747+
es.ClientCert = nil
748+
es.ServerName = ""
749+
es.SANs = []core_xds.SAN{}
750+
es.SkipHostnameVerification = true
751+
}
752+
}
753+
}
754+
return nil
755+
}
756+
745757
func createExternalServiceEndpoint(
746758
ctx context.Context,
747759
outbound core_xds.EndpointMap,
@@ -760,12 +772,14 @@ func createExternalServiceEndpoint(
760772
}
761773

762774
func fillExternalServicesOutboundsThroughEgress(
775+
ctx context.Context,
763776
outbound core_xds.EndpointMap,
764777
externalServices []*core_mesh.ExternalServiceResource,
765778
meshExternalServices []*meshexternalservice_api.MeshExternalServiceResource,
766779
zoneEgresses []*core_mesh.ZoneEgressResource,
767780
mesh *core_mesh.MeshResource,
768781
localZone string,
782+
loader datasource.Loader,
769783
) {
770784
if mesh.ZoneEgressEnabled() {
771785
for _, externalService := range externalServices {
@@ -799,6 +813,18 @@ func fillExternalServicesOutboundsThroughEgress(
799813
serviceTags := maps.Clone(mes.Meta.GetLabels())
800814
serviceName := mes.DestinationName(uint32(mes.Spec.Match.Port))
801815
locality := GetLocality(localZone, getZone(serviceTags), mesh.LocalityAwareLbEnabled())
816+
tls := mes.Spec.Tls
817+
es := &core_xds.ExternalService{
818+
Protocol: mes.Spec.Match.Protocol,
819+
OwnerResource: pointer.To(core_rules.UniqueKey(mes, "")),
820+
}
821+
if tls != nil && tls.Enabled {
822+
err := setTlsConfiguration(ctx, tls, es, mes.Meta.GetMesh(), loader)
823+
if err != nil {
824+
outboundLog.Error(err, "unable to create MeshExternalService endpoint for egress. Endpoint won't be included in the XDS.", "name", mes.Meta.GetName(), "mesh", mes.Meta.GetMesh())
825+
continue
826+
}
827+
}
802828

803829
for _, ze := range zoneEgresses {
804830
zeNetworking := ze.Spec.GetNetworking()
@@ -811,12 +837,9 @@ func fillExternalServicesOutboundsThroughEgress(
811837
Tags: serviceTags,
812838
// AS it's a role of zone egress to load balance traffic between
813839
// instances, we can safely set weight to 1
814-
Weight: 1,
815-
Locality: locality,
816-
ExternalService: &core_xds.ExternalService{
817-
Protocol: mes.Spec.Match.Protocol,
818-
OwnerResource: pointer.To(core_rules.UniqueKey(mes, "")),
819-
},
840+
Weight: 1,
841+
Locality: locality,
842+
ExternalService: es,
820843
}
821844

822845
outbound[serviceName] = append(outbound[serviceName], endpoint)

0 commit comments

Comments
 (0)