github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/virtualserver.go (about)

     1  package configs
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/golang/glog"
     9  	"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"
    10  	"github.com/nginxinc/kubernetes-ingress/internal/nginx"
    11  	conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1"
    12  	api_v1 "k8s.io/api/core/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"k8s.io/apimachinery/pkg/labels"
    15  	"k8s.io/apimachinery/pkg/runtime"
    16  
    17  	"github.com/nginxinc/kubernetes-ingress/internal/configs/version2"
    18  	conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1"
    19  )
    20  
    21  const (
    22  	nginx502Server         = "unix:/var/lib/nginx/nginx-502-server.sock"
    23  	internalLocationPrefix = "internal_location_"
    24  	nginx418Server         = "unix:/var/lib/nginx/nginx-418-server.sock"
    25  	specContext            = "spec"
    26  	routeContext           = "route"
    27  	subRouteContext        = "subroute"
    28  )
    29  
    30  var incompatibleLBMethodsForSlowStart = map[string]bool{
    31  	"random":                          true,
    32  	"ip_hash":                         true,
    33  	"random two":                      true,
    34  	"random two least_conn":           true,
    35  	"random two least_time=header":    true,
    36  	"random two least_time=last_byte": true,
    37  }
    38  
    39  // MeshPodOwner contains the type and name of the K8s resource that owns the pod.
    40  // This owner information is needed for NGINX Service Mesh metrics.
    41  type MeshPodOwner struct {
    42  	// OwnerType is one of the following: statefulset, daemonset, deployment.
    43  	OwnerType string
    44  	// OwnerName is the name of the statefulset, daemonset, or deployment.
    45  	OwnerName string
    46  }
    47  
    48  // PodInfo contains the name of the Pod and the MeshPodOwner information
    49  // which is used for NGINX Service Mesh metrics.
    50  type PodInfo struct {
    51  	Name string
    52  	MeshPodOwner
    53  }
    54  
    55  // VirtualServerEx holds a VirtualServer along with the resources that are referenced in this VirtualServer.
    56  type VirtualServerEx struct {
    57  	VirtualServer       *conf_v1.VirtualServer
    58  	Endpoints           map[string][]string
    59  	VirtualServerRoutes []*conf_v1.VirtualServerRoute
    60  	ExternalNameSvcs    map[string]bool
    61  	Policies            map[string]*conf_v1.Policy
    62  	PodsByIP            map[string]PodInfo
    63  	SecretRefs          map[string]*secrets.SecretReference
    64  	ApPolRefs           map[string]*unstructured.Unstructured
    65  	LogConfRefs         map[string]*unstructured.Unstructured
    66  }
    67  
    68  func (vsx *VirtualServerEx) String() string {
    69  	if vsx == nil {
    70  		return "<nil>"
    71  	}
    72  
    73  	if vsx.VirtualServer == nil {
    74  		return "VirtualServerEx has no VirtualServer"
    75  	}
    76  
    77  	return fmt.Sprintf("%s/%s", vsx.VirtualServer.Namespace, vsx.VirtualServer.Name)
    78  }
    79  
    80  // GenerateEndpointsKey generates a key for the Endpoints map in VirtualServerEx.
    81  func GenerateEndpointsKey(
    82  	serviceNamespace string,
    83  	serviceName string,
    84  	subselector map[string]string,
    85  	port uint16,
    86  ) string {
    87  	if len(subselector) > 0 {
    88  		return fmt.Sprintf("%s/%s_%s:%d", serviceNamespace, serviceName, labels.Set(subselector).String(), port)
    89  	}
    90  	return fmt.Sprintf("%s/%s:%d", serviceNamespace, serviceName, port)
    91  }
    92  
    93  type upstreamNamer struct {
    94  	prefix    string
    95  	namespace string
    96  }
    97  
    98  func newUpstreamNamerForVirtualServer(virtualServer *conf_v1.VirtualServer) *upstreamNamer {
    99  	return &upstreamNamer{
   100  		prefix:    fmt.Sprintf("vs_%s_%s", virtualServer.Namespace, virtualServer.Name),
   101  		namespace: virtualServer.Namespace,
   102  	}
   103  }
   104  
   105  func newUpstreamNamerForVirtualServerRoute(
   106  	virtualServer *conf_v1.VirtualServer,
   107  	virtualServerRoute *conf_v1.VirtualServerRoute,
   108  ) *upstreamNamer {
   109  	return &upstreamNamer{
   110  		prefix: fmt.Sprintf(
   111  			"vs_%s_%s_vsr_%s_%s",
   112  			virtualServer.Namespace,
   113  			virtualServer.Name,
   114  			virtualServerRoute.Namespace,
   115  			virtualServerRoute.Name,
   116  		),
   117  		namespace: virtualServerRoute.Namespace,
   118  	}
   119  }
   120  
   121  func newUpstreamNamerForTransportServer(transportServer *conf_v1alpha1.TransportServer) *upstreamNamer {
   122  	return &upstreamNamer{
   123  		prefix: fmt.Sprintf("ts_%s_%s", transportServer.Namespace, transportServer.Name),
   124  	}
   125  }
   126  
   127  func (namer *upstreamNamer) GetNameForUpstreamFromAction(action *conf_v1.Action) string {
   128  	var upstream string
   129  	if action.Proxy != nil && action.Proxy.Upstream != "" {
   130  		upstream = action.Proxy.Upstream
   131  	} else {
   132  		upstream = action.Pass
   133  	}
   134  
   135  	return fmt.Sprintf("%s_%s", namer.prefix, upstream)
   136  }
   137  
   138  func (namer *upstreamNamer) GetNameForUpstream(upstream string) string {
   139  	return fmt.Sprintf("%s_%s", namer.prefix, upstream)
   140  }
   141  
   142  type variableNamer struct {
   143  	safeNsName string
   144  }
   145  
   146  func newVariableNamer(virtualServer *conf_v1.VirtualServer) *variableNamer {
   147  	safeNsName := strings.ReplaceAll(fmt.Sprintf("%s_%s", virtualServer.Namespace, virtualServer.Name), "-", "_")
   148  	return &variableNamer{
   149  		safeNsName: safeNsName,
   150  	}
   151  }
   152  
   153  func (namer *variableNamer) GetNameForSplitClientVariable(index int) string {
   154  	return fmt.Sprintf("$vs_%s_splits_%d", namer.safeNsName, index)
   155  }
   156  
   157  func (namer *variableNamer) GetNameForVariableForMatchesRouteMap(
   158  	matchesIndex int,
   159  	matchIndex int,
   160  	conditionIndex int,
   161  ) string {
   162  	return fmt.Sprintf("$vs_%s_matches_%d_match_%d_cond_%d", namer.safeNsName, matchesIndex, matchIndex, conditionIndex)
   163  }
   164  
   165  func (namer *variableNamer) GetNameForVariableForMatchesRouteMainMap(matchesIndex int) string {
   166  	return fmt.Sprintf("$vs_%s_matches_%d", namer.safeNsName, matchesIndex)
   167  }
   168  
   169  func newHealthCheckWithDefaults(
   170  	upstream conf_v1.Upstream,
   171  	upstreamName string,
   172  	cfgParams *ConfigParams,
   173  ) *version2.HealthCheck {
   174  	return &version2.HealthCheck{
   175  		Name:                upstreamName,
   176  		URI:                 "/",
   177  		Interval:            "5s",
   178  		Jitter:              "0s",
   179  		Fails:               1,
   180  		Passes:              1,
   181  		Port:                int(upstream.Port),
   182  		ProxyPass:           fmt.Sprintf("%v://%v", generateProxyPassProtocol(upstream.TLS.Enable), upstreamName),
   183  		ProxyConnectTimeout: generateTimeWithDefault(upstream.ProxyConnectTimeout, cfgParams.ProxyConnectTimeout),
   184  		ProxyReadTimeout:    generateTimeWithDefault(upstream.ProxyReadTimeout, cfgParams.ProxyReadTimeout),
   185  		ProxySendTimeout:    generateTimeWithDefault(upstream.ProxySendTimeout, cfgParams.ProxySendTimeout),
   186  		Headers:             make(map[string]string),
   187  	}
   188  }
   189  
   190  // VirtualServerConfigurator generates a VirtualServer configuration
   191  type virtualServerConfigurator struct {
   192  	cfgParams            *ConfigParams
   193  	isPlus               bool
   194  	isResolverConfigured bool
   195  	isTLSPassthrough     bool
   196  	enableSnippets       bool
   197  	warnings             Warnings
   198  	spiffeCerts          bool
   199  	oidcPolCfg           *oidcPolicyCfg
   200  }
   201  
   202  type oidcPolicyCfg struct {
   203  	oidc *version2.OIDC
   204  	key  string
   205  }
   206  
   207  func (vsc *virtualServerConfigurator) addWarningf(obj runtime.Object, msgFmt string, args ...interface{}) {
   208  	vsc.warnings.AddWarningf(obj, msgFmt, args...)
   209  }
   210  
   211  func (vsc *virtualServerConfigurator) addWarnings(obj runtime.Object, msgs []string) {
   212  	for _, msg := range msgs {
   213  		vsc.warnings.AddWarning(obj, msg)
   214  	}
   215  }
   216  
   217  func (vsc *virtualServerConfigurator) clearWarnings() {
   218  	vsc.warnings = make(map[runtime.Object][]string)
   219  }
   220  
   221  // newVirtualServerConfigurator creates a new VirtualServerConfigurator
   222  func newVirtualServerConfigurator(
   223  	cfgParams *ConfigParams,
   224  	isPlus bool,
   225  	isResolverConfigured bool,
   226  	staticParams *StaticConfigParams,
   227  ) *virtualServerConfigurator {
   228  	return &virtualServerConfigurator{
   229  		cfgParams:            cfgParams,
   230  		isPlus:               isPlus,
   231  		isResolverConfigured: isResolverConfigured,
   232  		isTLSPassthrough:     staticParams.TLSPassthrough,
   233  		enableSnippets:       staticParams.EnableSnippets,
   234  		warnings:             make(map[runtime.Object][]string),
   235  		spiffeCerts:          staticParams.NginxServiceMesh,
   236  		oidcPolCfg:           &oidcPolicyCfg{},
   237  	}
   238  }
   239  
   240  func (vsc *virtualServerConfigurator) generateEndpointsForUpstream(
   241  	owner runtime.Object,
   242  	namespace string,
   243  	upstream conf_v1.Upstream,
   244  	virtualServerEx *VirtualServerEx,
   245  ) []string {
   246  	endpointsKey := GenerateEndpointsKey(namespace, upstream.Service, upstream.Subselector, upstream.Port)
   247  	externalNameSvcKey := GenerateExternalNameSvcKey(namespace, upstream.Service)
   248  	endpoints := virtualServerEx.Endpoints[endpointsKey]
   249  	if !vsc.isPlus && len(endpoints) == 0 {
   250  		return []string{nginx502Server}
   251  	}
   252  
   253  	_, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[externalNameSvcKey]
   254  	if isExternalNameSvc && !vsc.isResolverConfigured {
   255  		msgFmt := "Type ExternalName service %v in upstream %v will be ignored. To use ExternaName services, a resolver must be configured in the ConfigMap"
   256  		vsc.addWarningf(owner, msgFmt, upstream.Service, upstream.Name)
   257  		endpoints = []string{}
   258  	}
   259  
   260  	return endpoints
   261  }
   262  
   263  // GenerateVirtualServerConfig generates a full configuration for a VirtualServer
   264  func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualServerEx, apResources map[string]string) (version2.VirtualServerConfig, Warnings) {
   265  	vsc.clearWarnings()
   266  
   267  	sslConfig := vsc.generateSSLConfig(vsEx.VirtualServer, vsEx.VirtualServer.Spec.TLS, vsEx.VirtualServer.Namespace, vsEx.SecretRefs, vsc.cfgParams)
   268  	tlsRedirectConfig := generateTLSRedirectConfig(vsEx.VirtualServer.Spec.TLS)
   269  
   270  	policyOpts := policyOptions{
   271  		tls:         sslConfig != nil,
   272  		secretRefs:  vsEx.SecretRefs,
   273  		apResources: apResources,
   274  	}
   275  
   276  	ownerDetails := policyOwnerDetails{
   277  		owner:          vsEx.VirtualServer,
   278  		ownerNamespace: vsEx.VirtualServer.Namespace,
   279  		vsNamespace:    vsEx.VirtualServer.Namespace,
   280  		vsName:         vsEx.VirtualServer.Name,
   281  	}
   282  	policiesCfg := vsc.generatePolicies(ownerDetails, vsEx.VirtualServer.Spec.Policies, vsEx.Policies, specContext, policyOpts)
   283  
   284  	// crUpstreams maps an UpstreamName to its conf_v1.Upstream as they are generated
   285  	// necessary for generateLocation to know what Upstream each Location references
   286  	crUpstreams := make(map[string]conf_v1.Upstream)
   287  
   288  	virtualServerUpstreamNamer := newUpstreamNamerForVirtualServer(vsEx.VirtualServer)
   289  	var upstreams []version2.Upstream
   290  	var statusMatches []version2.StatusMatch
   291  	var healthChecks []version2.HealthCheck
   292  	var limitReqZones []version2.LimitReqZone
   293  
   294  	limitReqZones = append(limitReqZones, policiesCfg.LimitReqZones...)
   295  
   296  	// generate upstreams for VirtualServer
   297  	for _, u := range vsEx.VirtualServer.Spec.Upstreams {
   298  		upstreamName := virtualServerUpstreamNamer.GetNameForUpstream(u.Name)
   299  		upstreamNamespace := vsEx.VirtualServer.Namespace
   300  		endpoints := vsc.generateEndpointsForUpstream(vsEx.VirtualServer, upstreamNamespace, u, vsEx)
   301  
   302  		// isExternalNameSvc is always false for OSS
   303  		_, isExternalNameSvc := vsEx.ExternalNameSvcs[GenerateExternalNameSvcKey(upstreamNamespace, u.Service)]
   304  		ups := vsc.generateUpstream(vsEx.VirtualServer, upstreamName, u, isExternalNameSvc, endpoints)
   305  		upstreams = append(upstreams, ups)
   306  
   307  		u.TLS.Enable = isTLSEnabled(u, vsc.spiffeCerts)
   308  		crUpstreams[upstreamName] = u
   309  
   310  		if hc := generateHealthCheck(u, upstreamName, vsc.cfgParams); hc != nil {
   311  			healthChecks = append(healthChecks, *hc)
   312  			if u.HealthCheck.StatusMatch != "" {
   313  				statusMatches = append(
   314  					statusMatches,
   315  					generateUpstreamStatusMatch(upstreamName, u.HealthCheck.StatusMatch),
   316  				)
   317  			}
   318  		}
   319  	}
   320  	// generate upstreams for each VirtualServerRoute
   321  	for _, vsr := range vsEx.VirtualServerRoutes {
   322  		upstreamNamer := newUpstreamNamerForVirtualServerRoute(vsEx.VirtualServer, vsr)
   323  		for _, u := range vsr.Spec.Upstreams {
   324  			upstreamName := upstreamNamer.GetNameForUpstream(u.Name)
   325  			upstreamNamespace := vsr.Namespace
   326  			endpoints := vsc.generateEndpointsForUpstream(vsr, upstreamNamespace, u, vsEx)
   327  
   328  			// isExternalNameSvc is always false for OSS
   329  			_, isExternalNameSvc := vsEx.ExternalNameSvcs[GenerateExternalNameSvcKey(upstreamNamespace, u.Service)]
   330  			ups := vsc.generateUpstream(vsr, upstreamName, u, isExternalNameSvc, endpoints)
   331  			upstreams = append(upstreams, ups)
   332  			u.TLS.Enable = isTLSEnabled(u, vsc.spiffeCerts)
   333  			crUpstreams[upstreamName] = u
   334  
   335  			if hc := generateHealthCheck(u, upstreamName, vsc.cfgParams); hc != nil {
   336  				healthChecks = append(healthChecks, *hc)
   337  				if u.HealthCheck.StatusMatch != "" {
   338  					statusMatches = append(
   339  						statusMatches,
   340  						generateUpstreamStatusMatch(upstreamName, u.HealthCheck.StatusMatch),
   341  					)
   342  				}
   343  			}
   344  		}
   345  	}
   346  
   347  	var locations []version2.Location
   348  	var internalRedirectLocations []version2.InternalRedirectLocation
   349  	var returnLocations []version2.ReturnLocation
   350  	var splitClients []version2.SplitClient
   351  	var maps []version2.Map
   352  	var errorPageLocations []version2.ErrorPageLocation
   353  	vsrErrorPagesFromVs := make(map[string][]conf_v1.ErrorPage)
   354  	vsrErrorPagesRouteIndex := make(map[string]int)
   355  	vsrLocationSnippetsFromVs := make(map[string]string)
   356  	vsrPoliciesFromVs := make(map[string][]conf_v1.PolicyReference)
   357  	isVSR := false
   358  	matchesRoutes := 0
   359  
   360  	variableNamer := newVariableNamer(vsEx.VirtualServer)
   361  
   362  	// generates config for VirtualServer routes
   363  	for _, r := range vsEx.VirtualServer.Spec.Routes {
   364  		errorPageIndex := len(errorPageLocations)
   365  		errorPageLocations = append(errorPageLocations, generateErrorPageLocations(errorPageIndex, r.ErrorPages)...)
   366  
   367  		// ignore routes that reference VirtualServerRoute
   368  		if r.Route != "" {
   369  			name := r.Route
   370  			if !strings.Contains(name, "/") {
   371  				name = fmt.Sprintf("%v/%v", vsEx.VirtualServer.Namespace, r.Route)
   372  			}
   373  
   374  			// store route location snippet for the referenced VirtualServerRoute in case they don't define their own
   375  			if r.LocationSnippets != "" {
   376  				vsrLocationSnippetsFromVs[name] = r.LocationSnippets
   377  			}
   378  
   379  			// store route error pages and route index for the referenced VirtualServerRoute in case they don't define their own
   380  			if len(r.ErrorPages) > 0 {
   381  				vsrErrorPagesFromVs[name] = r.ErrorPages
   382  				vsrErrorPagesRouteIndex[name] = errorPageIndex
   383  			}
   384  
   385  			// store route policies for the referenced VirtualServerRoute in case they don't define their own
   386  			if len(r.Policies) > 0 {
   387  				vsrPoliciesFromVs[name] = r.Policies
   388  			}
   389  
   390  			continue
   391  		}
   392  
   393  		vsLocSnippets := r.LocationSnippets
   394  		ownerDetails := policyOwnerDetails{
   395  			owner:          vsEx.VirtualServer,
   396  			ownerNamespace: vsEx.VirtualServer.Namespace,
   397  			vsNamespace:    vsEx.VirtualServer.Namespace,
   398  			vsName:         vsEx.VirtualServer.Name,
   399  		}
   400  		routePoliciesCfg := vsc.generatePolicies(ownerDetails, r.Policies, vsEx.Policies, routeContext, policyOpts)
   401  		if policiesCfg.OIDC {
   402  			routePoliciesCfg.OIDC = policiesCfg.OIDC
   403  		}
   404  		limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...)
   405  
   406  		if len(r.Matches) > 0 {
   407  			cfg := generateMatchesConfig(
   408  				r,
   409  				virtualServerUpstreamNamer,
   410  				crUpstreams,
   411  				variableNamer,
   412  				matchesRoutes,
   413  				len(splitClients),
   414  				vsc.cfgParams,
   415  				r.ErrorPages,
   416  				errorPageIndex,
   417  				vsLocSnippets,
   418  				vsc.enableSnippets,
   419  				len(returnLocations),
   420  				isVSR,
   421  				"", "",
   422  			)
   423  			addPoliciesCfgToLocations(routePoliciesCfg, cfg.Locations)
   424  
   425  			maps = append(maps, cfg.Maps...)
   426  			locations = append(locations, cfg.Locations...)
   427  			internalRedirectLocations = append(internalRedirectLocations, cfg.InternalRedirectLocation)
   428  			returnLocations = append(returnLocations, cfg.ReturnLocations...)
   429  			splitClients = append(splitClients, cfg.SplitClients...)
   430  			matchesRoutes++
   431  		} else if len(r.Splits) > 0 {
   432  			cfg := generateDefaultSplitsConfig(r, virtualServerUpstreamNamer, crUpstreams, variableNamer, len(splitClients),
   433  				vsc.cfgParams, r.ErrorPages, errorPageIndex, r.Path, vsLocSnippets, vsc.enableSnippets, len(returnLocations), isVSR, "", "")
   434  			addPoliciesCfgToLocations(routePoliciesCfg, cfg.Locations)
   435  
   436  			splitClients = append(splitClients, cfg.SplitClients...)
   437  			locations = append(locations, cfg.Locations...)
   438  			internalRedirectLocations = append(internalRedirectLocations, cfg.InternalRedirectLocation)
   439  			returnLocations = append(returnLocations, cfg.ReturnLocations...)
   440  		} else {
   441  			upstreamName := virtualServerUpstreamNamer.GetNameForUpstreamFromAction(r.Action)
   442  			upstream := crUpstreams[upstreamName]
   443  
   444  			proxySSLName := generateProxySSLName(upstream.Service, vsEx.VirtualServer.Namespace)
   445  
   446  			loc, returnLoc := generateLocation(r.Path, upstreamName, upstream, r.Action, vsc.cfgParams, r.ErrorPages, false,
   447  				errorPageIndex, proxySSLName, r.Path, vsLocSnippets, vsc.enableSnippets, len(returnLocations), isVSR, "", "")
   448  			addPoliciesCfgToLocation(routePoliciesCfg, &loc)
   449  
   450  			locations = append(locations, loc)
   451  			if returnLoc != nil {
   452  				returnLocations = append(returnLocations, *returnLoc)
   453  			}
   454  		}
   455  	}
   456  
   457  	// generate config for subroutes of each VirtualServerRoute
   458  	for _, vsr := range vsEx.VirtualServerRoutes {
   459  		isVSR := true
   460  		upstreamNamer := newUpstreamNamerForVirtualServerRoute(vsEx.VirtualServer, vsr)
   461  		for _, r := range vsr.Spec.Subroutes {
   462  			errorPageIndex := len(errorPageLocations)
   463  			errorPageLocations = append(errorPageLocations, generateErrorPageLocations(errorPageIndex, r.ErrorPages)...)
   464  			errorPages := r.ErrorPages
   465  			vsrNamespaceName := fmt.Sprintf("%v/%v", vsr.Namespace, vsr.Name)
   466  			// use the VirtualServer error pages if the route does not define any
   467  			if r.ErrorPages == nil {
   468  				if vsErrorPages, ok := vsrErrorPagesFromVs[vsrNamespaceName]; ok {
   469  					errorPages = vsErrorPages
   470  					errorPageIndex = vsrErrorPagesRouteIndex[vsrNamespaceName]
   471  				}
   472  			}
   473  
   474  			locSnippets := r.LocationSnippets
   475  			// use the  VirtualServer location snippet if the route does not define any
   476  			if r.LocationSnippets == "" {
   477  				locSnippets = vsrLocationSnippetsFromVs[vsrNamespaceName]
   478  			}
   479  
   480  			var ownerDetails policyOwnerDetails
   481  			var policyRefs []conf_v1.PolicyReference
   482  			var context string
   483  			if len(r.Policies) == 0 {
   484  				// use the VirtualServer route policies if the route does not define any
   485  				ownerDetails = policyOwnerDetails{
   486  					owner:          vsEx.VirtualServer,
   487  					ownerNamespace: vsEx.VirtualServer.Namespace,
   488  					vsNamespace:    vsEx.VirtualServer.Namespace,
   489  					vsName:         vsEx.VirtualServer.Name,
   490  				}
   491  				policyRefs = vsrPoliciesFromVs[vsrNamespaceName]
   492  				context = routeContext
   493  			} else {
   494  				ownerDetails = policyOwnerDetails{
   495  					owner:          vsr,
   496  					ownerNamespace: vsr.Namespace,
   497  					vsNamespace:    vsEx.VirtualServer.Namespace,
   498  					vsName:         vsEx.VirtualServer.Name,
   499  				}
   500  				policyRefs = r.Policies
   501  				context = subRouteContext
   502  			}
   503  			routePoliciesCfg := vsc.generatePolicies(ownerDetails, policyRefs, vsEx.Policies, context, policyOpts)
   504  			if policiesCfg.OIDC {
   505  				routePoliciesCfg.OIDC = policiesCfg.OIDC
   506  			}
   507  			limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...)
   508  			if len(r.Matches) > 0 {
   509  				cfg := generateMatchesConfig(
   510  					r,
   511  					upstreamNamer,
   512  					crUpstreams,
   513  					variableNamer,
   514  					matchesRoutes,
   515  					len(splitClients),
   516  					vsc.cfgParams,
   517  					errorPages,
   518  					errorPageIndex,
   519  					locSnippets,
   520  					vsc.enableSnippets,
   521  					len(returnLocations),
   522  					isVSR,
   523  					vsr.Name,
   524  					vsr.Namespace,
   525  				)
   526  				addPoliciesCfgToLocations(routePoliciesCfg, cfg.Locations)
   527  
   528  				maps = append(maps, cfg.Maps...)
   529  				locations = append(locations, cfg.Locations...)
   530  				internalRedirectLocations = append(internalRedirectLocations, cfg.InternalRedirectLocation)
   531  				returnLocations = append(returnLocations, cfg.ReturnLocations...)
   532  				splitClients = append(splitClients, cfg.SplitClients...)
   533  				matchesRoutes++
   534  			} else if len(r.Splits) > 0 {
   535  				cfg := generateDefaultSplitsConfig(r, upstreamNamer, crUpstreams, variableNamer, len(splitClients), vsc.cfgParams,
   536  					errorPages, errorPageIndex, r.Path, locSnippets, vsc.enableSnippets, len(returnLocations), isVSR, vsr.Name, vsr.Namespace)
   537  				addPoliciesCfgToLocations(routePoliciesCfg, cfg.Locations)
   538  
   539  				splitClients = append(splitClients, cfg.SplitClients...)
   540  				locations = append(locations, cfg.Locations...)
   541  				internalRedirectLocations = append(internalRedirectLocations, cfg.InternalRedirectLocation)
   542  				returnLocations = append(returnLocations, cfg.ReturnLocations...)
   543  			} else {
   544  				upstreamName := upstreamNamer.GetNameForUpstreamFromAction(r.Action)
   545  				upstream := crUpstreams[upstreamName]
   546  				proxySSLName := generateProxySSLName(upstream.Service, vsr.Namespace)
   547  
   548  				loc, returnLoc := generateLocation(r.Path, upstreamName, upstream, r.Action, vsc.cfgParams, errorPages, false,
   549  					errorPageIndex, proxySSLName, r.Path, locSnippets, vsc.enableSnippets, len(returnLocations), isVSR, vsr.Name, vsr.Namespace)
   550  				addPoliciesCfgToLocation(routePoliciesCfg, &loc)
   551  
   552  				locations = append(locations, loc)
   553  				if returnLoc != nil {
   554  					returnLocations = append(returnLocations, *returnLoc)
   555  				}
   556  			}
   557  		}
   558  	}
   559  
   560  	httpSnippets := generateSnippets(vsc.enableSnippets, vsEx.VirtualServer.Spec.HTTPSnippets, []string{})
   561  	serverSnippets := generateSnippets(
   562  		vsc.enableSnippets,
   563  		vsEx.VirtualServer.Spec.ServerSnippets,
   564  		vsc.cfgParams.ServerSnippets,
   565  	)
   566  
   567  	vsCfg := version2.VirtualServerConfig{
   568  		Upstreams:     upstreams,
   569  		SplitClients:  splitClients,
   570  		Maps:          maps,
   571  		StatusMatches: statusMatches,
   572  		LimitReqZones: removeDuplicateLimitReqZones(limitReqZones),
   573  		HTTPSnippets:  httpSnippets,
   574  		Server: version2.Server{
   575  			ServerName:                vsEx.VirtualServer.Spec.Host,
   576  			StatusZone:                vsEx.VirtualServer.Spec.Host,
   577  			ProxyProtocol:             vsc.cfgParams.ProxyProtocol,
   578  			SSL:                       sslConfig,
   579  			ServerTokens:              vsc.cfgParams.ServerTokens,
   580  			SetRealIPFrom:             vsc.cfgParams.SetRealIPFrom,
   581  			RealIPHeader:              vsc.cfgParams.RealIPHeader,
   582  			RealIPRecursive:           vsc.cfgParams.RealIPRecursive,
   583  			Snippets:                  serverSnippets,
   584  			InternalRedirectLocations: internalRedirectLocations,
   585  			Locations:                 locations,
   586  			ReturnLocations:           returnLocations,
   587  			HealthChecks:              healthChecks,
   588  			TLSRedirect:               tlsRedirectConfig,
   589  			ErrorPageLocations:        errorPageLocations,
   590  			TLSPassthrough:            vsc.isTLSPassthrough,
   591  			Allow:                     policiesCfg.Allow,
   592  			Deny:                      policiesCfg.Deny,
   593  			LimitReqOptions:           policiesCfg.LimitReqOptions,
   594  			LimitReqs:                 policiesCfg.LimitReqs,
   595  			JWTAuth:                   policiesCfg.JWTAuth,
   596  			IngressMTLS:               policiesCfg.IngressMTLS,
   597  			EgressMTLS:                policiesCfg.EgressMTLS,
   598  			OIDC:                      vsc.oidcPolCfg.oidc,
   599  			WAF:                       policiesCfg.WAF,
   600  			PoliciesErrorReturn:       policiesCfg.ErrorReturn,
   601  			VSNamespace:               vsEx.VirtualServer.Namespace,
   602  			VSName:                    vsEx.VirtualServer.Name,
   603  		},
   604  		SpiffeCerts: vsc.spiffeCerts,
   605  	}
   606  
   607  	return vsCfg, vsc.warnings
   608  }
   609  
   610  type policiesCfg struct {
   611  	Allow           []string
   612  	Deny            []string
   613  	LimitReqOptions version2.LimitReqOptions
   614  	LimitReqZones   []version2.LimitReqZone
   615  	LimitReqs       []version2.LimitReq
   616  	JWTAuth         *version2.JWTAuth
   617  	IngressMTLS     *version2.IngressMTLS
   618  	EgressMTLS      *version2.EgressMTLS
   619  	OIDC            bool
   620  	WAF             *version2.WAF
   621  	ErrorReturn     *version2.Return
   622  }
   623  
   624  func newPoliciesConfig() *policiesCfg {
   625  	return &policiesCfg{}
   626  }
   627  
   628  type policyOwnerDetails struct {
   629  	owner          runtime.Object
   630  	ownerNamespace string
   631  	vsNamespace    string
   632  	vsName         string
   633  }
   634  
   635  type policyOptions struct {
   636  	tls         bool
   637  	secretRefs  map[string]*secrets.SecretReference
   638  	apResources map[string]string
   639  }
   640  
   641  type validationResults struct {
   642  	isError  bool
   643  	warnings []string
   644  }
   645  
   646  func newValidationResults() *validationResults {
   647  	return &validationResults{}
   648  }
   649  
   650  func (v *validationResults) addWarningf(msgFmt string, args ...interface{}) {
   651  	v.warnings = append(v.warnings, fmt.Sprintf(msgFmt, args...))
   652  }
   653  
   654  func (p *policiesCfg) addAccessControlConfig(accessControl *conf_v1.AccessControl) *validationResults {
   655  	res := newValidationResults()
   656  	p.Allow = append(p.Allow, accessControl.Allow...)
   657  	p.Deny = append(p.Deny, accessControl.Deny...)
   658  	if len(p.Allow) > 0 && len(p.Deny) > 0 {
   659  		res.addWarningf(
   660  			"AccessControl policy (or policies) with deny rules is overridden by policy (or policies) with allow rules",
   661  		)
   662  	}
   663  	return res
   664  }
   665  
   666  func (p *policiesCfg) addRateLimitConfig(
   667  	rateLimit *conf_v1.RateLimit,
   668  	polKey string,
   669  	polNamespace string,
   670  	polName string,
   671  	vsNamespace string,
   672  	vsName string,
   673  ) *validationResults {
   674  	res := newValidationResults()
   675  	rlZoneName := fmt.Sprintf("pol_rl_%v_%v_%v_%v", polNamespace, polName, vsNamespace, vsName)
   676  	p.LimitReqs = append(p.LimitReqs, generateLimitReq(rlZoneName, rateLimit))
   677  	p.LimitReqZones = append(p.LimitReqZones, generateLimitReqZone(rlZoneName, rateLimit))
   678  	if len(p.LimitReqs) == 1 {
   679  		p.LimitReqOptions = generateLimitReqOptions(rateLimit)
   680  	} else {
   681  		curOptions := generateLimitReqOptions(rateLimit)
   682  		if curOptions.DryRun != p.LimitReqOptions.DryRun {
   683  			res.addWarningf("RateLimit policy %s with limit request option dryRun='%v' is overridden to dryRun='%v' by the first policy reference in this context", polKey, curOptions.DryRun, p.LimitReqOptions.DryRun)
   684  		}
   685  		if curOptions.LogLevel != p.LimitReqOptions.LogLevel {
   686  			res.addWarningf("RateLimit policy %s with limit request option logLevel='%v' is overridden to logLevel='%v' by the first policy reference in this context", polKey, curOptions.LogLevel, p.LimitReqOptions.LogLevel)
   687  		}
   688  		if curOptions.RejectCode != p.LimitReqOptions.RejectCode {
   689  			res.addWarningf("RateLimit policy %s with limit request option rejectCode='%v' is overridden to rejectCode='%v' by the first policy reference in this context", polKey, curOptions.RejectCode, p.LimitReqOptions.RejectCode)
   690  		}
   691  	}
   692  	return res
   693  }
   694  
   695  func (p *policiesCfg) addJWTAuthConfig(
   696  	jwtAuth *conf_v1.JWTAuth,
   697  	polKey string,
   698  	polNamespace string,
   699  	secretRefs map[string]*secrets.SecretReference,
   700  ) *validationResults {
   701  	res := newValidationResults()
   702  	if p.JWTAuth != nil {
   703  		res.addWarningf("Multiple jwt policies in the same context is not valid. JWT policy %s will be ignored", polKey)
   704  		return res
   705  	}
   706  
   707  	jwtSecretKey := fmt.Sprintf("%v/%v", polNamespace, jwtAuth.Secret)
   708  	secretRef := secretRefs[jwtSecretKey]
   709  	var secretType api_v1.SecretType
   710  	if secretRef.Secret != nil {
   711  		secretType = secretRef.Secret.Type
   712  	}
   713  	if secretType != "" && secretType != secrets.SecretTypeJWK {
   714  		res.addWarningf("JWT policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, jwtSecretKey, secretType, secrets.SecretTypeJWK)
   715  		res.isError = true
   716  		return res
   717  	} else if secretRef.Error != nil {
   718  		res.addWarningf("JWT policy %s references an invalid secret %s: %v", polKey, jwtSecretKey, secretRef.Error)
   719  		res.isError = true
   720  		return res
   721  	}
   722  
   723  	p.JWTAuth = &version2.JWTAuth{
   724  		Secret: secretRef.Path,
   725  		Realm:  jwtAuth.Realm,
   726  		Token:  jwtAuth.Token,
   727  	}
   728  	return res
   729  }
   730  
   731  func (p *policiesCfg) addIngressMTLSConfig(
   732  	ingressMTLS *conf_v1.IngressMTLS,
   733  	polKey string,
   734  	polNamespace string,
   735  	context string,
   736  	tls bool,
   737  	secretRefs map[string]*secrets.SecretReference,
   738  ) *validationResults {
   739  	res := newValidationResults()
   740  	if !tls {
   741  		res.addWarningf("TLS must be enabled in VirtualServer for IngressMTLS policy %s", polKey)
   742  		res.isError = true
   743  		return res
   744  	}
   745  	if context != specContext {
   746  		res.addWarningf("IngressMTLS policy %s is not allowed in the %v context", polKey, context)
   747  		res.isError = true
   748  		return res
   749  	}
   750  	if p.IngressMTLS != nil {
   751  		res.addWarningf("Multiple ingressMTLS policies are not allowed. IngressMTLS policy %s will be ignored", polKey)
   752  		return res
   753  	}
   754  
   755  	secretKey := fmt.Sprintf("%v/%v", polNamespace, ingressMTLS.ClientCertSecret)
   756  	secretRef := secretRefs[secretKey]
   757  	var secretType api_v1.SecretType
   758  	if secretRef.Secret != nil {
   759  		secretType = secretRef.Secret.Type
   760  	}
   761  	if secretType != "" && secretType != secrets.SecretTypeCA {
   762  		res.addWarningf("IngressMTLS policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, secretKey, secretType, secrets.SecretTypeCA)
   763  		res.isError = true
   764  		return res
   765  	} else if secretRef.Error != nil {
   766  		res.addWarningf("IngressMTLS policy %q references an invalid secret %s: %v", polKey, secretKey, secretRef.Error)
   767  		res.isError = true
   768  		return res
   769  	}
   770  
   771  	verifyDepth := 1
   772  	verifyClient := "on"
   773  	if ingressMTLS.VerifyDepth != nil {
   774  		verifyDepth = *ingressMTLS.VerifyDepth
   775  	}
   776  	if ingressMTLS.VerifyClient != "" {
   777  		verifyClient = ingressMTLS.VerifyClient
   778  	}
   779  
   780  	p.IngressMTLS = &version2.IngressMTLS{
   781  		ClientCert:   secretRef.Path,
   782  		VerifyClient: verifyClient,
   783  		VerifyDepth:  verifyDepth,
   784  	}
   785  	return res
   786  }
   787  
   788  func (p *policiesCfg) addEgressMTLSConfig(
   789  	egressMTLS *conf_v1.EgressMTLS,
   790  	polKey string,
   791  	polNamespace string,
   792  	secretRefs map[string]*secrets.SecretReference,
   793  ) *validationResults {
   794  	res := newValidationResults()
   795  	if p.EgressMTLS != nil {
   796  		res.addWarningf(
   797  			"Multiple egressMTLS policies in the same context is not valid. EgressMTLS policy %s will be ignored",
   798  			polKey,
   799  		)
   800  		return res
   801  	}
   802  
   803  	var tlsSecretPath string
   804  
   805  	if egressMTLS.TLSSecret != "" {
   806  		egressTLSSecret := fmt.Sprintf("%v/%v", polNamespace, egressMTLS.TLSSecret)
   807  
   808  		secretRef := secretRefs[egressTLSSecret]
   809  		var secretType api_v1.SecretType
   810  		if secretRef.Secret != nil {
   811  			secretType = secretRef.Secret.Type
   812  		}
   813  		if secretType != "" && secretType != api_v1.SecretTypeTLS {
   814  			res.addWarningf("EgressMTLS policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, egressTLSSecret, secretType, api_v1.SecretTypeTLS)
   815  			res.isError = true
   816  			return res
   817  		} else if secretRef.Error != nil {
   818  			res.addWarningf("EgressMTLS policy %s references an invalid secret %s: %v", polKey, egressTLSSecret, secretRef.Error)
   819  			res.isError = true
   820  			return res
   821  		}
   822  
   823  		tlsSecretPath = secretRef.Path
   824  	}
   825  
   826  	var trustedSecretPath string
   827  
   828  	if egressMTLS.TrustedCertSecret != "" {
   829  		trustedCertSecret := fmt.Sprintf("%v/%v", polNamespace, egressMTLS.TrustedCertSecret)
   830  
   831  		secretRef := secretRefs[trustedCertSecret]
   832  		var secretType api_v1.SecretType
   833  		if secretRef.Secret != nil {
   834  			secretType = secretRef.Secret.Type
   835  		}
   836  		if secretType != "" && secretType != secrets.SecretTypeCA {
   837  			res.addWarningf("EgressMTLS policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, trustedCertSecret, secretType, secrets.SecretTypeCA)
   838  			res.isError = true
   839  			return res
   840  		} else if secretRef.Error != nil {
   841  			res.addWarningf("EgressMTLS policy %s references an invalid secret %s: %v", polKey, trustedCertSecret, secretRef.Error)
   842  			res.isError = true
   843  			return res
   844  		}
   845  
   846  		trustedSecretPath = secretRef.Path
   847  	}
   848  
   849  	p.EgressMTLS = &version2.EgressMTLS{
   850  		Certificate:    tlsSecretPath,
   851  		CertificateKey: tlsSecretPath,
   852  		Ciphers:        generateString(egressMTLS.Ciphers, "DEFAULT"),
   853  		Protocols:      generateString(egressMTLS.Protocols, "TLSv1 TLSv1.1 TLSv1.2"),
   854  		VerifyServer:   egressMTLS.VerifyServer,
   855  		VerifyDepth:    generateIntFromPointer(egressMTLS.VerifyDepth, 1),
   856  		SessionReuse:   generateBool(egressMTLS.SessionReuse, true),
   857  		ServerName:     egressMTLS.ServerName,
   858  		TrustedCert:    trustedSecretPath,
   859  		SSLName:        generateString(egressMTLS.SSLName, "$proxy_host"),
   860  	}
   861  	return res
   862  }
   863  
   864  func (p *policiesCfg) addOIDCConfig(
   865  	oidc *conf_v1.OIDC,
   866  	polKey string,
   867  	polNamespace string,
   868  	secretRefs map[string]*secrets.SecretReference,
   869  	oidcPolCfg *oidcPolicyCfg,
   870  ) *validationResults {
   871  	res := newValidationResults()
   872  	if p.OIDC {
   873  		res.addWarningf(
   874  			"Multiple oidc policies in the same context is not valid. OIDC policy %s will be ignored",
   875  			polKey,
   876  		)
   877  		return res
   878  	}
   879  
   880  	if oidcPolCfg.oidc != nil {
   881  		if oidcPolCfg.key != polKey {
   882  			res.addWarningf(
   883  				"Only one oidc policy is allowed in a VirtualServer and its VirtualServerRoutes. Can't use %s. Use %s",
   884  				polKey,
   885  				oidcPolCfg.key,
   886  			)
   887  			res.isError = true
   888  			return res
   889  		}
   890  	} else {
   891  		secretKey := fmt.Sprintf("%v/%v", polNamespace, oidc.ClientSecret)
   892  		secretRef := secretRefs[secretKey]
   893  
   894  		var secretType api_v1.SecretType
   895  		if secretRef.Secret != nil {
   896  			secretType = secretRef.Secret.Type
   897  		}
   898  		if secretType != "" && secretType != secrets.SecretTypeOIDC {
   899  			res.addWarningf("OIDC policy %s references a secret %s of a wrong type '%s', must be '%s'", polKey, secretKey, secretType, secrets.SecretTypeOIDC)
   900  			res.isError = true
   901  			return res
   902  		} else if secretRef.Error != nil {
   903  			res.addWarningf("OIDC policy %s references an invalid secret %s: %v", polKey, secretKey, secretRef.Error)
   904  			res.isError = true
   905  			return res
   906  		}
   907  
   908  		clientSecret := secretRef.Secret.Data[ClientSecretKey]
   909  
   910  		redirectURI := oidc.RedirectURI
   911  		if redirectURI == "" {
   912  			redirectURI = "/_codexch"
   913  		}
   914  		scope := oidc.Scope
   915  		if scope == "" {
   916  			scope = "openid"
   917  		}
   918  
   919  		oidcPolCfg.oidc = &version2.OIDC{
   920  			AuthEndpoint:  oidc.AuthEndpoint,
   921  			TokenEndpoint: oidc.TokenEndpoint,
   922  			JwksURI:       oidc.JWKSURI,
   923  			ClientID:      oidc.ClientID,
   924  			ClientSecret:  string(clientSecret),
   925  			Scope:         scope,
   926  			RedirectURI:   redirectURI,
   927  		}
   928  		oidcPolCfg.key = polKey
   929  	}
   930  
   931  	p.OIDC = true
   932  
   933  	return res
   934  }
   935  
   936  func (p *policiesCfg) addWAFConfig(
   937  	waf *conf_v1.WAF,
   938  	polKey string,
   939  	polNamespace string,
   940  	apResources map[string]string,
   941  ) *validationResults {
   942  	res := newValidationResults()
   943  	if p.WAF != nil {
   944  		res.addWarningf("Multiple WAF policies in the same context is not valid. WAF policy %s will be ignored", polKey)
   945  		return res
   946  	}
   947  
   948  	if waf.Enable {
   949  		p.WAF = &version2.WAF{Enable: "on"}
   950  	} else {
   951  		p.WAF = &version2.WAF{Enable: "off"}
   952  	}
   953  
   954  	if waf.ApPolicy != "" {
   955  		apPolKey := waf.ApPolicy
   956  		hasNamepace := strings.Contains(apPolKey, "/")
   957  		if !hasNamepace {
   958  			apPolKey = fmt.Sprintf("%v/%v", polNamespace, apPolKey)
   959  		}
   960  
   961  		if apPolPath, exists := apResources[apPolKey]; exists {
   962  			p.WAF.ApPolicy = apPolPath
   963  		} else {
   964  			res.addWarningf("WAF policy %s references an invalid or non-existing App Protect policy %s", polKey, apPolKey)
   965  			res.isError = true
   966  			return res
   967  		}
   968  	}
   969  
   970  	if waf.SecurityLog != nil {
   971  		p.WAF.ApSecurityLogEnable = true
   972  
   973  		logConfKey := waf.SecurityLog.ApLogConf
   974  		hasNamepace := strings.Contains(logConfKey, "/")
   975  		if !hasNamepace {
   976  			logConfKey = fmt.Sprintf("%v/%v", polNamespace, logConfKey)
   977  		}
   978  
   979  		if logConfPath, ok := apResources[logConfKey]; ok {
   980  			logDest := generateString(waf.SecurityLog.LogDest, "syslog:server=localhost:514")
   981  			p.WAF.ApLogConf = fmt.Sprintf("%s %s", logConfPath, logDest)
   982  		} else {
   983  			res.addWarningf("WAF policy %s references an invalid or non-existing log config %s", polKey, logConfKey)
   984  			res.isError = true
   985  		}
   986  	}
   987  
   988  	return res
   989  }
   990  
   991  func (vsc *virtualServerConfigurator) generatePolicies(
   992  	ownerDetails policyOwnerDetails,
   993  	policyRefs []conf_v1.PolicyReference,
   994  	policies map[string]*conf_v1.Policy,
   995  	context string,
   996  	policyOpts policyOptions,
   997  ) policiesCfg {
   998  	config := newPoliciesConfig()
   999  
  1000  	for _, p := range policyRefs {
  1001  		polNamespace := p.Namespace
  1002  		if polNamespace == "" {
  1003  			polNamespace = ownerDetails.ownerNamespace
  1004  		}
  1005  
  1006  		key := fmt.Sprintf("%s/%s", polNamespace, p.Name)
  1007  
  1008  		if pol, exists := policies[key]; exists {
  1009  			var res *validationResults
  1010  			switch {
  1011  			case pol.Spec.AccessControl != nil:
  1012  				res = config.addAccessControlConfig(pol.Spec.AccessControl)
  1013  			case pol.Spec.RateLimit != nil:
  1014  				res = config.addRateLimitConfig(
  1015  					pol.Spec.RateLimit,
  1016  					key,
  1017  					polNamespace,
  1018  					p.Name,
  1019  					ownerDetails.vsNamespace,
  1020  					ownerDetails.vsName,
  1021  				)
  1022  			case pol.Spec.JWTAuth != nil:
  1023  				res = config.addJWTAuthConfig(pol.Spec.JWTAuth, key, polNamespace, policyOpts.secretRefs)
  1024  			case pol.Spec.IngressMTLS != nil:
  1025  				res = config.addIngressMTLSConfig(
  1026  					pol.Spec.IngressMTLS,
  1027  					key,
  1028  					polNamespace,
  1029  					context,
  1030  					policyOpts.tls,
  1031  					policyOpts.secretRefs,
  1032  				)
  1033  			case pol.Spec.EgressMTLS != nil:
  1034  				res = config.addEgressMTLSConfig(pol.Spec.EgressMTLS, key, polNamespace, policyOpts.secretRefs)
  1035  			case pol.Spec.OIDC != nil:
  1036  				res = config.addOIDCConfig(pol.Spec.OIDC, key, polNamespace, policyOpts.secretRefs, vsc.oidcPolCfg)
  1037  			case pol.Spec.WAF != nil:
  1038  				res = config.addWAFConfig(pol.Spec.WAF, key, polNamespace, policyOpts.apResources)
  1039  			default:
  1040  				res = newValidationResults()
  1041  			}
  1042  			vsc.addWarnings(ownerDetails.owner, res.warnings)
  1043  			if res.isError {
  1044  				return policiesCfg{
  1045  					ErrorReturn: &version2.Return{Code: 500},
  1046  				}
  1047  			}
  1048  		} else {
  1049  			vsc.addWarningf(ownerDetails.owner, "Policy %s is missing or invalid", key)
  1050  			return policiesCfg{
  1051  				ErrorReturn: &version2.Return{Code: 500},
  1052  			}
  1053  		}
  1054  	}
  1055  
  1056  	return *config
  1057  }
  1058  
  1059  func generateLimitReq(zoneName string, rateLimitPol *conf_v1.RateLimit) version2.LimitReq {
  1060  	var limitReq version2.LimitReq
  1061  
  1062  	limitReq.ZoneName = zoneName
  1063  
  1064  	if rateLimitPol.Burst != nil {
  1065  		limitReq.Burst = *rateLimitPol.Burst
  1066  	}
  1067  	if rateLimitPol.Delay != nil {
  1068  		limitReq.Delay = *rateLimitPol.Delay
  1069  	}
  1070  
  1071  	limitReq.NoDelay = generateBool(rateLimitPol.NoDelay, false)
  1072  	if limitReq.NoDelay {
  1073  		limitReq.Delay = 0
  1074  	}
  1075  
  1076  	return limitReq
  1077  }
  1078  
  1079  func generateLimitReqZone(zoneName string, rateLimitPol *conf_v1.RateLimit) version2.LimitReqZone {
  1080  	return version2.LimitReqZone{
  1081  		ZoneName: zoneName,
  1082  		Key:      rateLimitPol.Key,
  1083  		ZoneSize: rateLimitPol.ZoneSize,
  1084  		Rate:     rateLimitPol.Rate,
  1085  	}
  1086  }
  1087  
  1088  func generateLimitReqOptions(rateLimitPol *conf_v1.RateLimit) version2.LimitReqOptions {
  1089  	return version2.LimitReqOptions{
  1090  		DryRun:     generateBool(rateLimitPol.DryRun, false),
  1091  		LogLevel:   generateString(rateLimitPol.LogLevel, "error"),
  1092  		RejectCode: generateIntFromPointer(rateLimitPol.RejectCode, 503),
  1093  	}
  1094  }
  1095  
  1096  func removeDuplicateLimitReqZones(rlz []version2.LimitReqZone) []version2.LimitReqZone {
  1097  	encountered := make(map[string]bool)
  1098  	result := []version2.LimitReqZone{}
  1099  
  1100  	for _, v := range rlz {
  1101  		if !encountered[v.ZoneName] {
  1102  			encountered[v.ZoneName] = true
  1103  			result = append(result, v)
  1104  		}
  1105  	}
  1106  
  1107  	return result
  1108  }
  1109  
  1110  func addPoliciesCfgToLocation(cfg policiesCfg, location *version2.Location) {
  1111  	location.Allow = cfg.Allow
  1112  	location.Deny = cfg.Deny
  1113  	location.LimitReqOptions = cfg.LimitReqOptions
  1114  	location.LimitReqs = cfg.LimitReqs
  1115  	location.JWTAuth = cfg.JWTAuth
  1116  	location.EgressMTLS = cfg.EgressMTLS
  1117  	location.OIDC = cfg.OIDC
  1118  	location.WAF = cfg.WAF
  1119  	location.PoliciesErrorReturn = cfg.ErrorReturn
  1120  }
  1121  
  1122  func addPoliciesCfgToLocations(cfg policiesCfg, locations []version2.Location) {
  1123  	for i := range locations {
  1124  		addPoliciesCfgToLocation(cfg, &locations[i])
  1125  	}
  1126  }
  1127  
  1128  func getUpstreamResourceLabels(owner runtime.Object) version2.UpstreamLabels {
  1129  	var resourceType, resourceName, resourceNamespace string
  1130  
  1131  	switch owner := owner.(type) {
  1132  	case *conf_v1.VirtualServer:
  1133  		resourceType = "virtualserver"
  1134  		resourceName = owner.Name
  1135  		resourceNamespace = owner.Namespace
  1136  	case *conf_v1.VirtualServerRoute:
  1137  		resourceType = "virtualserverroute"
  1138  		resourceName = owner.Name
  1139  		resourceNamespace = owner.Namespace
  1140  	}
  1141  
  1142  	return version2.UpstreamLabels{
  1143  		ResourceType:      resourceType,
  1144  		ResourceName:      resourceName,
  1145  		ResourceNamespace: resourceNamespace,
  1146  	}
  1147  }
  1148  
  1149  func (vsc *virtualServerConfigurator) generateUpstream(
  1150  	owner runtime.Object,
  1151  	upstreamName string,
  1152  	upstream conf_v1.Upstream,
  1153  	isExternalNameSvc bool,
  1154  	endpoints []string,
  1155  ) version2.Upstream {
  1156  	var upsServers []version2.UpstreamServer
  1157  	for _, e := range endpoints {
  1158  		s := version2.UpstreamServer{
  1159  			Address: e,
  1160  		}
  1161  		upsServers = append(upsServers, s)
  1162  	}
  1163  
  1164  	lbMethod := generateLBMethod(upstream.LBMethod, vsc.cfgParams.LBMethod)
  1165  
  1166  	upstreamLabels := getUpstreamResourceLabels(owner)
  1167  	upstreamLabels.Service = upstream.Service
  1168  
  1169  	ups := version2.Upstream{
  1170  		Name:             upstreamName,
  1171  		UpstreamLabels:   upstreamLabels,
  1172  		Servers:          upsServers,
  1173  		Resolve:          isExternalNameSvc,
  1174  		LBMethod:         lbMethod,
  1175  		Keepalive:        generateIntFromPointer(upstream.Keepalive, vsc.cfgParams.Keepalive),
  1176  		MaxFails:         generateIntFromPointer(upstream.MaxFails, vsc.cfgParams.MaxFails),
  1177  		FailTimeout:      generateTimeWithDefault(upstream.FailTimeout, vsc.cfgParams.FailTimeout),
  1178  		MaxConns:         generateIntFromPointer(upstream.MaxConns, vsc.cfgParams.MaxConns),
  1179  		UpstreamZoneSize: vsc.cfgParams.UpstreamZoneSize,
  1180  	}
  1181  
  1182  	if vsc.isPlus {
  1183  		ups.SlowStart = vsc.generateSlowStartForPlus(owner, upstream, lbMethod)
  1184  		ups.Queue = generateQueueForPlus(upstream.Queue, "60s")
  1185  		ups.SessionCookie = generateSessionCookie(upstream.SessionCookie)
  1186  	}
  1187  
  1188  	return ups
  1189  }
  1190  
  1191  func (vsc *virtualServerConfigurator) generateSlowStartForPlus(
  1192  	owner runtime.Object,
  1193  	upstream conf_v1.Upstream,
  1194  	lbMethod string,
  1195  ) string {
  1196  	if upstream.SlowStart == "" {
  1197  		return ""
  1198  	}
  1199  
  1200  	_, isIncompatible := incompatibleLBMethodsForSlowStart[lbMethod]
  1201  	isHash := strings.HasPrefix(lbMethod, "hash")
  1202  	if isIncompatible || isHash {
  1203  		msgFmt := "Slow start will be disabled for upstream %v because lb method '%v' is incompatible with slow start"
  1204  		vsc.addWarningf(owner, msgFmt, upstream.Name, lbMethod)
  1205  		return ""
  1206  	}
  1207  
  1208  	return generateTime(upstream.SlowStart)
  1209  }
  1210  
  1211  func generateHealthCheck(
  1212  	upstream conf_v1.Upstream,
  1213  	upstreamName string,
  1214  	cfgParams *ConfigParams,
  1215  ) *version2.HealthCheck {
  1216  	if upstream.HealthCheck == nil || !upstream.HealthCheck.Enable {
  1217  		return nil
  1218  	}
  1219  
  1220  	hc := newHealthCheckWithDefaults(upstream, upstreamName, cfgParams)
  1221  
  1222  	if upstream.HealthCheck.Path != "" {
  1223  		hc.URI = upstream.HealthCheck.Path
  1224  	}
  1225  
  1226  	if upstream.HealthCheck.Interval != "" {
  1227  		hc.Interval = generateTime(upstream.HealthCheck.Interval)
  1228  	}
  1229  
  1230  	if upstream.HealthCheck.Jitter != "" {
  1231  		hc.Jitter = generateTime(upstream.HealthCheck.Jitter)
  1232  	}
  1233  
  1234  	if upstream.HealthCheck.Fails > 0 {
  1235  		hc.Fails = upstream.HealthCheck.Fails
  1236  	}
  1237  
  1238  	if upstream.HealthCheck.Passes > 0 {
  1239  		hc.Passes = upstream.HealthCheck.Passes
  1240  	}
  1241  
  1242  	if upstream.HealthCheck.Port > 0 {
  1243  		hc.Port = upstream.HealthCheck.Port
  1244  	}
  1245  
  1246  	if upstream.HealthCheck.ConnectTimeout != "" {
  1247  		hc.ProxyConnectTimeout = generateTime(upstream.HealthCheck.ConnectTimeout)
  1248  	}
  1249  
  1250  	if upstream.HealthCheck.ReadTimeout != "" {
  1251  		hc.ProxyReadTimeout = generateTime(upstream.HealthCheck.ReadTimeout)
  1252  	}
  1253  
  1254  	if upstream.HealthCheck.SendTimeout != "" {
  1255  		hc.ProxySendTimeout = generateTime(upstream.HealthCheck.SendTimeout)
  1256  	}
  1257  
  1258  	for _, h := range upstream.HealthCheck.Headers {
  1259  		hc.Headers[h.Name] = h.Value
  1260  	}
  1261  
  1262  	if upstream.HealthCheck.TLS != nil {
  1263  		hc.ProxyPass = fmt.Sprintf("%v://%v", generateProxyPassProtocol(upstream.HealthCheck.TLS.Enable), upstreamName)
  1264  	}
  1265  
  1266  	if upstream.HealthCheck.StatusMatch != "" {
  1267  		hc.Match = generateStatusMatchName(upstreamName)
  1268  	}
  1269  
  1270  	return hc
  1271  }
  1272  
  1273  func generateSessionCookie(sc *conf_v1.SessionCookie) *version2.SessionCookie {
  1274  	if sc == nil || !sc.Enable {
  1275  		return nil
  1276  	}
  1277  
  1278  	return &version2.SessionCookie{
  1279  		Enable:   true,
  1280  		Name:     sc.Name,
  1281  		Path:     sc.Path,
  1282  		Expires:  sc.Expires,
  1283  		Domain:   sc.Domain,
  1284  		HTTPOnly: sc.HTTPOnly,
  1285  		Secure:   sc.Secure,
  1286  	}
  1287  }
  1288  
  1289  func generateStatusMatchName(upstreamName string) string {
  1290  	return fmt.Sprintf("%s_match", upstreamName)
  1291  }
  1292  
  1293  func generateUpstreamStatusMatch(upstreamName string, status string) version2.StatusMatch {
  1294  	return version2.StatusMatch{
  1295  		Name: generateStatusMatchName(upstreamName),
  1296  		Code: status,
  1297  	}
  1298  }
  1299  
  1300  // GenerateExternalNameSvcKey returns the key to identify an ExternalName service.
  1301  func GenerateExternalNameSvcKey(namespace string, service string) string {
  1302  	return fmt.Sprintf("%v/%v", namespace, service)
  1303  }
  1304  
  1305  func generateLBMethod(method string, defaultMethod string) string {
  1306  	if method == "" {
  1307  		return defaultMethod
  1308  	} else if method == "round_robin" {
  1309  		return ""
  1310  	}
  1311  	return method
  1312  }
  1313  
  1314  func generateIntFromPointer(n *int, defaultN int) int {
  1315  	if n == nil {
  1316  		return defaultN
  1317  	}
  1318  	return *n
  1319  }
  1320  
  1321  func upstreamHasKeepalive(upstream conf_v1.Upstream, cfgParams *ConfigParams) bool {
  1322  	if upstream.Keepalive != nil {
  1323  		return *upstream.Keepalive != 0
  1324  	}
  1325  	return cfgParams.Keepalive != 0
  1326  }
  1327  
  1328  func generateRewrites(path string, proxy *conf_v1.ActionProxy, internal bool, originalPath string) []string {
  1329  	if proxy == nil || proxy.RewritePath == "" {
  1330  		return nil
  1331  	}
  1332  
  1333  	if originalPath != "" {
  1334  		path = originalPath
  1335  	}
  1336  
  1337  	isRegex := false
  1338  	if strings.HasPrefix(path, "~") {
  1339  		isRegex = true
  1340  	}
  1341  
  1342  	trimmedPath := strings.TrimPrefix(strings.TrimPrefix(path, "~"), "*")
  1343  	trimmedPath = strings.TrimSpace(trimmedPath)
  1344  
  1345  	var rewrites []string
  1346  
  1347  	if internal {
  1348  		// For internal locations (splits locations) only, recover the original request_uri.
  1349  		rewrites = append(rewrites, "^ $request_uri")
  1350  	}
  1351  
  1352  	if isRegex {
  1353  		rewrites = append(rewrites, fmt.Sprintf(`"^%v" "%v" break`, trimmedPath, proxy.RewritePath))
  1354  	} else if internal {
  1355  		rewrites = append(rewrites, fmt.Sprintf(`"^%v(.*)$" "%v$1" break`, trimmedPath, proxy.RewritePath))
  1356  	}
  1357  
  1358  	return rewrites
  1359  }
  1360  
  1361  func generateProxyPassRewrite(path string, proxy *conf_v1.ActionProxy, internal bool) string {
  1362  	if proxy == nil || internal {
  1363  		return ""
  1364  	}
  1365  
  1366  	if strings.HasPrefix(path, "/") || strings.HasPrefix(path, "=") {
  1367  		return proxy.RewritePath
  1368  	}
  1369  
  1370  	return ""
  1371  }
  1372  
  1373  func generateProxyPass(tlsEnabled bool, upstreamName string, internal bool, proxy *conf_v1.ActionProxy) string {
  1374  	proxyPass := fmt.Sprintf("%v://%v", generateProxyPassProtocol(tlsEnabled), upstreamName)
  1375  
  1376  	if internal && (proxy == nil || proxy.RewritePath == "") {
  1377  		return fmt.Sprintf("%v$request_uri", proxyPass)
  1378  	}
  1379  
  1380  	return proxyPass
  1381  }
  1382  
  1383  func generateProxyPassProtocol(enableTLS bool) string {
  1384  	if enableTLS {
  1385  		return "https"
  1386  	}
  1387  	return "http"
  1388  }
  1389  
  1390  func generateString(s string, defaultS string) string {
  1391  	if s == "" {
  1392  		return defaultS
  1393  	}
  1394  	return s
  1395  }
  1396  
  1397  func generateTime(value string) string {
  1398  	// it is expected that the value has been validated prior to call generateTime
  1399  	parsed, _ := ParseTime(value)
  1400  	return parsed
  1401  }
  1402  
  1403  func generateTimeWithDefault(value string, defaultValue string) string {
  1404  	if value == "" {
  1405  		// we don't transform the default value yet
  1406  		// this is done for backward compatibility, as the time values in the ConfigMap are not validated yet
  1407  		return defaultValue
  1408  	}
  1409  
  1410  	return generateTime(value)
  1411  }
  1412  
  1413  func generateSnippets(enableSnippets bool, snippet string, defaultSnippets []string) []string {
  1414  	if !enableSnippets || snippet == "" {
  1415  		return defaultSnippets
  1416  	}
  1417  	return strings.Split(snippet, "\n")
  1418  }
  1419  
  1420  func generateBuffers(s *conf_v1.UpstreamBuffers, defaultS string) string {
  1421  	if s == nil {
  1422  		return defaultS
  1423  	}
  1424  	return fmt.Sprintf("%v %v", s.Number, s.Size)
  1425  }
  1426  
  1427  func generateBool(s *bool, defaultS bool) bool {
  1428  	if s != nil {
  1429  		return *s
  1430  	}
  1431  	return defaultS
  1432  }
  1433  
  1434  func generatePath(path string) string {
  1435  	// Wrap the regular expression (if present) inside double quotes (") to avoid NGINX parsing errors
  1436  	if strings.HasPrefix(path, "~*") {
  1437  		return fmt.Sprintf(`~* "%v"`, strings.TrimPrefix(strings.TrimPrefix(path, "~*"), " "))
  1438  	}
  1439  	if strings.HasPrefix(path, "~") {
  1440  		return fmt.Sprintf(`~ "%v"`, strings.TrimPrefix(strings.TrimPrefix(path, "~"), " "))
  1441  	}
  1442  
  1443  	return path
  1444  }
  1445  
  1446  func generateReturnBlock(text string, code int, defaultCode int) *version2.Return {
  1447  	returnBlock := &version2.Return{
  1448  		Code: defaultCode,
  1449  		Text: text,
  1450  	}
  1451  
  1452  	if code != 0 {
  1453  		returnBlock.Code = code
  1454  	}
  1455  
  1456  	return returnBlock
  1457  }
  1458  
  1459  func generateLocation(path string, upstreamName string, upstream conf_v1.Upstream, action *conf_v1.Action,
  1460  	cfgParams *ConfigParams, errorPages []conf_v1.ErrorPage, internal bool, errPageIndex int, proxySSLName string,
  1461  	originalPath string, locSnippets string, enableSnippets bool, retLocIndex int, isVSR bool, vsrName string, vsrNamespace string) (version2.Location, *version2.ReturnLocation) {
  1462  	locationSnippets := generateSnippets(enableSnippets, locSnippets, cfgParams.LocationSnippets)
  1463  
  1464  	if action.Redirect != nil {
  1465  		return generateLocationForRedirect(path, locationSnippets, action.Redirect), nil
  1466  	}
  1467  
  1468  	if action.Return != nil {
  1469  		return generateLocationForReturn(path, cfgParams.LocationSnippets, action.Return, retLocIndex)
  1470  	}
  1471  
  1472  	return generateLocationForProxying(path, upstreamName, upstream, cfgParams, errorPages, internal,
  1473  		errPageIndex, proxySSLName, action.Proxy, originalPath, locationSnippets, isVSR, vsrName, vsrNamespace), nil
  1474  }
  1475  
  1476  func generateProxySetHeaders(proxy *conf_v1.ActionProxy) []version2.Header {
  1477  	var headers []version2.Header
  1478  
  1479  	hasHostHeader := false
  1480  
  1481  	if proxy != nil && proxy.RequestHeaders != nil {
  1482  		for _, h := range proxy.RequestHeaders.Set {
  1483  			headers = append(headers, version2.Header{
  1484  				Name:  h.Name,
  1485  				Value: h.Value,
  1486  			})
  1487  
  1488  			if strings.ToLower(h.Name) == "host" {
  1489  				hasHostHeader = true
  1490  			}
  1491  		}
  1492  	}
  1493  
  1494  	if !hasHostHeader {
  1495  		headers = append(headers, version2.Header{Name: "Host", Value: "$host"})
  1496  	}
  1497  
  1498  	return headers
  1499  }
  1500  
  1501  func generateProxyPassRequestHeaders(proxy *conf_v1.ActionProxy) bool {
  1502  	if proxy == nil || proxy.RequestHeaders == nil {
  1503  		return true
  1504  	}
  1505  
  1506  	if proxy.RequestHeaders.Pass != nil {
  1507  		return *proxy.RequestHeaders.Pass
  1508  	}
  1509  
  1510  	return true
  1511  }
  1512  
  1513  func generateProxyHideHeaders(proxy *conf_v1.ActionProxy) []string {
  1514  	if proxy == nil || proxy.ResponseHeaders == nil {
  1515  		return nil
  1516  	}
  1517  
  1518  	return proxy.ResponseHeaders.Hide
  1519  }
  1520  
  1521  func generateProxyPassHeaders(proxy *conf_v1.ActionProxy) []string {
  1522  	if proxy == nil || proxy.ResponseHeaders == nil {
  1523  		return nil
  1524  	}
  1525  
  1526  	return proxy.ResponseHeaders.Pass
  1527  }
  1528  
  1529  func generateProxyIgnoreHeaders(proxy *conf_v1.ActionProxy) string {
  1530  	if proxy == nil || proxy.ResponseHeaders == nil {
  1531  		return ""
  1532  	}
  1533  
  1534  	return strings.Join(proxy.ResponseHeaders.Ignore, " ")
  1535  }
  1536  
  1537  func generateProxyAddHeaders(proxy *conf_v1.ActionProxy) []version2.AddHeader {
  1538  	if proxy == nil || proxy.ResponseHeaders == nil {
  1539  		return nil
  1540  	}
  1541  
  1542  	var addHeaders []version2.AddHeader
  1543  	for _, h := range proxy.ResponseHeaders.Add {
  1544  		addHeaders = append(addHeaders, version2.AddHeader{
  1545  			Header: version2.Header{
  1546  				Name:  h.Name,
  1547  				Value: h.Value,
  1548  			},
  1549  			Always: h.Always,
  1550  		})
  1551  	}
  1552  
  1553  	return addHeaders
  1554  }
  1555  
  1556  func generateLocationForProxying(path string, upstreamName string, upstream conf_v1.Upstream,
  1557  	cfgParams *ConfigParams, errorPages []conf_v1.ErrorPage, internal bool, errPageIndex int,
  1558  	proxySSLName string, proxy *conf_v1.ActionProxy, originalPath string, locationSnippets []string, isVSR bool, vsrName string, vsrNamespace string) version2.Location {
  1559  	return version2.Location{
  1560  		Path:                     generatePath(path),
  1561  		Internal:                 internal,
  1562  		Snippets:                 locationSnippets,
  1563  		ProxyConnectTimeout:      generateTimeWithDefault(upstream.ProxyConnectTimeout, cfgParams.ProxyConnectTimeout),
  1564  		ProxyReadTimeout:         generateTimeWithDefault(upstream.ProxyReadTimeout, cfgParams.ProxyReadTimeout),
  1565  		ProxySendTimeout:         generateTimeWithDefault(upstream.ProxySendTimeout, cfgParams.ProxySendTimeout),
  1566  		ClientMaxBodySize:        generateString(upstream.ClientMaxBodySize, cfgParams.ClientMaxBodySize),
  1567  		ProxyMaxTempFileSize:     cfgParams.ProxyMaxTempFileSize,
  1568  		ProxyBuffering:           generateBool(upstream.ProxyBuffering, cfgParams.ProxyBuffering),
  1569  		ProxyBuffers:             generateBuffers(upstream.ProxyBuffers, cfgParams.ProxyBuffers),
  1570  		ProxyBufferSize:          generateString(upstream.ProxyBufferSize, cfgParams.ProxyBufferSize),
  1571  		ProxyPass:                generateProxyPass(upstream.TLS.Enable, upstreamName, internal, proxy),
  1572  		ProxyNextUpstream:        generateString(upstream.ProxyNextUpstream, "error timeout"),
  1573  		ProxyNextUpstreamTimeout: generateTimeWithDefault(upstream.ProxyNextUpstreamTimeout, "0s"),
  1574  		ProxyNextUpstreamTries:   upstream.ProxyNextUpstreamTries,
  1575  		ProxyInterceptErrors:     generateProxyInterceptErrors(errorPages),
  1576  		ProxyPassRequestHeaders:  generateProxyPassRequestHeaders(proxy),
  1577  		ProxySetHeaders:          generateProxySetHeaders(proxy),
  1578  		ProxyHideHeaders:         generateProxyHideHeaders(proxy),
  1579  		ProxyPassHeaders:         generateProxyPassHeaders(proxy),
  1580  		ProxyIgnoreHeaders:       generateProxyIgnoreHeaders(proxy),
  1581  		AddHeaders:               generateProxyAddHeaders(proxy),
  1582  		ProxyPassRewrite:         generateProxyPassRewrite(path, proxy, internal),
  1583  		Rewrites:                 generateRewrites(path, proxy, internal, originalPath),
  1584  		HasKeepalive:             upstreamHasKeepalive(upstream, cfgParams),
  1585  		ErrorPages:               generateErrorPages(errPageIndex, errorPages),
  1586  		ProxySSLName:             proxySSLName,
  1587  		ServiceName:              upstream.Service,
  1588  		IsVSR:                    isVSR,
  1589  		VSRName:                  vsrName,
  1590  		VSRNamespace:             vsrNamespace,
  1591  	}
  1592  }
  1593  
  1594  func generateProxyInterceptErrors(errorPages []conf_v1.ErrorPage) bool {
  1595  	return len(errorPages) > 0
  1596  }
  1597  
  1598  func generateLocationForRedirect(
  1599  	path string,
  1600  	locationSnippets []string,
  1601  	redirect *conf_v1.ActionRedirect,
  1602  ) version2.Location {
  1603  	code := redirect.Code
  1604  	if code == 0 {
  1605  		code = 301
  1606  	}
  1607  
  1608  	return version2.Location{
  1609  		Path:                 path,
  1610  		Snippets:             locationSnippets,
  1611  		ProxyInterceptErrors: true,
  1612  		InternalProxyPass:    fmt.Sprintf("http://%s", nginx418Server),
  1613  		ErrorPages: []version2.ErrorPage{
  1614  			{
  1615  				Name:         redirect.URL,
  1616  				Codes:        "418",
  1617  				ResponseCode: code,
  1618  			},
  1619  		},
  1620  	}
  1621  }
  1622  
  1623  func generateLocationForReturn(path string, locationSnippets []string, actionReturn *conf_v1.ActionReturn,
  1624  	retLocIndex int) (version2.Location, *version2.ReturnLocation) {
  1625  	defaultType := actionReturn.Type
  1626  	if defaultType == "" {
  1627  		defaultType = "text/plain"
  1628  	}
  1629  	code := actionReturn.Code
  1630  	if code == 0 {
  1631  		code = 200
  1632  	}
  1633  
  1634  	retLocName := fmt.Sprintf("@return_%d", retLocIndex)
  1635  
  1636  	return version2.Location{
  1637  			Path:                 path,
  1638  			Snippets:             locationSnippets,
  1639  			ProxyInterceptErrors: true,
  1640  			InternalProxyPass:    fmt.Sprintf("http://%s", nginx418Server),
  1641  			ErrorPages: []version2.ErrorPage{
  1642  				{
  1643  					Name:         retLocName,
  1644  					Codes:        "418",
  1645  					ResponseCode: code,
  1646  				},
  1647  			},
  1648  		},
  1649  		&version2.ReturnLocation{
  1650  			Name:        retLocName,
  1651  			DefaultType: defaultType,
  1652  			Return: version2.Return{
  1653  				Text: actionReturn.Body,
  1654  			},
  1655  		}
  1656  }
  1657  
  1658  type routingCfg struct {
  1659  	Maps                     []version2.Map
  1660  	SplitClients             []version2.SplitClient
  1661  	Locations                []version2.Location
  1662  	InternalRedirectLocation version2.InternalRedirectLocation
  1663  	ReturnLocations          []version2.ReturnLocation
  1664  }
  1665  
  1666  func generateSplits(
  1667  	splits []conf_v1.Split,
  1668  	upstreamNamer *upstreamNamer,
  1669  	crUpstreams map[string]conf_v1.Upstream,
  1670  	variableNamer *variableNamer,
  1671  	scIndex int,
  1672  	cfgParams *ConfigParams,
  1673  	errorPages []conf_v1.ErrorPage,
  1674  	errPageIndex int,
  1675  	originalPath string,
  1676  	locSnippets string,
  1677  	enableSnippets bool,
  1678  	retLocIndex int,
  1679  	isVSR bool,
  1680  	vsrName string,
  1681  	vsrNamespace string,
  1682  ) (version2.SplitClient, []version2.Location, []version2.ReturnLocation) {
  1683  	var distributions []version2.Distribution
  1684  
  1685  	for i, s := range splits {
  1686  		d := version2.Distribution{
  1687  			Weight: fmt.Sprintf("%d%%", s.Weight),
  1688  			Value:  fmt.Sprintf("/%vsplits_%d_split_%d", internalLocationPrefix, scIndex, i),
  1689  		}
  1690  		distributions = append(distributions, d)
  1691  	}
  1692  
  1693  	splitClient := version2.SplitClient{
  1694  		Source:        "$request_id",
  1695  		Variable:      variableNamer.GetNameForSplitClientVariable(scIndex),
  1696  		Distributions: distributions,
  1697  	}
  1698  
  1699  	var locations []version2.Location
  1700  	var returnLocations []version2.ReturnLocation
  1701  
  1702  	for i, s := range splits {
  1703  		path := fmt.Sprintf("/%vsplits_%d_split_%d", internalLocationPrefix, scIndex, i)
  1704  		upstreamName := upstreamNamer.GetNameForUpstreamFromAction(s.Action)
  1705  		upstream := crUpstreams[upstreamName]
  1706  		proxySSLName := generateProxySSLName(upstream.Service, upstreamNamer.namespace)
  1707  		newRetLocIndex := retLocIndex + len(returnLocations)
  1708  		loc, returnLoc := generateLocation(path, upstreamName, upstream, s.Action, cfgParams, errorPages, true,
  1709  			errPageIndex, proxySSLName, originalPath, locSnippets, enableSnippets, newRetLocIndex, isVSR, vsrName, vsrNamespace)
  1710  		locations = append(locations, loc)
  1711  		if returnLoc != nil {
  1712  			returnLocations = append(returnLocations, *returnLoc)
  1713  		}
  1714  	}
  1715  
  1716  	return splitClient, locations, returnLocations
  1717  }
  1718  
  1719  func generateDefaultSplitsConfig(
  1720  	route conf_v1.Route,
  1721  	upstreamNamer *upstreamNamer,
  1722  	crUpstreams map[string]conf_v1.Upstream,
  1723  	variableNamer *variableNamer,
  1724  	scIndex int,
  1725  	cfgParams *ConfigParams,
  1726  	errorPages []conf_v1.ErrorPage,
  1727  	errPageIndex int,
  1728  	originalPath string,
  1729  	locSnippets string,
  1730  	enableSnippets bool,
  1731  	retLocIndex int,
  1732  	isVSR bool,
  1733  	vsrName string,
  1734  	vsrNamespace string,
  1735  ) routingCfg {
  1736  	sc, locs, returnLocs := generateSplits(route.Splits, upstreamNamer, crUpstreams, variableNamer, scIndex, cfgParams,
  1737  		errorPages, errPageIndex, originalPath, locSnippets, enableSnippets, retLocIndex, isVSR, vsrName, vsrNamespace)
  1738  
  1739  	splitClientVarName := variableNamer.GetNameForSplitClientVariable(scIndex)
  1740  
  1741  	irl := version2.InternalRedirectLocation{
  1742  		Path:        route.Path,
  1743  		Destination: splitClientVarName,
  1744  	}
  1745  
  1746  	return routingCfg{
  1747  		SplitClients:             []version2.SplitClient{sc},
  1748  		Locations:                locs,
  1749  		InternalRedirectLocation: irl,
  1750  		ReturnLocations:          returnLocs,
  1751  	}
  1752  }
  1753  
  1754  func generateMatchesConfig(route conf_v1.Route, upstreamNamer *upstreamNamer, crUpstreams map[string]conf_v1.Upstream,
  1755  	variableNamer *variableNamer, index int, scIndex int, cfgParams *ConfigParams, errorPages []conf_v1.ErrorPage,
  1756  	errPageIndex int, locSnippets string, enableSnippets bool, retLocIndex int, isVSR bool, vsrName string, vsrNamespace string) routingCfg {
  1757  	// Generate maps
  1758  	var maps []version2.Map
  1759  
  1760  	for i, m := range route.Matches {
  1761  		for j, c := range m.Conditions {
  1762  			source := getNameForSourceForMatchesRouteMapFromCondition(c)
  1763  			variable := variableNamer.GetNameForVariableForMatchesRouteMap(index, i, j)
  1764  			successfulResult := "1"
  1765  			if j < len(m.Conditions)-1 {
  1766  				successfulResult = variableNamer.GetNameForVariableForMatchesRouteMap(index, i, j+1)
  1767  			}
  1768  
  1769  			params := generateParametersForMatchesRouteMap(c.Value, successfulResult)
  1770  
  1771  			matchMap := version2.Map{
  1772  				Source:     source,
  1773  				Variable:   variable,
  1774  				Parameters: params,
  1775  			}
  1776  			maps = append(maps, matchMap)
  1777  		}
  1778  	}
  1779  
  1780  	scLocalIndex := 0
  1781  
  1782  	// Generate the main map
  1783  	source := ""
  1784  	var params []version2.Parameter
  1785  	for i, m := range route.Matches {
  1786  		source += variableNamer.GetNameForVariableForMatchesRouteMap(index, i, 0)
  1787  
  1788  		v := fmt.Sprintf("~^%s1", strings.Repeat("0", i))
  1789  		r := fmt.Sprintf("/%vmatches_%d_match_%d", internalLocationPrefix, index, i)
  1790  		if len(m.Splits) > 0 {
  1791  			r = variableNamer.GetNameForSplitClientVariable(scIndex + scLocalIndex)
  1792  			scLocalIndex++
  1793  		}
  1794  
  1795  		p := version2.Parameter{
  1796  			Value:  v,
  1797  			Result: r,
  1798  		}
  1799  		params = append(params, p)
  1800  	}
  1801  
  1802  	defaultResult := fmt.Sprintf("/%vmatches_%d_default", internalLocationPrefix, index)
  1803  	if len(route.Splits) > 0 {
  1804  		defaultResult = variableNamer.GetNameForSplitClientVariable(scIndex + scLocalIndex)
  1805  	}
  1806  
  1807  	defaultParam := version2.Parameter{
  1808  		Value:  "default",
  1809  		Result: defaultResult,
  1810  	}
  1811  	params = append(params, defaultParam)
  1812  
  1813  	variable := variableNamer.GetNameForVariableForMatchesRouteMainMap(index)
  1814  
  1815  	mainMap := version2.Map{
  1816  		Source:     source,
  1817  		Variable:   variable,
  1818  		Parameters: params,
  1819  	}
  1820  	maps = append(maps, mainMap)
  1821  
  1822  	// Generate locations for each match and split client
  1823  	var locations []version2.Location
  1824  	var returnLocations []version2.ReturnLocation
  1825  	var splitClients []version2.SplitClient
  1826  	scLocalIndex = 0
  1827  
  1828  	for i, m := range route.Matches {
  1829  		if len(m.Splits) > 0 {
  1830  			newRetLocIndex := retLocIndex + len(returnLocations)
  1831  			sc, locs, returnLocs := generateSplits(
  1832  				m.Splits,
  1833  				upstreamNamer,
  1834  				crUpstreams,
  1835  				variableNamer,
  1836  				scIndex+scLocalIndex,
  1837  				cfgParams,
  1838  				errorPages,
  1839  				errPageIndex,
  1840  				route.Path,
  1841  				locSnippets,
  1842  				enableSnippets,
  1843  				newRetLocIndex,
  1844  				isVSR,
  1845  				vsrName,
  1846  				vsrNamespace,
  1847  			)
  1848  			scLocalIndex++
  1849  			splitClients = append(splitClients, sc)
  1850  			locations = append(locations, locs...)
  1851  			returnLocations = append(returnLocations, returnLocs...)
  1852  		} else {
  1853  			path := fmt.Sprintf("/%vmatches_%d_match_%d", internalLocationPrefix, index, i)
  1854  			upstreamName := upstreamNamer.GetNameForUpstreamFromAction(m.Action)
  1855  			upstream := crUpstreams[upstreamName]
  1856  			proxySSLName := generateProxySSLName(upstream.Service, upstreamNamer.namespace)
  1857  			newRetLocIndex := retLocIndex + len(returnLocations)
  1858  			loc, returnLoc := generateLocation(path, upstreamName, upstream, m.Action, cfgParams, errorPages, true,
  1859  				errPageIndex, proxySSLName, route.Path, locSnippets, enableSnippets, newRetLocIndex, isVSR, vsrName, vsrNamespace)
  1860  			locations = append(locations, loc)
  1861  			if returnLoc != nil {
  1862  				returnLocations = append(returnLocations, *returnLoc)
  1863  			}
  1864  		}
  1865  	}
  1866  
  1867  	// Generate default splits or default action
  1868  	if len(route.Splits) > 0 {
  1869  		newRetLocIndex := retLocIndex + len(returnLocations)
  1870  		sc, locs, returnLocs := generateSplits(
  1871  			route.Splits,
  1872  			upstreamNamer,
  1873  			crUpstreams,
  1874  			variableNamer,
  1875  			scIndex+scLocalIndex,
  1876  			cfgParams,
  1877  			errorPages,
  1878  			errPageIndex,
  1879  			route.Path,
  1880  			locSnippets,
  1881  			enableSnippets,
  1882  			newRetLocIndex,
  1883  			isVSR,
  1884  			vsrName,
  1885  			vsrNamespace,
  1886  		)
  1887  		splitClients = append(splitClients, sc)
  1888  		locations = append(locations, locs...)
  1889  		returnLocations = append(returnLocations, returnLocs...)
  1890  	} else {
  1891  		path := fmt.Sprintf("/%vmatches_%d_default", internalLocationPrefix, index)
  1892  		upstreamName := upstreamNamer.GetNameForUpstreamFromAction(route.Action)
  1893  		upstream := crUpstreams[upstreamName]
  1894  		proxySSLName := generateProxySSLName(upstream.Service, upstreamNamer.namespace)
  1895  		newRetLocIndex := retLocIndex + len(returnLocations)
  1896  		loc, returnLoc := generateLocation(path, upstreamName, upstream, route.Action, cfgParams, errorPages, true,
  1897  			errPageIndex, proxySSLName, route.Path, locSnippets, enableSnippets, newRetLocIndex, isVSR, vsrName, vsrNamespace)
  1898  		locations = append(locations, loc)
  1899  		if returnLoc != nil {
  1900  			returnLocations = append(returnLocations, *returnLoc)
  1901  		}
  1902  	}
  1903  
  1904  	// Generate an InternalRedirectLocation to the location defined by the main map variable
  1905  	irl := version2.InternalRedirectLocation{
  1906  		Path:        route.Path,
  1907  		Destination: variable,
  1908  	}
  1909  
  1910  	return routingCfg{
  1911  		Maps:                     maps,
  1912  		Locations:                locations,
  1913  		InternalRedirectLocation: irl,
  1914  		SplitClients:             splitClients,
  1915  		ReturnLocations:          returnLocations,
  1916  	}
  1917  }
  1918  
  1919  var specialMapParameters = map[string]bool{
  1920  	"default":   true,
  1921  	"hostnames": true,
  1922  	"include":   true,
  1923  	"volatile":  true,
  1924  }
  1925  
  1926  func generateValueForMatchesRouteMap(matchedValue string) (value string, isNegative bool) {
  1927  	if len(matchedValue) == 0 {
  1928  		return `""`, false
  1929  	}
  1930  
  1931  	if matchedValue[0] == '!' {
  1932  		isNegative = true
  1933  		matchedValue = matchedValue[1:]
  1934  	}
  1935  
  1936  	if _, exists := specialMapParameters[matchedValue]; exists {
  1937  		return `\` + matchedValue, isNegative
  1938  	}
  1939  
  1940  	return fmt.Sprintf(`"%s"`, matchedValue), isNegative
  1941  }
  1942  
  1943  func generateParametersForMatchesRouteMap(matchedValue string, successfulResult string) []version2.Parameter {
  1944  	value, isNegative := generateValueForMatchesRouteMap(matchedValue)
  1945  
  1946  	valueResult := successfulResult
  1947  	defaultResult := "0"
  1948  	if isNegative {
  1949  		valueResult = "0"
  1950  		defaultResult = successfulResult
  1951  	}
  1952  
  1953  	params := []version2.Parameter{
  1954  		{
  1955  			Value:  value,
  1956  			Result: valueResult,
  1957  		},
  1958  		{
  1959  			Value:  "default",
  1960  			Result: defaultResult,
  1961  		},
  1962  	}
  1963  
  1964  	return params
  1965  }
  1966  
  1967  func getNameForSourceForMatchesRouteMapFromCondition(condition conf_v1.Condition) string {
  1968  	if condition.Header != "" {
  1969  		return fmt.Sprintf("$http_%s", strings.ReplaceAll(condition.Header, "-", "_"))
  1970  	}
  1971  
  1972  	if condition.Cookie != "" {
  1973  		return fmt.Sprintf("$cookie_%s", condition.Cookie)
  1974  	}
  1975  
  1976  	if condition.Argument != "" {
  1977  		return fmt.Sprintf("$arg_%s", condition.Argument)
  1978  	}
  1979  
  1980  	return condition.Variable
  1981  }
  1982  
  1983  func (vsc *virtualServerConfigurator) generateSSLConfig(owner runtime.Object, tls *conf_v1.TLS, namespace string,
  1984  	secretRefs map[string]*secrets.SecretReference, cfgParams *ConfigParams) *version2.SSL {
  1985  	if tls == nil {
  1986  		return nil
  1987  	}
  1988  
  1989  	if tls.Secret == "" {
  1990  		return nil
  1991  	}
  1992  
  1993  	secretRef := secretRefs[fmt.Sprintf("%s/%s", namespace, tls.Secret)]
  1994  	var secretType api_v1.SecretType
  1995  	if secretRef.Secret != nil {
  1996  		secretType = secretRef.Secret.Type
  1997  	}
  1998  	var name string
  1999  	var rejectHandshake bool
  2000  	if secretType != "" && secretType != api_v1.SecretTypeTLS {
  2001  		rejectHandshake = true
  2002  		vsc.addWarningf(owner, "TLS secret %s is of a wrong type '%s', must be '%s'", tls.Secret, secretType, api_v1.SecretTypeTLS)
  2003  	} else if secretRef.Error != nil {
  2004  		rejectHandshake = true
  2005  		vsc.addWarningf(owner, "TLS secret %s is invalid: %v", tls.Secret, secretRef.Error)
  2006  	} else {
  2007  		name = secretRef.Path
  2008  	}
  2009  
  2010  	ssl := version2.SSL{
  2011  		HTTP2:           cfgParams.HTTP2,
  2012  		Certificate:     name,
  2013  		CertificateKey:  name,
  2014  		RejectHandshake: rejectHandshake,
  2015  	}
  2016  
  2017  	return &ssl
  2018  }
  2019  
  2020  func generateTLSRedirectConfig(tls *conf_v1.TLS) *version2.TLSRedirect {
  2021  	if tls == nil || tls.Redirect == nil || !tls.Redirect.Enable {
  2022  		return nil
  2023  	}
  2024  
  2025  	redirect := &version2.TLSRedirect{
  2026  		Code:    generateIntFromPointer(tls.Redirect.Code, 301),
  2027  		BasedOn: generateTLSRedirectBasedOn(tls.Redirect.BasedOn),
  2028  	}
  2029  
  2030  	return redirect
  2031  }
  2032  
  2033  func generateTLSRedirectBasedOn(basedOn string) string {
  2034  	if basedOn == "x-forwarded-proto" {
  2035  		return "$http_x_forwarded_proto"
  2036  	}
  2037  	return "$scheme"
  2038  }
  2039  
  2040  func createEndpointsFromUpstream(upstream version2.Upstream) []string {
  2041  	var endpoints []string
  2042  
  2043  	for _, server := range upstream.Servers {
  2044  		endpoints = append(endpoints, server.Address)
  2045  	}
  2046  
  2047  	return endpoints
  2048  }
  2049  
  2050  func createUpstreamsForPlus(
  2051  	virtualServerEx *VirtualServerEx,
  2052  	baseCfgParams *ConfigParams,
  2053  	staticParams *StaticConfigParams,
  2054  ) []version2.Upstream {
  2055  	var upstreams []version2.Upstream
  2056  
  2057  	isPlus := true
  2058  	upstreamNamer := newUpstreamNamerForVirtualServer(virtualServerEx.VirtualServer)
  2059  	vsc := newVirtualServerConfigurator(baseCfgParams, isPlus, false, staticParams)
  2060  
  2061  	for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams {
  2062  		isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(virtualServerEx.VirtualServer.Namespace, u.Service)]
  2063  		if isExternalNameSvc {
  2064  			glog.V(3).Infof("Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API", u.Service)
  2065  			continue
  2066  		}
  2067  
  2068  		upstreamName := upstreamNamer.GetNameForUpstream(u.Name)
  2069  		upstreamNamespace := virtualServerEx.VirtualServer.Namespace
  2070  
  2071  		endpointsKey := GenerateEndpointsKey(upstreamNamespace, u.Service, u.Subselector, u.Port)
  2072  		endpoints := virtualServerEx.Endpoints[endpointsKey]
  2073  
  2074  		ups := vsc.generateUpstream(virtualServerEx.VirtualServer, upstreamName, u, isExternalNameSvc, endpoints)
  2075  		upstreams = append(upstreams, ups)
  2076  	}
  2077  
  2078  	for _, vsr := range virtualServerEx.VirtualServerRoutes {
  2079  		upstreamNamer = newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr)
  2080  		for _, u := range vsr.Spec.Upstreams {
  2081  			isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(vsr.Namespace, u.Service)]
  2082  			if isExternalNameSvc {
  2083  				glog.V(
  2084  					3,
  2085  				).Infof(
  2086  					"Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API",
  2087  					u.Service,
  2088  				)
  2089  				continue
  2090  			}
  2091  
  2092  			upstreamName := upstreamNamer.GetNameForUpstream(u.Name)
  2093  			upstreamNamespace := vsr.Namespace
  2094  
  2095  			endpointsKey := GenerateEndpointsKey(upstreamNamespace, u.Service, u.Subselector, u.Port)
  2096  			endpoints := virtualServerEx.Endpoints[endpointsKey]
  2097  
  2098  			ups := vsc.generateUpstream(vsr, upstreamName, u, isExternalNameSvc, endpoints)
  2099  			upstreams = append(upstreams, ups)
  2100  		}
  2101  	}
  2102  
  2103  	return upstreams
  2104  }
  2105  
  2106  func createUpstreamServersConfigForPlus(upstream version2.Upstream) nginx.ServerConfig {
  2107  	if len(upstream.Servers) == 0 {
  2108  		return nginx.ServerConfig{}
  2109  	}
  2110  	return nginx.ServerConfig{
  2111  		MaxFails:    upstream.MaxFails,
  2112  		FailTimeout: upstream.FailTimeout,
  2113  		MaxConns:    upstream.MaxConns,
  2114  		SlowStart:   upstream.SlowStart,
  2115  	}
  2116  }
  2117  
  2118  func generateQueueForPlus(upstreamQueue *conf_v1.UpstreamQueue, defaultTimeout string) *version2.Queue {
  2119  	if upstreamQueue == nil {
  2120  		return nil
  2121  	}
  2122  
  2123  	return &version2.Queue{
  2124  		Size:    upstreamQueue.Size,
  2125  		Timeout: generateTimeWithDefault(upstreamQueue.Timeout, defaultTimeout),
  2126  	}
  2127  }
  2128  
  2129  func generateErrorPageName(errPageIndex int, index int) string {
  2130  	return fmt.Sprintf("@error_page_%v_%v", errPageIndex, index)
  2131  }
  2132  
  2133  func generateErrorPageCodes(codes []int) string {
  2134  	var c []string
  2135  	for _, code := range codes {
  2136  		c = append(c, strconv.Itoa(code))
  2137  	}
  2138  	return strings.Join(c, " ")
  2139  }
  2140  
  2141  func generateErrorPages(errPageIndex int, errorPages []conf_v1.ErrorPage) []version2.ErrorPage {
  2142  	var ePages []version2.ErrorPage
  2143  
  2144  	for i, e := range errorPages {
  2145  		var code int
  2146  		var name string
  2147  
  2148  		if e.Redirect != nil {
  2149  			code = 301
  2150  			if e.Redirect.Code != 0 {
  2151  				code = e.Redirect.Code
  2152  			}
  2153  			name = e.Redirect.URL
  2154  		} else {
  2155  			code = e.Return.Code
  2156  			name = generateErrorPageName(errPageIndex, i)
  2157  		}
  2158  
  2159  		ep := version2.ErrorPage{
  2160  			Name:         name,
  2161  			Codes:        generateErrorPageCodes(e.Codes),
  2162  			ResponseCode: code,
  2163  		}
  2164  
  2165  		ePages = append(ePages, ep)
  2166  	}
  2167  
  2168  	return ePages
  2169  }
  2170  
  2171  func generateErrorPageLocations(errPageIndex int, errorPages []conf_v1.ErrorPage) []version2.ErrorPageLocation {
  2172  	var errorPageLocations []version2.ErrorPageLocation
  2173  	for i, e := range errorPages {
  2174  		if e.Redirect != nil {
  2175  			// Redirects are handled in the error_page of the location directly, no need for a named location.
  2176  			continue
  2177  		}
  2178  
  2179  		var headers []version2.Header
  2180  
  2181  		for _, h := range e.Return.Headers {
  2182  			headers = append(headers, version2.Header{
  2183  				Name:  h.Name,
  2184  				Value: h.Value,
  2185  			})
  2186  		}
  2187  
  2188  		defaultType := "text/html"
  2189  		if e.Return.Type != "" {
  2190  			defaultType = e.Return.Type
  2191  		}
  2192  
  2193  		epl := version2.ErrorPageLocation{
  2194  			Name:        generateErrorPageName(errPageIndex, i),
  2195  			DefaultType: defaultType,
  2196  			Return:      generateReturnBlock(e.Return.Body, 0, 0),
  2197  			Headers:     headers,
  2198  		}
  2199  
  2200  		errorPageLocations = append(errorPageLocations, epl)
  2201  	}
  2202  
  2203  	return errorPageLocations
  2204  }
  2205  
  2206  func generateProxySSLName(svcName, ns string) string {
  2207  	return fmt.Sprintf("%s.%s.svc", svcName, ns)
  2208  }
  2209  
  2210  func isTLSEnabled(u conf_v1.Upstream, spiffeCerts bool) bool {
  2211  	return u.TLS.Enable || spiffeCerts
  2212  }