github.com/projectcontour/contour@v1.28.2/cmd/contour/servecontext.go (about)

     1  // Copyright Project Contour Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  //     http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package main
    15  
    16  import (
    17  	"crypto/rand"
    18  	"crypto/tls"
    19  	"crypto/x509"
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"strings"
    24  	"time"
    25  
    26  	contour_api_v1 "github.com/projectcontour/contour/apis/projectcontour/v1"
    27  	contour_api_v1alpha1 "github.com/projectcontour/contour/apis/projectcontour/v1alpha1"
    28  	envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3"
    29  	"github.com/projectcontour/contour/internal/k8s"
    30  	"github.com/projectcontour/contour/internal/ref"
    31  	xdscache_v3 "github.com/projectcontour/contour/internal/xdscache/v3"
    32  	"github.com/projectcontour/contour/pkg/config"
    33  	"github.com/sirupsen/logrus"
    34  	"google.golang.org/grpc"
    35  	"google.golang.org/grpc/credentials"
    36  	"google.golang.org/grpc/keepalive"
    37  )
    38  
    39  type serveContext struct {
    40  	// Name of the ContourConfiguration CRD to use for configuration.
    41  	contourConfigurationName string
    42  
    43  	Config config.Parameters
    44  
    45  	ServerConfig
    46  
    47  	// Enable Kubernetes client-go debugging.
    48  	KubernetesDebug uint
    49  
    50  	// contour's debug handler parameters
    51  	debugAddr string
    52  	debugPort int
    53  
    54  	// contour's metrics handler parameters
    55  	metricsAddr string
    56  	metricsPort int
    57  
    58  	// Contour's health handler parameters.
    59  	healthAddr string
    60  	healthPort int
    61  
    62  	// httpproxy root namespaces
    63  	rootNamespaces string
    64  
    65  	// Watch only these namespaces to allow running with limited RBAC permissions.
    66  	watchNamespaces string
    67  
    68  	// ingress class
    69  	ingressClassName string
    70  
    71  	// envoy's stats listener parameters
    72  	statsAddr string
    73  	statsPort int
    74  
    75  	// envoy's listener parameters
    76  	useProxyProto bool
    77  
    78  	// envoy's http listener parameters
    79  	httpAddr      string
    80  	httpPort      int
    81  	httpAccessLog string
    82  
    83  	// envoy's https listener parameters
    84  	httpsAddr      string
    85  	httpsPort      int
    86  	httpsAccessLog string
    87  
    88  	// PermitInsecureGRPC disables TLS on Contour's gRPC listener.
    89  	PermitInsecureGRPC bool
    90  
    91  	// Leader election configuration.
    92  	LeaderElection LeaderElection
    93  
    94  	// Features disabled by the user.
    95  	disabledFeatures []string
    96  }
    97  
    98  type ServerConfig struct {
    99  	// contour's xds service parameters
   100  	xdsAddr                         string
   101  	xdsPort                         int
   102  	caFile, contourCert, contourKey string
   103  }
   104  
   105  type LeaderElection struct {
   106  	Disable       bool
   107  	LeaseDuration time.Duration
   108  	RenewDeadline time.Duration
   109  	RetryPeriod   time.Duration
   110  	Namespace     string
   111  	Name          string
   112  }
   113  
   114  // newServeContext returns a serveContext initialized to defaults.
   115  func newServeContext() *serveContext {
   116  	// Set defaults for parameters which are then overridden via flags, ENV, or ConfigFile
   117  	return &serveContext{
   118  		Config:             config.Defaults(),
   119  		statsAddr:          "0.0.0.0",
   120  		statsPort:          8002,
   121  		debugAddr:          "127.0.0.1",
   122  		debugPort:          6060,
   123  		healthAddr:         "0.0.0.0",
   124  		healthPort:         8000,
   125  		metricsAddr:        "0.0.0.0",
   126  		metricsPort:        8000,
   127  		httpAccessLog:      xdscache_v3.DEFAULT_HTTP_ACCESS_LOG,
   128  		httpsAccessLog:     xdscache_v3.DEFAULT_HTTPS_ACCESS_LOG,
   129  		httpAddr:           "0.0.0.0",
   130  		httpsAddr:          "0.0.0.0",
   131  		httpPort:           8080,
   132  		httpsPort:          8443,
   133  		PermitInsecureGRPC: false,
   134  		ServerConfig: ServerConfig{
   135  			xdsAddr:     "127.0.0.1",
   136  			xdsPort:     8001,
   137  			caFile:      "",
   138  			contourCert: "",
   139  			contourKey:  "",
   140  		},
   141  	}
   142  }
   143  
   144  // grpcOptions returns a slice of grpc.ServerOptions.
   145  // if ctx.PermitInsecureGRPC is false, the option set will
   146  // include TLS configuration.
   147  func grpcOptions(log logrus.FieldLogger, contourXDSConfig *contour_api_v1alpha1.TLS) []grpc.ServerOption {
   148  	opts := []grpc.ServerOption{
   149  		// By default the Go grpc library defaults to a value of ~100 streams per
   150  		// connection. This number is likely derived from the HTTP/2 spec:
   151  		// https://http2.github.io/http2-spec/#SettingValues
   152  		// We need to raise this value because Envoy will open one EDS stream per
   153  		// CDS entry. There doesn't seem to be a penalty for increasing this value,
   154  		// so set it the limit similar to envoyproxy/go-control-plane#70.
   155  		//
   156  		// Somewhat arbitrary limit to handle many, many, EDS streams.
   157  		grpc.MaxConcurrentStreams(1 << 20),
   158  		// Set gRPC keepalive params.
   159  		// See https://github.com/projectcontour/contour/issues/1756 for background.
   160  		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
   161  			PermitWithoutStream: true,
   162  		}),
   163  		grpc.KeepaliveParams(keepalive.ServerParameters{
   164  			Time:    60 * time.Second,
   165  			Timeout: 20 * time.Second,
   166  		}),
   167  	}
   168  
   169  	if !ref.Val(contourXDSConfig.Insecure, false) {
   170  		tlsconfig := tlsconfig(log, contourXDSConfig)
   171  		creds := credentials.NewTLS(tlsconfig)
   172  		opts = append(opts, grpc.Creds(creds))
   173  	}
   174  	return opts
   175  }
   176  
   177  // tlsconfig returns a new *tls.Config. If the TLS parameters passed are not properly configured
   178  // for tls communication, tlsconfig returns nil.
   179  func tlsconfig(log logrus.FieldLogger, contourXDSTLS *contour_api_v1alpha1.TLS) *tls.Config {
   180  	err := verifyTLSFlags(contourXDSTLS)
   181  	if err != nil {
   182  		log.WithError(err).Fatal("failed to verify TLS flags")
   183  	}
   184  
   185  	// Define a closure that lazily loads certificates and key at TLS handshake
   186  	// to ensure that latest certificates are used in case they have been rotated.
   187  	loadConfig := func() (*tls.Config, error) {
   188  		if contourXDSTLS == nil {
   189  			return nil, nil
   190  		}
   191  		cert, err := tls.LoadX509KeyPair(contourXDSTLS.CertFile, contourXDSTLS.KeyFile)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  
   196  		ca, err := os.ReadFile(contourXDSTLS.CAFile)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  
   201  		certPool := x509.NewCertPool()
   202  		if ok := certPool.AppendCertsFromPEM(ca); !ok {
   203  			return nil, fmt.Errorf("unable to append certificate in %s to CA pool", contourXDSTLS.CAFile)
   204  		}
   205  
   206  		return &tls.Config{
   207  			Certificates: []tls.Certificate{cert},
   208  			ClientAuth:   tls.RequireAndVerifyClientCert,
   209  			ClientCAs:    certPool,
   210  			MinVersion:   tls.VersionTLS13,
   211  		}, nil
   212  	}
   213  
   214  	// Attempt to load certificates and key to catch configuration errors early.
   215  	if _, lerr := loadConfig(); lerr != nil {
   216  		log.WithError(lerr).Fatal("failed to load certificate and key")
   217  	}
   218  
   219  	return &tls.Config{
   220  		MinVersion: tls.VersionTLS13,
   221  		ClientAuth: tls.RequireAndVerifyClientCert,
   222  		Rand:       rand.Reader,
   223  		GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) {
   224  			return loadConfig()
   225  		},
   226  	}
   227  }
   228  
   229  // verifyTLSFlags indicates if the TLS flags are set up correctly.
   230  func verifyTLSFlags(contourXDSTLS *contour_api_v1alpha1.TLS) error {
   231  	if contourXDSTLS.CAFile == "" && contourXDSTLS.CertFile == "" && contourXDSTLS.KeyFile == "" {
   232  		return errors.New("no TLS parameters and --insecure not supplied. You must supply one or the other")
   233  	}
   234  	// If one of the three TLS commands is not empty, they all must be not empty
   235  	if !(contourXDSTLS.CAFile != "" && contourXDSTLS.CertFile != "" && contourXDSTLS.KeyFile != "") {
   236  		return errors.New("you must supply all three TLS parameters - --contour-cafile, --contour-cert-file, --contour-key-file, or none of them")
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  // proxyRootNamespaces returns a slice of namespaces restricting where
   243  // contour should look for httpproxy roots.
   244  func (ctx *serveContext) proxyRootNamespaces() []string {
   245  	if strings.TrimSpace(ctx.rootNamespaces) == "" {
   246  		return nil
   247  	}
   248  	var ns []string
   249  	for _, s := range strings.Split(ctx.rootNamespaces, ",") {
   250  		ns = append(ns, strings.TrimSpace(s))
   251  	}
   252  	return ns
   253  }
   254  
   255  func (ctx *serveContext) watchedNamespaces() []string {
   256  	if strings.TrimSpace(ctx.watchNamespaces) == "" {
   257  		return nil
   258  	}
   259  	var ns []string
   260  	for _, s := range strings.Split(ctx.watchNamespaces, ",") {
   261  		ns = append(ns, strings.TrimSpace(s))
   262  	}
   263  	return ns
   264  }
   265  
   266  // parseDefaultHTTPVersions parses a list of supported HTTP versions
   267  // (of the form "HTTP/xx") into a slice of unique version constants.
   268  func parseDefaultHTTPVersions(versions []contour_api_v1alpha1.HTTPVersionType) []envoy_v3.HTTPVersionType {
   269  	wanted := map[envoy_v3.HTTPVersionType]struct{}{}
   270  
   271  	for _, v := range versions {
   272  		switch v {
   273  		case contour_api_v1alpha1.HTTPVersion1:
   274  			wanted[envoy_v3.HTTPVersion1] = struct{}{}
   275  		case contour_api_v1alpha1.HTTPVersion2:
   276  			wanted[envoy_v3.HTTPVersion2] = struct{}{}
   277  		}
   278  	}
   279  
   280  	var parsed []envoy_v3.HTTPVersionType
   281  	for k := range wanted {
   282  		parsed = append(parsed, k)
   283  	}
   284  
   285  	return parsed
   286  }
   287  
   288  func (ctx *serveContext) convertToContourConfigurationSpec() contour_api_v1alpha1.ContourConfigurationSpec {
   289  	ingress := &contour_api_v1alpha1.IngressConfig{}
   290  	if len(ctx.ingressClassName) > 0 {
   291  		ingress.ClassNames = strings.Split(ctx.ingressClassName, ",")
   292  	}
   293  	ingress.StatusAddress = ctx.Config.IngressStatusAddress
   294  
   295  	var gatewayConfig *contour_api_v1alpha1.GatewayConfig
   296  	if ctx.Config.GatewayConfig != nil {
   297  		gatewayConfig = &contour_api_v1alpha1.GatewayConfig{
   298  			// nolint:staticcheck
   299  			ControllerName: ctx.Config.GatewayConfig.ControllerName,
   300  		}
   301  
   302  		if ctx.Config.GatewayConfig.GatewayRef != nil {
   303  			gatewayConfig.GatewayRef = &contour_api_v1alpha1.NamespacedName{
   304  				Namespace: ctx.Config.GatewayConfig.GatewayRef.Namespace,
   305  				Name:      ctx.Config.GatewayConfig.GatewayRef.Name,
   306  			}
   307  		}
   308  	}
   309  
   310  	var cipherSuites []string
   311  	for _, suite := range ctx.Config.TLS.CipherSuites {
   312  		cipherSuites = append(cipherSuites, suite)
   313  	}
   314  
   315  	var accessLogFormat contour_api_v1alpha1.AccessLogType
   316  	switch ctx.Config.AccessLogFormat {
   317  	case config.EnvoyAccessLog:
   318  		accessLogFormat = contour_api_v1alpha1.EnvoyAccessLog
   319  	case config.JSONAccessLog:
   320  		accessLogFormat = contour_api_v1alpha1.JSONAccessLog
   321  	}
   322  
   323  	var accessLogFields contour_api_v1alpha1.AccessLogJSONFields
   324  	for _, alf := range ctx.Config.AccessLogFields {
   325  		accessLogFields = append(accessLogFields, alf)
   326  	}
   327  
   328  	var accessLogLevel contour_api_v1alpha1.AccessLogLevel
   329  	switch ctx.Config.AccessLogLevel {
   330  	case config.LogLevelInfo:
   331  		accessLogLevel = contour_api_v1alpha1.LogLevelInfo
   332  	case config.LogLevelError:
   333  		accessLogLevel = contour_api_v1alpha1.LogLevelError
   334  	case config.LogLevelCritical:
   335  		accessLogLevel = contour_api_v1alpha1.LogLevelCritical
   336  	case config.LogLevelDisabled:
   337  		accessLogLevel = contour_api_v1alpha1.LogLevelDisabled
   338  	}
   339  
   340  	var defaultHTTPVersions []contour_api_v1alpha1.HTTPVersionType
   341  	for _, version := range ctx.Config.DefaultHTTPVersions {
   342  		switch version {
   343  		case config.HTTPVersion1:
   344  			defaultHTTPVersions = append(defaultHTTPVersions, contour_api_v1alpha1.HTTPVersion1)
   345  		case config.HTTPVersion2:
   346  			defaultHTTPVersions = append(defaultHTTPVersions, contour_api_v1alpha1.HTTPVersion2)
   347  		}
   348  	}
   349  
   350  	timeoutParams := &contour_api_v1alpha1.TimeoutParameters{}
   351  	if len(ctx.Config.Timeouts.RequestTimeout) > 0 {
   352  		timeoutParams.RequestTimeout = ref.To(ctx.Config.Timeouts.RequestTimeout)
   353  	}
   354  	if len(ctx.Config.Timeouts.ConnectionIdleTimeout) > 0 {
   355  		timeoutParams.ConnectionIdleTimeout = ref.To(ctx.Config.Timeouts.ConnectionIdleTimeout)
   356  	}
   357  	if len(ctx.Config.Timeouts.StreamIdleTimeout) > 0 {
   358  		timeoutParams.StreamIdleTimeout = ref.To(ctx.Config.Timeouts.StreamIdleTimeout)
   359  	}
   360  	if len(ctx.Config.Timeouts.MaxConnectionDuration) > 0 {
   361  		timeoutParams.MaxConnectionDuration = ref.To(ctx.Config.Timeouts.MaxConnectionDuration)
   362  	}
   363  	if len(ctx.Config.Timeouts.DelayedCloseTimeout) > 0 {
   364  		timeoutParams.DelayedCloseTimeout = ref.To(ctx.Config.Timeouts.DelayedCloseTimeout)
   365  	}
   366  	if len(ctx.Config.Timeouts.ConnectionShutdownGracePeriod) > 0 {
   367  		timeoutParams.ConnectionShutdownGracePeriod = ref.To(ctx.Config.Timeouts.ConnectionShutdownGracePeriod)
   368  	}
   369  	if len(ctx.Config.Timeouts.ConnectTimeout) > 0 {
   370  		timeoutParams.ConnectTimeout = ref.To(ctx.Config.Timeouts.ConnectTimeout)
   371  	}
   372  
   373  	var dnsLookupFamily contour_api_v1alpha1.ClusterDNSFamilyType
   374  	switch ctx.Config.Cluster.DNSLookupFamily {
   375  	case config.AutoClusterDNSFamily:
   376  		dnsLookupFamily = contour_api_v1alpha1.AutoClusterDNSFamily
   377  	case config.IPv6ClusterDNSFamily:
   378  		dnsLookupFamily = contour_api_v1alpha1.IPv6ClusterDNSFamily
   379  	case config.IPv4ClusterDNSFamily:
   380  		dnsLookupFamily = contour_api_v1alpha1.IPv4ClusterDNSFamily
   381  	case config.AllClusterDNSFamily:
   382  		dnsLookupFamily = contour_api_v1alpha1.AllClusterDNSFamily
   383  	}
   384  
   385  	var tracingConfig *contour_api_v1alpha1.TracingConfig
   386  	if ctx.Config.Tracing != nil {
   387  		namespacedName := k8s.NamespacedNameFrom(ctx.Config.Tracing.ExtensionService)
   388  		var customTags []*contour_api_v1alpha1.CustomTag
   389  		for _, customTag := range ctx.Config.Tracing.CustomTags {
   390  			customTags = append(customTags, &contour_api_v1alpha1.CustomTag{
   391  				TagName:           customTag.TagName,
   392  				Literal:           customTag.Literal,
   393  				RequestHeaderName: customTag.RequestHeaderName,
   394  			})
   395  		}
   396  		tracingConfig = &contour_api_v1alpha1.TracingConfig{
   397  			IncludePodDetail: ctx.Config.Tracing.IncludePodDetail,
   398  			ServiceName:      ctx.Config.Tracing.ServiceName,
   399  			OverallSampling:  ctx.Config.Tracing.OverallSampling,
   400  			MaxPathTagLength: ctx.Config.Tracing.MaxPathTagLength,
   401  			CustomTags:       customTags,
   402  			ExtensionService: &contour_api_v1alpha1.NamespacedName{
   403  				Name:      namespacedName.Name,
   404  				Namespace: namespacedName.Namespace,
   405  			},
   406  		}
   407  	}
   408  
   409  	var rateLimitService *contour_api_v1alpha1.RateLimitServiceConfig
   410  	if ctx.Config.RateLimitService.ExtensionService != "" {
   411  
   412  		nsedName := k8s.NamespacedNameFrom(ctx.Config.RateLimitService.ExtensionService)
   413  		rateLimitService = &contour_api_v1alpha1.RateLimitServiceConfig{
   414  			ExtensionService: contour_api_v1alpha1.NamespacedName{
   415  				Name:      nsedName.Name,
   416  				Namespace: nsedName.Namespace,
   417  			},
   418  			Domain:                       ctx.Config.RateLimitService.Domain,
   419  			FailOpen:                     ref.To(ctx.Config.RateLimitService.FailOpen),
   420  			EnableXRateLimitHeaders:      ref.To(ctx.Config.RateLimitService.EnableXRateLimitHeaders),
   421  			EnableResourceExhaustedCode:  ref.To(ctx.Config.RateLimitService.EnableResourceExhaustedCode),
   422  			DefaultGlobalRateLimitPolicy: ctx.Config.RateLimitService.DefaultGlobalRateLimitPolicy,
   423  		}
   424  	}
   425  
   426  	var serverHeaderTransformation contour_api_v1alpha1.ServerHeaderTransformationType
   427  	switch ctx.Config.ServerHeaderTransformation {
   428  	case config.OverwriteServerHeader:
   429  		serverHeaderTransformation = contour_api_v1alpha1.OverwriteServerHeader
   430  	case config.AppendIfAbsentServerHeader:
   431  		serverHeaderTransformation = contour_api_v1alpha1.AppendIfAbsentServerHeader
   432  	case config.PassThroughServerHeader:
   433  		serverHeaderTransformation = contour_api_v1alpha1.PassThroughServerHeader
   434  	}
   435  
   436  	var globalExtAuth *contour_api_v1.AuthorizationServer
   437  	if ctx.Config.GlobalExternalAuthorization.ExtensionService != "" {
   438  		nsedName := k8s.NamespacedNameFrom(ctx.Config.GlobalExternalAuthorization.ExtensionService)
   439  		globalExtAuth = &contour_api_v1.AuthorizationServer{
   440  			ExtensionServiceRef: contour_api_v1.ExtensionServiceReference{
   441  				Name:      nsedName.Name,
   442  				Namespace: nsedName.Namespace,
   443  			},
   444  			ResponseTimeout: ctx.Config.GlobalExternalAuthorization.ResponseTimeout,
   445  			FailOpen:        ctx.Config.GlobalExternalAuthorization.FailOpen,
   446  		}
   447  
   448  		if ctx.Config.GlobalExternalAuthorization.AuthPolicy != nil {
   449  			globalExtAuth.AuthPolicy = &contour_api_v1.AuthorizationPolicy{
   450  				Disabled: ctx.Config.GlobalExternalAuthorization.AuthPolicy.Disabled,
   451  				Context:  ctx.Config.GlobalExternalAuthorization.AuthPolicy.Context,
   452  			}
   453  		}
   454  
   455  		if ctx.Config.GlobalExternalAuthorization.WithRequestBody != nil {
   456  			globalExtAuth.WithRequestBody = &contour_api_v1.AuthorizationServerBufferSettings{
   457  				MaxRequestBytes:     ctx.Config.GlobalExternalAuthorization.WithRequestBody.MaxRequestBytes,
   458  				AllowPartialMessage: ctx.Config.GlobalExternalAuthorization.WithRequestBody.AllowPartialMessage,
   459  				PackAsBytes:         ctx.Config.GlobalExternalAuthorization.WithRequestBody.PackAsBytes,
   460  			}
   461  		}
   462  	}
   463  
   464  	policy := &contour_api_v1alpha1.PolicyConfig{
   465  		RequestHeadersPolicy: &contour_api_v1alpha1.HeadersPolicy{
   466  			Set:    ctx.Config.Policy.RequestHeadersPolicy.Set,
   467  			Remove: ctx.Config.Policy.RequestHeadersPolicy.Remove,
   468  		},
   469  		ResponseHeadersPolicy: &contour_api_v1alpha1.HeadersPolicy{
   470  			Set:    ctx.Config.Policy.ResponseHeadersPolicy.Set,
   471  			Remove: ctx.Config.Policy.ResponseHeadersPolicy.Remove,
   472  		},
   473  		ApplyToIngress: ref.To(ctx.Config.Policy.ApplyToIngress),
   474  	}
   475  
   476  	var clientCertificate *contour_api_v1alpha1.NamespacedName
   477  	if len(ctx.Config.TLS.ClientCertificate.Name) > 0 {
   478  		clientCertificate = &contour_api_v1alpha1.NamespacedName{
   479  			Name:      ctx.Config.TLS.ClientCertificate.Name,
   480  			Namespace: ctx.Config.TLS.ClientCertificate.Namespace,
   481  		}
   482  	}
   483  
   484  	var fallbackCertificate *contour_api_v1alpha1.NamespacedName
   485  	if len(ctx.Config.TLS.FallbackCertificate.Name) > 0 {
   486  		fallbackCertificate = &contour_api_v1alpha1.NamespacedName{
   487  			Name:      ctx.Config.TLS.FallbackCertificate.Name,
   488  			Namespace: ctx.Config.TLS.FallbackCertificate.Namespace,
   489  		}
   490  	}
   491  
   492  	contourMetrics := contour_api_v1alpha1.MetricsConfig{
   493  		Address: ctx.metricsAddr,
   494  		Port:    ctx.metricsPort,
   495  	}
   496  
   497  	envoyMetrics := contour_api_v1alpha1.MetricsConfig{
   498  		Address: ctx.statsAddr,
   499  		Port:    ctx.statsPort,
   500  	}
   501  
   502  	// Override metrics endpoint info from config files
   503  	//
   504  	// Note!
   505  	// Parameters from command line should take precedence over config file,
   506  	// but here we cannot know anymore if value in ctx.nnn are defaults from
   507  	// newServeContext() or from command line arguments. Therefore metrics
   508  	// configuration from config file takes precedence over command line.
   509  	setMetricsFromConfig(ctx.Config.Metrics.Contour, &contourMetrics)
   510  	setMetricsFromConfig(ctx.Config.Metrics.Envoy, &envoyMetrics)
   511  
   512  	// Convert serveContext to a ContourConfiguration
   513  	contourConfiguration := contour_api_v1alpha1.ContourConfigurationSpec{
   514  		Ingress: ingress,
   515  		Debug: &contour_api_v1alpha1.DebugConfig{
   516  			Address: ctx.debugAddr,
   517  			Port:    ctx.debugPort,
   518  		},
   519  		Health: &contour_api_v1alpha1.HealthConfig{
   520  			Address: ctx.healthAddr,
   521  			Port:    ctx.healthPort,
   522  		},
   523  		Envoy: &contour_api_v1alpha1.EnvoyConfig{
   524  			Listener: &contour_api_v1alpha1.EnvoyListenerConfig{
   525  				UseProxyProto:                 &ctx.useProxyProto,
   526  				DisableAllowChunkedLength:     &ctx.Config.DisableAllowChunkedLength,
   527  				DisableMergeSlashes:           &ctx.Config.DisableMergeSlashes,
   528  				ServerHeaderTransformation:    serverHeaderTransformation,
   529  				ConnectionBalancer:            ctx.Config.Listener.ConnectionBalancer,
   530  				PerConnectionBufferLimitBytes: ctx.Config.Listener.PerConnectionBufferLimitBytes,
   531  				MaxRequestsPerConnection:      ctx.Config.Listener.MaxRequestsPerConnection,
   532  				MaxRequestsPerIOCycle:         ctx.Config.Listener.MaxRequestsPerIOCycle,
   533  				HTTP2MaxConcurrentStreams:     ctx.Config.Listener.HTTP2MaxConcurrentStreams,
   534  				MaxConnectionsPerListener:     ctx.Config.Listener.MaxConnectionsPerListener,
   535  				TLS: &contour_api_v1alpha1.EnvoyTLS{
   536  					MinimumProtocolVersion: ctx.Config.TLS.MinimumProtocolVersion,
   537  					MaximumProtocolVersion: ctx.Config.TLS.MaximumProtocolVersion,
   538  					CipherSuites:           cipherSuites,
   539  				},
   540  				SocketOptions: &contour_api_v1alpha1.SocketOptions{
   541  					TOS:          ctx.Config.Listener.SocketOptions.TOS,
   542  					TrafficClass: ctx.Config.Listener.SocketOptions.TrafficClass,
   543  				},
   544  			},
   545  			Service: &contour_api_v1alpha1.NamespacedName{
   546  				Name:      ctx.Config.EnvoyServiceName,
   547  				Namespace: ctx.Config.EnvoyServiceNamespace,
   548  			},
   549  			HTTPListener: &contour_api_v1alpha1.EnvoyListener{
   550  				Address:   ctx.httpAddr,
   551  				Port:      ctx.httpPort,
   552  				AccessLog: ctx.httpAccessLog,
   553  			},
   554  			HTTPSListener: &contour_api_v1alpha1.EnvoyListener{
   555  				Address:   ctx.httpsAddr,
   556  				Port:      ctx.httpsPort,
   557  				AccessLog: ctx.httpsAccessLog,
   558  			},
   559  			Metrics: &envoyMetrics,
   560  			Health: &contour_api_v1alpha1.HealthConfig{
   561  				Address: ctx.statsAddr,
   562  				Port:    ctx.statsPort,
   563  			},
   564  			ClientCertificate: clientCertificate,
   565  			Logging: &contour_api_v1alpha1.EnvoyLogging{
   566  				AccessLogFormat:       accessLogFormat,
   567  				AccessLogFormatString: ctx.Config.AccessLogFormatString,
   568  				AccessLogJSONFields:   accessLogFields,
   569  				AccessLogLevel:        accessLogLevel,
   570  			},
   571  			DefaultHTTPVersions: defaultHTTPVersions,
   572  			Timeouts:            timeoutParams,
   573  			Cluster: &contour_api_v1alpha1.ClusterParameters{
   574  				DNSLookupFamily:               dnsLookupFamily,
   575  				MaxRequestsPerConnection:      ctx.Config.Cluster.MaxRequestsPerConnection,
   576  				PerConnectionBufferLimitBytes: ctx.Config.Cluster.PerConnectionBufferLimitBytes,
   577  				GlobalCircuitBreakerDefaults:  ctx.Config.Cluster.GlobalCircuitBreakerDefaults,
   578  				UpstreamTLS: &contour_api_v1alpha1.EnvoyTLS{
   579  					MinimumProtocolVersion: ctx.Config.Cluster.UpstreamTLS.MinimumProtocolVersion,
   580  					MaximumProtocolVersion: ctx.Config.Cluster.UpstreamTLS.MaximumProtocolVersion,
   581  					CipherSuites:           ctx.Config.Cluster.UpstreamTLS.CipherSuites,
   582  				},
   583  			},
   584  			Network: &contour_api_v1alpha1.NetworkParameters{
   585  				XffNumTrustedHops: &ctx.Config.Network.XffNumTrustedHops,
   586  				EnvoyAdminPort:    &ctx.Config.Network.EnvoyAdminPort,
   587  			},
   588  		},
   589  		Gateway: gatewayConfig,
   590  		HTTPProxy: &contour_api_v1alpha1.HTTPProxyConfig{
   591  			DisablePermitInsecure: &ctx.Config.DisablePermitInsecure,
   592  			RootNamespaces:        ctx.proxyRootNamespaces(),
   593  			FallbackCertificate:   fallbackCertificate,
   594  		},
   595  		EnableExternalNameService:   &ctx.Config.EnableExternalNameService,
   596  		GlobalExternalAuthorization: globalExtAuth,
   597  		RateLimitService:            rateLimitService,
   598  		Policy:                      policy,
   599  		Metrics:                     &contourMetrics,
   600  		Tracing:                     tracingConfig,
   601  		FeatureFlags:                ctx.Config.FeatureFlags,
   602  	}
   603  
   604  	xdsServerType := contour_api_v1alpha1.ContourServerType
   605  	if ctx.Config.Server.XDSServerType == config.EnvoyServerType {
   606  		xdsServerType = contour_api_v1alpha1.EnvoyServerType
   607  	}
   608  
   609  	contourConfiguration.XDSServer = &contour_api_v1alpha1.XDSServerConfig{
   610  		Type:    xdsServerType,
   611  		Address: ctx.xdsAddr,
   612  		Port:    ctx.xdsPort,
   613  		TLS: &contour_api_v1alpha1.TLS{
   614  			CAFile:   ctx.caFile,
   615  			CertFile: ctx.contourCert,
   616  			KeyFile:  ctx.contourKey,
   617  			Insecure: &ctx.PermitInsecureGRPC,
   618  		},
   619  	}
   620  
   621  	return contourConfiguration
   622  }
   623  
   624  func setMetricsFromConfig(src config.MetricsServerParameters, dst *contour_api_v1alpha1.MetricsConfig) {
   625  	if len(src.Address) > 0 {
   626  		dst.Address = src.Address
   627  	}
   628  
   629  	if src.Port > 0 {
   630  		dst.Port = src.Port
   631  	}
   632  
   633  	if src.HasTLS() {
   634  		dst.TLS = &contour_api_v1alpha1.MetricsTLS{
   635  			CertFile: src.ServerCert,
   636  			KeyFile:  src.ServerKey,
   637  			CAFile:   src.CABundle,
   638  		}
   639  	}
   640  }