github.com/kiali/kiali@v1.84.0/business/istio_validations.go (about) 1 package business 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 9 networking_v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" 10 security_v1beta "istio.io/client-go/pkg/apis/security/v1beta1" 11 12 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 14 "github.com/kiali/kiali/business/checkers" 15 "github.com/kiali/kiali/business/references" 16 "github.com/kiali/kiali/config" 17 "github.com/kiali/kiali/kubernetes" 18 "github.com/kiali/kiali/log" 19 "github.com/kiali/kiali/models" 20 "github.com/kiali/kiali/observability" 21 "github.com/kiali/kiali/prometheus/internalmetrics" 22 ) 23 24 type IstioValidationsService struct { 25 userClients map[string]kubernetes.ClientInterface 26 businessLayer *Layer 27 } 28 29 type ObjectChecker interface { 30 Check() models.IstioValidations 31 } 32 33 type ReferenceChecker interface { 34 References() models.IstioReferencesMap 35 } 36 37 // GetValidations returns an IstioValidations object with all the checks found when running 38 // all the enabled checkers. If service is "" then the whole namespace is validated. 39 // If service is not empty string, then all of its associated Istio objects are validated. 40 func (in *IstioValidationsService) GetValidations(ctx context.Context, cluster, namespace, service, workload string) (models.IstioValidations, error) { 41 var end observability.EndFunc 42 ctx, end = observability.StartSpan(ctx, "GetValidations", 43 observability.Attribute("package", "business"), 44 observability.Attribute("cluster", cluster), 45 observability.Attribute("namespace", namespace), 46 observability.Attribute("service", service), 47 observability.Attribute("workload", workload), 48 ) 49 defer end() 50 51 // Check if user has access to the namespace (RBAC) in cache scenarios and/or 52 // if namespace is accessible from Kiali (Deployment.AccessibleNamespaces) 53 if namespace != "" { 54 if _, err := in.businessLayer.Namespace.GetClusterNamespace(ctx, namespace, cluster); err != nil { 55 return nil, err 56 } 57 } 58 59 // Ensure the service exists 60 if service != "" { 61 _, err := in.businessLayer.Svc.GetService(ctx, cluster, namespace, service) 62 if err != nil { 63 if err != nil { 64 log.Warningf("Error invoking GetService %s", err) 65 } 66 return nil, fmt.Errorf("Service [namespace: %s] [name: %s] doesn't exist for Validations.", namespace, service) 67 } 68 } 69 70 // time this function execution so we can capture how long it takes to fully validate this namespace/service 71 timer := internalmetrics.GetValidationProcessingTimePrometheusTimer(namespace, service) 72 defer timer.ObserveDuration() 73 74 wg := sync.WaitGroup{} 75 errChan := make(chan error, 1) 76 77 var istioConfigList models.IstioConfigList 78 var services models.ServiceList 79 var serviceAccounts map[string][]string 80 var namespaces models.Namespaces 81 var workloadsPerNamespace map[string]models.WorkloadList 82 var mtlsDetails kubernetes.MTLSDetails 83 var rbacDetails kubernetes.RBACDetails 84 var registryServices []*kubernetes.RegistryService 85 86 wg.Add(4) // We need to add these here to make sure we don't execute wg.Wait() before scheduler has started goroutines 87 if service != "" { 88 wg.Add(1) 89 } 90 91 // We fetch without target service as some validations will require full-namespace details 92 go in.fetchIstioConfigList(ctx, &istioConfigList, &mtlsDetails, &rbacDetails, cluster, namespace, errChan, &wg) 93 94 if workload != "" { 95 // load only requested workload 96 go in.fetchWorkload(ctx, &workloadsPerNamespace, cluster, workload, namespace, errChan, &wg) 97 } else { 98 go in.fetchAllWorkloads(ctx, &workloadsPerNamespace, cluster, &namespaces, errChan, &wg) 99 } 100 101 go in.fetchServiceAccounts(ctx, &serviceAccounts, errChan, &wg) 102 103 go in.fetchNonLocalmTLSConfigs(&mtlsDetails, cluster, errChan, &wg) 104 if service != "" { 105 go in.fetchServices(ctx, &services, cluster, namespace, errChan, &wg) 106 } 107 108 criteria := RegistryCriteria{AllNamespaces: true, Cluster: cluster} 109 registryServices = in.businessLayer.RegistryStatus.GetRegistryServices(criteria) 110 111 wg.Wait() 112 close(errChan) 113 for e := range errChan { 114 if e != nil { // Check that default value wasn't returned 115 return nil, e 116 } 117 } 118 119 objectCheckers := in.getAllObjectCheckers(istioConfigList, workloadsPerNamespace, mtlsDetails, rbacDetails, namespaces, registryServices, cluster, serviceAccounts) 120 121 // Get group validations for same kind istio objects 122 validations := runObjectCheckers(objectCheckers) 123 124 if service != "" { 125 // in.businessLayer.Svc.GetServiceList(criteria) on fetchServices performs the validations on the service 126 // No need to re-fetch deployments+pods for this 127 validations.MergeValidations(services.Validations) 128 validations = validations.FilterBySingleType("service", service) 129 } else if workload != "" { 130 workloadList := workloadsPerNamespace[namespace] 131 validations.MergeValidations(workloadList.Validations) 132 validations = validations.FilterBySingleType("workload", workload) 133 } 134 135 return validations, nil 136 } 137 138 func (in *IstioValidationsService) getAllObjectCheckers(istioConfigList models.IstioConfigList, workloadsPerNamespace map[string]models.WorkloadList, mtlsDetails kubernetes.MTLSDetails, rbacDetails kubernetes.RBACDetails, namespaces []models.Namespace, registryServices []*kubernetes.RegistryService, cluster string, serviceAccounts map[string][]string) []ObjectChecker { 139 return []ObjectChecker{ 140 checkers.NoServiceChecker{Namespaces: namespaces, IstioConfigList: &istioConfigList, WorkloadsPerNamespace: workloadsPerNamespace, AuthorizationDetails: &rbacDetails, RegistryServices: registryServices, PolicyAllowAny: in.isPolicyAllowAny(), Cluster: cluster}, 141 checkers.VirtualServiceChecker{Namespaces: namespaces, VirtualServices: istioConfigList.VirtualServices, DestinationRules: istioConfigList.DestinationRules, Cluster: cluster}, 142 checkers.DestinationRulesChecker{Namespaces: namespaces, DestinationRules: istioConfigList.DestinationRules, MTLSDetails: mtlsDetails, ServiceEntries: istioConfigList.ServiceEntries, Cluster: cluster}, 143 checkers.GatewayChecker{Gateways: istioConfigList.Gateways, WorkloadsPerNamespace: workloadsPerNamespace, IsGatewayToNamespace: in.isGatewayToNamespace(), Cluster: cluster}, 144 checkers.PeerAuthenticationChecker{PeerAuthentications: mtlsDetails.PeerAuthentications, MTLSDetails: mtlsDetails, WorkloadsPerNamespace: workloadsPerNamespace, Cluster: cluster}, 145 checkers.ServiceEntryChecker{ServiceEntries: istioConfigList.ServiceEntries, Namespaces: namespaces, WorkloadEntries: istioConfigList.WorkloadEntries, Cluster: cluster}, 146 checkers.AuthorizationPolicyChecker{AuthorizationPolicies: rbacDetails.AuthorizationPolicies, Namespaces: namespaces, ServiceEntries: istioConfigList.ServiceEntries, WorkloadsPerNamespace: workloadsPerNamespace, MtlsDetails: mtlsDetails, VirtualServices: istioConfigList.VirtualServices, RegistryServices: registryServices, PolicyAllowAny: in.isPolicyAllowAny(), Cluster: cluster, ServiceAccounts: serviceAccounts}, 147 checkers.SidecarChecker{Sidecars: istioConfigList.Sidecars, Namespaces: namespaces, WorkloadsPerNamespace: workloadsPerNamespace, ServiceEntries: istioConfigList.ServiceEntries, RegistryServices: registryServices, Cluster: cluster}, 148 checkers.RequestAuthenticationChecker{RequestAuthentications: istioConfigList.RequestAuthentications, WorkloadsPerNamespace: workloadsPerNamespace, Cluster: cluster}, 149 checkers.WorkloadChecker{AuthorizationPolicies: rbacDetails.AuthorizationPolicies, WorkloadsPerNamespace: workloadsPerNamespace, Cluster: cluster}, 150 checkers.K8sGatewayChecker{K8sGateways: istioConfigList.K8sGateways, Cluster: cluster, GatewayClasses: in.businessLayer.IstioConfig.GatewayAPIClasses(cluster)}, 151 checkers.K8sHTTPRouteChecker{K8sHTTPRoutes: istioConfigList.K8sHTTPRoutes, K8sGateways: istioConfigList.K8sGateways, K8sReferenceGrants: istioConfigList.K8sReferenceGrants, Namespaces: namespaces, RegistryServices: registryServices, Cluster: cluster}, 152 checkers.K8sReferenceGrantChecker{K8sReferenceGrants: istioConfigList.K8sReferenceGrants, Namespaces: namespaces, Cluster: cluster}, 153 checkers.WasmPluginChecker{WasmPlugins: istioConfigList.WasmPlugins, Namespaces: namespaces}, 154 checkers.TelemetryChecker{Telemetries: istioConfigList.Telemetries, Namespaces: namespaces}, 155 } 156 } 157 158 // GetIstioObjectValidations validates a single Istio object of the given type with the given name found in the given namespace. 159 func (in *IstioValidationsService) GetIstioObjectValidations(ctx context.Context, cluster, namespace string, objectType string, object string) (models.IstioValidations, models.IstioReferencesMap, error) { 160 var end observability.EndFunc 161 ctx, end = observability.StartSpan(ctx, "GetIstioObjectValidations", 162 observability.Attribute("package", "business"), 163 observability.Attribute("cluster", "cluster"), 164 observability.Attribute("namespace", namespace), 165 observability.Attribute("objectType", objectType), 166 observability.Attribute("object", object), 167 ) 168 defer end() 169 170 var istioConfigList models.IstioConfigList 171 var namespaces models.Namespaces 172 var workloadsPerNamespace map[string]models.WorkloadList 173 var mtlsDetails kubernetes.MTLSDetails 174 var rbacDetails kubernetes.RBACDetails 175 var registryServices []*kubernetes.RegistryService 176 var serviceAccounts map[string][]string 177 var err error 178 var objectCheckers []ObjectChecker 179 var referenceChecker ReferenceChecker 180 istioReferences := models.IstioReferencesMap{} 181 182 istioApiEnabled := config.Get().ExternalServices.Istio.IstioAPIEnabled 183 184 // Check if user has access to the namespace (RBAC) in cache scenarios and/or 185 // if namespace is accessible from Kiali (Deployment.AccessibleNamespaces) 186 if _, err = in.businessLayer.Namespace.GetClusterNamespace(ctx, namespace, cluster); err != nil { 187 return nil, istioReferences, err 188 } 189 190 // time this function execution so we can capture how long it takes to fully validate this istio object 191 timer := internalmetrics.GetSingleValidationProcessingTimePrometheusTimer(namespace, objectType, object) 192 defer timer.ObserveDuration() 193 194 wg := sync.WaitGroup{} 195 errChan := make(chan error, 1) 196 197 // Get all the Istio objects from a Namespace and all gateways from every namespace 198 wg.Add(4) 199 200 go in.fetchIstioConfigList(ctx, &istioConfigList, &mtlsDetails, &rbacDetails, cluster, namespace, errChan, &wg) 201 go in.fetchAllWorkloads(ctx, &workloadsPerNamespace, cluster, &namespaces, errChan, &wg) 202 go in.fetchServiceAccounts(ctx, &serviceAccounts, errChan, &wg) 203 go in.fetchNonLocalmTLSConfigs(&mtlsDetails, cluster, errChan, &wg) 204 205 if istioApiEnabled { 206 criteria := RegistryCriteria{AllNamespaces: true, Cluster: cluster} 207 registryServices = in.businessLayer.RegistryStatus.GetRegistryServices(criteria) 208 } 209 210 wg.Wait() 211 212 noServiceChecker := checkers.NoServiceChecker{Cluster: cluster, Namespaces: namespaces, IstioConfigList: &istioConfigList, WorkloadsPerNamespace: workloadsPerNamespace, AuthorizationDetails: &rbacDetails, RegistryServices: registryServices, PolicyAllowAny: in.isPolicyAllowAny()} 213 214 switch objectType { 215 case kubernetes.Gateways: 216 objectCheckers = []ObjectChecker{ 217 checkers.GatewayChecker{Cluster: cluster, Gateways: istioConfigList.Gateways, WorkloadsPerNamespace: workloadsPerNamespace, IsGatewayToNamespace: in.isGatewayToNamespace()}, 218 } 219 referenceChecker = references.GatewayReferences{Gateways: istioConfigList.Gateways, VirtualServices: istioConfigList.VirtualServices, WorkloadsPerNamespace: workloadsPerNamespace} 220 case kubernetes.VirtualServices: 221 virtualServiceChecker := checkers.VirtualServiceChecker{Cluster: cluster, Namespaces: namespaces, VirtualServices: istioConfigList.VirtualServices, DestinationRules: istioConfigList.DestinationRules} 222 objectCheckers = []ObjectChecker{noServiceChecker, virtualServiceChecker} 223 referenceChecker = references.VirtualServiceReferences{Namespace: namespace, Namespaces: namespaces, VirtualServices: istioConfigList.VirtualServices, DestinationRules: istioConfigList.DestinationRules, AuthorizationPolicies: rbacDetails.AuthorizationPolicies} 224 case kubernetes.DestinationRules: 225 destinationRulesChecker := checkers.DestinationRulesChecker{Cluster: cluster, Namespaces: namespaces, DestinationRules: istioConfigList.DestinationRules, MTLSDetails: mtlsDetails, ServiceEntries: istioConfigList.ServiceEntries} 226 objectCheckers = []ObjectChecker{noServiceChecker, destinationRulesChecker} 227 referenceChecker = references.DestinationRuleReferences{Namespace: namespace, Namespaces: namespaces, DestinationRules: istioConfigList.DestinationRules, VirtualServices: istioConfigList.VirtualServices, WorkloadsPerNamespace: workloadsPerNamespace, ServiceEntries: istioConfigList.ServiceEntries, RegistryServices: registryServices} 228 case kubernetes.ServiceEntries: 229 serviceEntryChecker := checkers.ServiceEntryChecker{Cluster: cluster, ServiceEntries: istioConfigList.ServiceEntries, Namespaces: namespaces, WorkloadEntries: istioConfigList.WorkloadEntries} 230 objectCheckers = []ObjectChecker{serviceEntryChecker} 231 referenceChecker = references.ServiceEntryReferences{AuthorizationPolicies: rbacDetails.AuthorizationPolicies, Namespace: namespace, Namespaces: namespaces, DestinationRules: istioConfigList.DestinationRules, ServiceEntries: istioConfigList.ServiceEntries, Sidecars: istioConfigList.Sidecars, RegistryServices: registryServices} 232 case kubernetes.Sidecars: 233 sidecarsChecker := checkers.SidecarChecker{ 234 Cluster: cluster, Sidecars: istioConfigList.Sidecars, Namespaces: namespaces, 235 WorkloadsPerNamespace: workloadsPerNamespace, ServiceEntries: istioConfigList.ServiceEntries, RegistryServices: registryServices, 236 } 237 objectCheckers = []ObjectChecker{sidecarsChecker} 238 referenceChecker = references.SidecarReferences{Sidecars: istioConfigList.Sidecars, Namespace: namespace, Namespaces: namespaces, ServiceEntries: istioConfigList.ServiceEntries, RegistryServices: registryServices, WorkloadsPerNamespace: workloadsPerNamespace} 239 case kubernetes.AuthorizationPolicies: 240 authPoliciesChecker := checkers.AuthorizationPolicyChecker{ 241 AuthorizationPolicies: rbacDetails.AuthorizationPolicies, 242 Cluster: cluster, Namespaces: namespaces, ServiceEntries: istioConfigList.ServiceEntries, ServiceAccounts: serviceAccounts, 243 WorkloadsPerNamespace: workloadsPerNamespace, MtlsDetails: mtlsDetails, VirtualServices: istioConfigList.VirtualServices, RegistryServices: registryServices, PolicyAllowAny: in.isPolicyAllowAny(), 244 } 245 objectCheckers = []ObjectChecker{authPoliciesChecker} 246 referenceChecker = references.AuthorizationPolicyReferences{AuthorizationPolicies: rbacDetails.AuthorizationPolicies, Namespace: namespace, Namespaces: namespaces, VirtualServices: istioConfigList.VirtualServices, ServiceEntries: istioConfigList.ServiceEntries, RegistryServices: registryServices, WorkloadsPerNamespace: workloadsPerNamespace} 247 case kubernetes.PeerAuthentications: 248 // Validations on PeerAuthentications 249 peerAuthnChecker := checkers.PeerAuthenticationChecker{Cluster: cluster, PeerAuthentications: mtlsDetails.PeerAuthentications, MTLSDetails: mtlsDetails, WorkloadsPerNamespace: workloadsPerNamespace} 250 objectCheckers = []ObjectChecker{peerAuthnChecker} 251 referenceChecker = references.PeerAuthReferences{MTLSDetails: mtlsDetails, WorkloadsPerNamespace: workloadsPerNamespace} 252 case kubernetes.WorkloadEntries: 253 // Validation on WorkloadEntries are not yet in place 254 case kubernetes.WorkloadGroups: 255 // Validation on WorkloadGroups are not yet in place 256 case kubernetes.RequestAuthentications: 257 // Validation on RequestAuthentications are not yet in place 258 requestAuthnChecker := checkers.RequestAuthenticationChecker{Cluster: cluster, RequestAuthentications: istioConfigList.RequestAuthentications, WorkloadsPerNamespace: workloadsPerNamespace} 259 objectCheckers = []ObjectChecker{requestAuthnChecker} 260 case kubernetes.EnvoyFilters: 261 // Validation on EnvoyFilters are not yet in place 262 case kubernetes.WasmPlugins: 263 // Validation on WasmPlugins is not expected 264 case kubernetes.Telemetries: 265 // Validation on Telemetries is not expected 266 case kubernetes.K8sGateways: 267 // Validations on K8sGateways 268 objectCheckers = []ObjectChecker{ 269 checkers.K8sGatewayChecker{Cluster: cluster, K8sGateways: istioConfigList.K8sGateways, GatewayClasses: in.businessLayer.IstioConfig.GatewayAPIClasses(cluster)}, 270 } 271 referenceChecker = references.K8sGatewayReferences{K8sGateways: istioConfigList.K8sGateways, K8sHTTPRoutes: istioConfigList.K8sHTTPRoutes} 272 case kubernetes.K8sGRPCRoutes: 273 // Validation on K8sGRPCRoutes is not expected 274 case kubernetes.K8sHTTPRoutes: 275 httpRouteChecker := checkers.K8sHTTPRouteChecker{Cluster: cluster, K8sHTTPRoutes: istioConfigList.K8sHTTPRoutes, K8sGateways: istioConfigList.K8sGateways, K8sReferenceGrants: istioConfigList.K8sReferenceGrants, Namespaces: namespaces, RegistryServices: registryServices} 276 objectCheckers = []ObjectChecker{noServiceChecker, httpRouteChecker} 277 referenceChecker = references.K8sHTTPRouteReferences{K8sHTTPRoutes: istioConfigList.K8sHTTPRoutes, Namespaces: namespaces, K8sReferenceGrants: istioConfigList.K8sReferenceGrants} 278 case kubernetes.K8sReferenceGrants: 279 objectCheckers = []ObjectChecker{ 280 checkers.K8sReferenceGrantChecker{Cluster: cluster, K8sReferenceGrants: istioConfigList.K8sReferenceGrants, Namespaces: namespaces}, 281 } 282 case kubernetes.K8sTCPRoutes: 283 // Validation on K8sTCPRoutes is not expected 284 case kubernetes.K8sTLSRoutes: 285 // Validation on K8sTLSRoutes is not expected 286 default: 287 err = fmt.Errorf("object type not found: %v", objectType) 288 } 289 290 close(errChan) 291 for e := range errChan { 292 if e != nil { // Check that default value wasn't returned 293 return nil, istioReferences, err 294 } 295 } 296 297 if referenceChecker != nil { 298 istioReferences = runObjectReferenceChecker(referenceChecker) 299 } 300 301 if objectCheckers == nil { 302 return models.IstioValidations{}, istioReferences, err 303 } 304 305 return runObjectCheckers(objectCheckers).FilterByKey(models.ObjectTypeSingular[objectType], object), istioReferences, nil 306 } 307 308 func runObjectCheckers(objectCheckers []ObjectChecker) models.IstioValidations { 309 objectTypeValidations := models.IstioValidations{} 310 311 // Run checks for each IstioObject type 312 for _, objectChecker := range objectCheckers { 313 objectTypeValidations.MergeValidations(runObjectChecker(objectChecker)) 314 } 315 316 objectTypeValidations.StripIgnoredChecks() 317 318 return objectTypeValidations 319 } 320 321 func runObjectChecker(objectChecker ObjectChecker) models.IstioValidations { 322 // tracking the time it takes to execute the Check 323 promtimer := internalmetrics.GetCheckerProcessingTimePrometheusTimer(fmt.Sprintf("%T", objectChecker)) 324 defer promtimer.ObserveDuration() 325 return objectChecker.Check() 326 } 327 328 func runObjectReferenceChecker(referenceChecker ReferenceChecker) models.IstioReferencesMap { 329 // tracking the time it takes to execute the Check 330 promtimer := internalmetrics.GetCheckerProcessingTimePrometheusTimer(fmt.Sprintf("%T", referenceChecker)) 331 defer promtimer.ObserveDuration() 332 return referenceChecker.References() 333 } 334 335 func (in *IstioValidationsService) fetchServices(ctx context.Context, rValue *models.ServiceList, cluster, namespace string, errChan chan error, wg *sync.WaitGroup) { 336 defer wg.Done() 337 if len(errChan) == 0 { 338 var services *models.ServiceList 339 var err error 340 criteria := ServiceCriteria{ 341 IncludeHealth: false, 342 Namespace: namespace, 343 Cluster: cluster, 344 } 345 services, err = in.businessLayer.Svc.GetServiceList(ctx, criteria) 346 if err != nil { 347 select { 348 case errChan <- err: 349 default: 350 } 351 } else { 352 *rValue = *services 353 } 354 } 355 } 356 357 func (in *IstioValidationsService) fetchAllWorkloads(ctx context.Context, rValue *map[string]models.WorkloadList, cluster string, namespaces *models.Namespaces, errChan chan error, wg *sync.WaitGroup) { 358 defer wg.Done() 359 if len(errChan) == 0 { 360 nss, err := in.businessLayer.Namespace.GetClusterNamespaces(ctx, cluster) 361 if err != nil { 362 errChan <- err 363 return 364 365 } 366 *namespaces = nss 367 368 allWorkloads := map[string]models.WorkloadList{} 369 for _, ns := range nss { 370 criteria := WorkloadCriteria{Cluster: cluster, Namespace: ns.Name, IncludeIstioResources: false, IncludeHealth: false} 371 workloadList, err := in.businessLayer.Workload.GetWorkloadList(ctx, criteria) 372 if err != nil { 373 select { 374 case errChan <- err: 375 default: 376 } 377 } else { 378 allWorkloads[ns.Name] = workloadList 379 } 380 } 381 *rValue = allWorkloads 382 } 383 } 384 385 func (in *IstioValidationsService) fetchWorkload(ctx context.Context, rValue *map[string]models.WorkloadList, cluster, workload, namespace string, errChan chan error, wg *sync.WaitGroup) { 386 defer wg.Done() 387 if len(errChan) == 0 { 388 allWorkloads := map[string]models.WorkloadList{} 389 criteria := WorkloadCriteria{Cluster: cluster, WorkloadName: workload, Namespace: namespace, IncludeIstioResources: true, IncludeHealth: false} 390 workloadList, err := in.businessLayer.Workload.GetWorkloadList(ctx, criteria) 391 if err != nil { 392 select { 393 case errChan <- err: 394 default: 395 } 396 } else { 397 allWorkloads[namespace] = workloadList 398 } 399 *rValue = allWorkloads 400 } 401 } 402 403 // fetchServiceAccounts returns list of names of the ServiceAccounts retrieved from Registry Services in a map per cluster. 404 func (in *IstioValidationsService) fetchServiceAccounts(ctx context.Context, rValue *map[string][]string, errChan chan error, wg *sync.WaitGroup) { 405 serviceAccounts := map[string][]string{} 406 407 istioDomain := strings.Replace(config.Get().ExternalServices.Istio.IstioIdentityDomain, "svc.", "", 1) 408 defer wg.Done() 409 if len(errChan) == 0 { 410 for _, cluster := range in.businessLayer.Namespace.GetClusterList() { 411 nss, err := in.businessLayer.Namespace.GetClusterNamespaces(ctx, cluster) 412 if err != nil { 413 errChan <- err 414 return 415 } 416 for _, ns := range nss { 417 criteria := WorkloadCriteria{Cluster: cluster, Namespace: ns.Name, IncludeIstioResources: false, IncludeHealth: false} 418 workloadList, err := in.businessLayer.Workload.GetWorkloadList(ctx, criteria) 419 if err != nil { 420 select { 421 case errChan <- err: 422 default: 423 } 424 } else { 425 for _, wl := range workloadList.Workloads { 426 for _, sAccountName := range wl.ServiceAccountNames { 427 saFullName := fmt.Sprintf("%s/ns/%s/sa/%s", istioDomain, ns.Name, sAccountName) 428 found := false 429 if _, ok := serviceAccounts[cluster]; !ok { 430 serviceAccounts[cluster] = []string{} 431 } 432 for _, name := range serviceAccounts[cluster] { 433 if name == saFullName { 434 found = true 435 break 436 } 437 } 438 if !found { 439 serviceAccounts[cluster] = append(serviceAccounts[cluster], saFullName) 440 } 441 } 442 } 443 } 444 } 445 } 446 *rValue = serviceAccounts 447 } 448 } 449 450 func (in *IstioValidationsService) fetchIstioConfigList(ctx context.Context, rValue *models.IstioConfigList, mtlsDetails *kubernetes.MTLSDetails, rbacDetails *kubernetes.RBACDetails, cluster, namespace string, errChan chan error, wg *sync.WaitGroup) { 451 defer wg.Done() 452 if len(errChan) > 0 { 453 return 454 } 455 456 // all namespaces are necessary to check Ambient mode of each namespace 457 nss, err := in.businessLayer.Namespace.GetClusterNamespaces(ctx, cluster) 458 if err != nil { 459 errChan <- err 460 return 461 } 462 463 criteria := IstioConfigCriteria{ 464 IncludeGateways: true, 465 IncludeDestinationRules: true, 466 IncludeServiceEntries: true, 467 IncludeVirtualServices: true, 468 IncludeSidecars: true, 469 IncludeRequestAuthentications: true, 470 IncludeWorkloadEntries: true, 471 IncludeAuthorizationPolicies: true, 472 IncludePeerAuthentications: true, 473 IncludeK8sHTTPRoutes: true, 474 IncludeK8sGateways: true, 475 IncludeK8sReferenceGrants: true, 476 } 477 istioConfigMap, err := in.businessLayer.IstioConfig.GetIstioConfigMap(ctx, meta_v1.NamespaceAll, criteria) 478 if err != nil { 479 errChan <- err 480 return 481 } 482 istioConfigList := istioConfigMap[cluster] 483 484 // Filter VS 485 filteredVSs := in.filterVSExportToNamespaces(nss, namespace, cluster, istioConfigList.VirtualServices) 486 rValue.VirtualServices = append(rValue.VirtualServices, filteredVSs...) 487 488 // Filter DR 489 filteredDRs := in.filterDRExportToNamespaces(nss, namespace, cluster, kubernetes.FilterAutogeneratedDestinationRules(istioConfigList.DestinationRules)) 490 rValue.DestinationRules = append(rValue.DestinationRules, filteredDRs...) 491 mtlsDetails.DestinationRules = append(mtlsDetails.DestinationRules, filteredDRs...) 492 493 // Filter SE 494 filteredSEs := in.filterSEExportToNamespaces(nss, namespace, cluster, istioConfigList.ServiceEntries) 495 rValue.ServiceEntries = append(rValue.ServiceEntries, filteredSEs...) 496 497 // All Gateways 498 rValue.Gateways = append(rValue.Gateways, kubernetes.FilterAutogeneratedGateways(istioConfigList.Gateways)...) 499 500 // All K8sGateways 501 rValue.K8sGateways = append(rValue.K8sGateways, istioConfigList.K8sGateways...) 502 503 // All K8sHTTPRoutes 504 rValue.K8sHTTPRoutes = append(rValue.K8sHTTPRoutes, istioConfigList.K8sHTTPRoutes...) 505 506 // All K8sReferenceGrants 507 rValue.K8sReferenceGrants = append(rValue.K8sReferenceGrants, istioConfigList.K8sReferenceGrants...) 508 509 // All Sidecars 510 rValue.Sidecars = append(rValue.Sidecars, istioConfigList.Sidecars...) 511 512 // All RequestAuthentications 513 rValue.RequestAuthentications = append(rValue.RequestAuthentications, istioConfigList.RequestAuthentications...) 514 515 // All WorkloadEntries 516 rValue.WorkloadEntries = append(rValue.WorkloadEntries, istioConfigList.WorkloadEntries...) 517 518 in.filterPeerAuths(namespace, mtlsDetails, istioConfigList.PeerAuthentications) 519 520 in.filterAuthPolicies(namespace, rbacDetails, istioConfigList.AuthorizationPolicies) 521 } 522 523 func (in *IstioValidationsService) filterPeerAuths(namespace string, mtlsDetails *kubernetes.MTLSDetails, peerAuths []*security_v1beta.PeerAuthentication) { 524 rootNs := config.Get().ExternalServices.Istio.RootNamespace 525 for _, pa := range peerAuths { 526 if pa.Namespace == rootNs { 527 mtlsDetails.MeshPeerAuthentications = append(mtlsDetails.MeshPeerAuthentications, pa) 528 } 529 if pa.Namespace == namespace || namespace == "" { 530 mtlsDetails.PeerAuthentications = append(mtlsDetails.PeerAuthentications, pa) 531 } 532 } 533 } 534 535 func (in *IstioValidationsService) filterAuthPolicies(namespace string, rbacDetails *kubernetes.RBACDetails, authPolicies []*security_v1beta.AuthorizationPolicy) { 536 for _, ap := range authPolicies { 537 if ap.Namespace == namespace || namespace == "" { 538 rbacDetails.AuthorizationPolicies = append(rbacDetails.AuthorizationPolicies, ap) 539 } 540 } 541 } 542 543 func (in *IstioValidationsService) filterVSExportToNamespaces(allNamespaces models.Namespaces, namespace string, cluster string, vs []*networking_v1beta1.VirtualService) []*networking_v1beta1.VirtualService { 544 if namespace == "" { 545 return kubernetes.FilterAutogeneratedVirtualServices(vs) 546 } 547 var result []*networking_v1beta1.VirtualService 548 for _, v := range vs { 549 if kubernetes.IsAutogenerated(v.Name) { 550 continue 551 } 552 if in.isExportedObjectIncluded(v.Spec.ExportTo, allNamespaces, v.Namespace, namespace, cluster) { 553 result = append(result, v) 554 } 555 } 556 return result 557 } 558 559 func (in *IstioValidationsService) filterDRExportToNamespaces(allNamespaces models.Namespaces, namespace string, cluster string, dr []*networking_v1beta1.DestinationRule) []*networking_v1beta1.DestinationRule { 560 if namespace == "" { 561 return dr 562 } 563 var result []*networking_v1beta1.DestinationRule 564 for _, d := range dr { 565 if in.isExportedObjectIncluded(d.Spec.ExportTo, allNamespaces, d.Namespace, namespace, cluster) { 566 result = append(result, d) 567 } 568 } 569 return result 570 } 571 572 func (in *IstioValidationsService) filterSEExportToNamespaces(allNamespaces models.Namespaces, namespace string, cluster string, se []*networking_v1beta1.ServiceEntry) []*networking_v1beta1.ServiceEntry { 573 if namespace == "" { 574 return se 575 } 576 var result []*networking_v1beta1.ServiceEntry 577 for _, s := range se { 578 if in.isExportedObjectIncluded(s.Spec.ExportTo, allNamespaces, s.Namespace, namespace, cluster) { 579 result = append(result, s) 580 } 581 } 582 return result 583 } 584 585 func (in *IstioValidationsService) isExportedObjectIncluded(exportTo []string, allNamespaces models.Namespaces, objectNamespace, exportedNamespace string, cluster string) bool { 586 // Ambient mode namespace does not support ExportTo, so export only to own namespace 587 if in.businessLayer.IstioConfig.IsAmbientEnabled(cluster) && allNamespaces.IsNamespaceAmbient(objectNamespace, cluster) { 588 return objectNamespace == exportedNamespace 589 } else { 590 if len(exportTo) > 0 { 591 for _, exportToNs := range exportTo { 592 // take only namespaces where it is exported to, or if it is exported to all namespaces, or export to own namespace 593 if checkExportTo(exportToNs, exportedNamespace, objectNamespace, allNamespaces) { 594 return true 595 } 596 } 597 } else { 598 // no exportTo field, means object exported to all namespaces 599 return true 600 } 601 } 602 return false 603 } 604 605 func (in *IstioValidationsService) fetchNonLocalmTLSConfigs(mtlsDetails *kubernetes.MTLSDetails, cluster string, errChan chan error, wg *sync.WaitGroup) { 606 defer wg.Done() 607 if len(errChan) > 0 { 608 return 609 } 610 611 cfg := config.Get() 612 // TODO: Handle multi-primary instead of only using home cluster. 613 kubeCache, err := kialiCache.GetKubeCache(cfg.KubernetesConfig.ClusterName) 614 if err != nil { 615 return 616 } 617 618 istioConfig, err := kubeCache.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, "")) 619 if err != nil { 620 errChan <- err 621 return 622 } 623 624 icm, err := kubernetes.GetIstioConfigMap(istioConfig) 625 if err != nil { 626 errChan <- err 627 } else { 628 mtlsDetails.EnabledAutoMtls = icm.GetEnableAutoMtls() 629 } 630 } 631 632 func (in *IstioValidationsService) isGatewayToNamespace() bool { 633 mesh, err := in.businessLayer.Mesh.GetMesh(context.TODO()) 634 if err != nil { 635 log.Errorf("Error getting mesh config: %s", err) 636 return false 637 } 638 639 // TODO: Multi-primary support 640 for _, controlPlane := range mesh.ControlPlanes { 641 if controlPlane.Cluster.IsKialiHome { 642 return controlPlane.Config.IsGatewayToNamespace 643 } 644 } 645 646 return false 647 } 648 649 func (in *IstioValidationsService) isPolicyAllowAny() bool { 650 allowAny := false 651 if in.businessLayer != nil { 652 if otp, err := in.businessLayer.Mesh.OutboundTrafficPolicy(); err == nil { 653 if otp.Mode == "" || otp.Mode == AllowAny { 654 return true 655 } 656 } 657 } 658 return allowAny 659 } 660 661 func checkExportTo(exportToNs string, namespace string, ownNs string, allNamespaces models.Namespaces) bool { 662 // check if namespaces where it is exported to, or if it is exported to all namespaces, or export to own namespace 663 // when exported to non-existing namespace, consider it to show validation error 664 return exportToNs == "*" || exportToNs == namespace || (exportToNs == "." && ownNs == namespace) || (exportToNs != "." && exportToNs != "*" && !allNamespaces.Includes(exportToNs)) 665 }