istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/gateway/certificate.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gateway 16 17 import ( 18 "istio.io/api/networking/v1alpha3" 19 "istio.io/istio/pilot/pkg/features" 20 "istio.io/istio/pkg/config" 21 "istio.io/istio/pkg/config/analysis" 22 "istio.io/istio/pkg/config/analysis/analyzers/util" 23 "istio.io/istio/pkg/config/analysis/msg" 24 "istio.io/istio/pkg/config/resource" 25 "istio.io/istio/pkg/config/schema/gvk" 26 ) 27 28 type CertificateAnalyzer struct{} 29 30 var _ analysis.Analyzer = &CertificateAnalyzer{} 31 32 func (*CertificateAnalyzer) Metadata() analysis.Metadata { 33 return analysis.Metadata{ 34 Name: "gateway.CertificateAnalyzer", 35 Description: "Checks a gateway certificate", 36 Inputs: []config.GroupVersionKind{ 37 gvk.Gateway, 38 }, 39 } 40 } 41 42 // Analyze implements analysis.Analyzer 43 func (gateway *CertificateAnalyzer) Analyze(context analysis.Context) { 44 context.ForEach(gvk.Gateway, func(resource *resource.Instance) bool { 45 gateway.analyzeDuplicateCertificate(resource, context, features.ScopeGatewayToNamespace) 46 return true 47 }) 48 } 49 50 func (gateway *CertificateAnalyzer) analyzeDuplicateCertificate(currentResource *resource.Instance, context analysis.Context, scopeGatewayToNamespace bool) { 51 currentGateway := currentResource.Message.(*v1alpha3.Gateway) 52 currentGatewayFullName := currentResource.Metadata.FullName 53 gateways := getGatewaysWithSelector(context, scopeGatewayToNamespace, currentGatewayFullName, currentGateway.Selector) 54 55 for _, gatewayFullName := range gateways { 56 // ignore matching the same exact gateway 57 if currentGatewayFullName == gatewayFullName { 58 continue 59 } 60 61 gatewayInstance := context.Find(gvk.Gateway, gatewayFullName) 62 gateway := gatewayInstance.Message.(*v1alpha3.Gateway) 63 for _, currentServer := range currentGateway.Servers { 64 for _, server := range gateway.Servers { 65 // make sure have TLS configuration 66 if currentServer.Tls == nil || server.Tls == nil { 67 continue 68 } 69 70 if haveSameCertificate(currentServer.Tls, server.Tls) { 71 gatewayNames := []string{currentGatewayFullName.String(), gatewayFullName.String()} 72 message := msg.NewGatewayDuplicateCertificate(currentResource, gatewayNames) 73 74 if line, ok := util.ErrorLine(currentResource, util.MetadataName); ok { 75 message.Line = line 76 } 77 78 context.Report(gvk.Gateway, message) 79 } 80 } 81 } 82 } 83 } 84 85 func haveSameCertificate(currentGatewayTLS, gatewayTLS *v1alpha3.ServerTLSSettings) bool { 86 if currentGatewayTLS.CredentialName != "" && gatewayTLS.CredentialName != "" { 87 return currentGatewayTLS.CredentialName == gatewayTLS.CredentialName 88 } 89 90 if currentGatewayTLS.CredentialName == "" && gatewayTLS.CredentialName == "" { 91 if currentGatewayTLS.ServerCertificate != "" && gatewayTLS.ServerCertificate != "" { 92 if currentGatewayTLS.ServerCertificate == gatewayTLS.ServerCertificate { 93 if currentGatewayTLS.PrivateKey != "" && gatewayTLS.PrivateKey != "" { 94 return currentGatewayTLS.PrivateKey == gatewayTLS.PrivateKey 95 } 96 return false 97 } 98 } 99 } 100 101 return false 102 } 103 104 // get all gateways that is superset of the selector 105 func getGatewaysWithSelector(c analysis.Context, gwScope bool, currentGWName resource.FullName, currentGWSelector map[string]string) []resource.FullName { 106 var gateways []resource.FullName 107 108 c.ForEach(gvk.Gateway, func(resource *resource.Instance) bool { 109 // if scopeToNamespace true, ignore adding gateways from other namespace 110 if gwScope { 111 if currentGWName.Namespace != resource.Metadata.FullName.Namespace { 112 return true 113 } 114 } 115 116 // if current gateway selector is empty, match all gateway 117 if len(currentGWSelector) == 0 { 118 gateways = append(gateways, resource.Metadata.FullName) 119 return true 120 } 121 122 gateway := resource.Message.(*v1alpha3.Gateway) 123 // if current gateway selector is subset of other gateway selector 124 // add other gateway 125 if selectorSubset(currentGWSelector, gateway.Selector) { 126 gateways = append(gateways, resource.Metadata.FullName) 127 } 128 129 return true 130 }) 131 132 return gateways 133 } 134 135 func selectorSubset(selectorX, selectorY map[string]string) bool { 136 var count int 137 138 for keyX, valueX := range selectorX { 139 for keyY, valueY := range selectorY { 140 if keyX == keyY { 141 // if have same key but different value 142 // mean selectorX is not subset of selectorY 143 if valueX != valueY { 144 return false 145 } 146 // if key and value is same 147 // increase the counting 148 count++ 149 } 150 } 151 } 152 153 // if total counting is not same with the length 154 // of selectorX, selectorX is not subset of selectorY 155 return count == len(selectorX) 156 }