...

Source file src/github.com/linkerd/linkerd2/cli/cmd/options.go

Documentation: github.com/linkerd/linkerd2/cli/cmd

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"fmt"
    10  	"net"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/linkerd/linkerd2/cli/flag"
    15  	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
    16  	"github.com/linkerd/linkerd2/pkg/cmd"
    17  	flagspkg "github.com/linkerd/linkerd2/pkg/flags"
    18  	"github.com/linkerd/linkerd2/pkg/inject"
    19  	"github.com/linkerd/linkerd2/pkg/issuercerts"
    20  	"github.com/linkerd/linkerd2/pkg/k8s"
    21  	"github.com/linkerd/linkerd2/pkg/tls"
    22  	"github.com/linkerd/linkerd2/pkg/version"
    23  	log "github.com/sirupsen/logrus"
    24  	"github.com/spf13/pflag"
    25  	corev1 "k8s.io/api/core/v1"
    26  	k8sResource "k8s.io/apimachinery/pkg/api/resource"
    27  	"k8s.io/apimachinery/pkg/util/validation"
    28  )
    29  
    30  // makeInstallUpgradeFlags builds the set of flags which are used by install and
    31  // upgrade.  These flags control the majority of how the control plane is
    32  // configured.
    33  func makeInstallUpgradeFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet, error) {
    34  	installUpgradeFlags := pflag.NewFlagSet("install", pflag.ExitOnError)
    35  
    36  	issuanceLifetime, err := time.ParseDuration(defaults.Identity.Issuer.IssuanceLifetime)
    37  	if err != nil {
    38  		return nil, nil, err
    39  	}
    40  	clockSkewAllowance, err := time.ParseDuration(defaults.Identity.Issuer.ClockSkewAllowance)
    41  	if err != nil {
    42  		return nil, nil, err
    43  	}
    44  
    45  	flags := []flag.Flag{
    46  		flag.NewBoolFlag(installUpgradeFlags, "linkerd-cni-enabled", defaults.CNIEnabled,
    47  			"Omit the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed",
    48  			func(values *l5dcharts.Values, value bool) error {
    49  				values.CNIEnabled = value
    50  				return nil
    51  			}),
    52  
    53  		flag.NewStringFlag(installUpgradeFlags, "controller-log-level", defaults.ControllerLogLevel,
    54  			"Log level for the controller and web components", func(values *l5dcharts.Values, value string) error {
    55  				values.ControllerLogLevel = value
    56  				return nil
    57  			}),
    58  
    59  		// The HA flag must be processed before flags that set these values individually so that the
    60  		// individual flags can override the HA defaults.
    61  		flag.NewBoolFlag(installUpgradeFlags, "ha", false, "Enable HA deployment config for the control plane (default false)",
    62  			func(values *l5dcharts.Values, value bool) error {
    63  				values.HighAvailability = value
    64  				if value {
    65  					if err := l5dcharts.MergeHAValues(values); err != nil {
    66  						return err
    67  					}
    68  				}
    69  				return nil
    70  			}),
    71  
    72  		flag.NewUintFlag(installUpgradeFlags, "controller-replicas", defaults.ControllerReplicas,
    73  			"Replicas of the controller to deploy", func(values *l5dcharts.Values, value uint) error {
    74  				values.ControllerReplicas = value
    75  				return nil
    76  			}),
    77  
    78  		flag.NewInt64Flag(installUpgradeFlags, "controller-uid", defaults.ControllerUID,
    79  			"Run the control plane components under this user ID", func(values *l5dcharts.Values, value int64) error {
    80  				values.ControllerUID = value
    81  				return nil
    82  			}),
    83  
    84  		flag.NewInt64Flag(installUpgradeFlags, "controller-gid", defaults.ControllerGID,
    85  			"Run the control plane components under this group ID", func(values *l5dcharts.Values, value int64) error {
    86  				values.ControllerGID = value
    87  				return nil
    88  			}),
    89  
    90  		flag.NewBoolFlag(installUpgradeFlags, "disable-h2-upgrade", !defaults.EnableH2Upgrade,
    91  			"Prevents the controller from instructing proxies to perform transparent HTTP/2 upgrading (default false)",
    92  			func(values *l5dcharts.Values, value bool) error {
    93  				values.EnableH2Upgrade = !value
    94  				return nil
    95  			}),
    96  
    97  		flag.NewBoolFlag(installUpgradeFlags, "disable-heartbeat", defaults.DisableHeartBeat,
    98  			"Disables the heartbeat cronjob (default false)", func(values *l5dcharts.Values, value bool) error {
    99  				values.DisableHeartBeat = value
   100  				return nil
   101  			}),
   102  
   103  		flag.NewDurationFlag(installUpgradeFlags, "identity-issuance-lifetime", issuanceLifetime,
   104  			"The amount of time for which the Identity issuer should certify identity",
   105  			func(values *l5dcharts.Values, value time.Duration) error {
   106  				values.Identity.Issuer.IssuanceLifetime = value.String()
   107  				return nil
   108  			}),
   109  
   110  		flag.NewDurationFlag(installUpgradeFlags, "identity-clock-skew-allowance", clockSkewAllowance,
   111  			"The amount of time to allow for clock skew within a Linkerd cluster",
   112  			func(values *l5dcharts.Values, value time.Duration) error {
   113  				values.Identity.Issuer.ClockSkewAllowance = value.String()
   114  				return nil
   115  			}),
   116  
   117  		flag.NewBoolFlag(installUpgradeFlags, "control-plane-tracing", defaults.ControlPlaneTracing,
   118  			"Enables Control Plane Tracing with the defaults", func(values *l5dcharts.Values, value bool) error {
   119  				values.ControlPlaneTracing = value
   120  				return nil
   121  			}),
   122  
   123  		flag.NewStringFlag(installUpgradeFlags, "control-plane-tracing-namespace", defaults.ControlPlaneTracingNamespace,
   124  			"Send control plane traces to Linkerd-Jaeger extension in this namespace", func(values *l5dcharts.Values, value string) error {
   125  				values.ControlPlaneTracingNamespace = value
   126  				return nil
   127  			}),
   128  
   129  		flag.NewStringFlag(installUpgradeFlags, "identity-issuer-certificate-file", "",
   130  			"A path to a PEM-encoded file containing the Linkerd Identity issuer certificate (generated by default)",
   131  			func(values *l5dcharts.Values, value string) error {
   132  				if value != "" {
   133  					crt, err := loadCrtPEM(value)
   134  					if err != nil {
   135  						return err
   136  					}
   137  					values.Identity.Issuer.TLS.CrtPEM = crt
   138  				}
   139  				return nil
   140  			}),
   141  
   142  		flag.NewStringFlag(installUpgradeFlags, "identity-issuer-key-file", "",
   143  			"A path to a PEM-encoded file containing the Linkerd Identity issuer private key (generated by default)",
   144  			func(values *l5dcharts.Values, value string) error {
   145  				if value != "" {
   146  					key, err := loadKeyPEM(value)
   147  					if err != nil {
   148  						return err
   149  					}
   150  					values.Identity.Issuer.TLS.KeyPEM = key
   151  				}
   152  				return nil
   153  			}),
   154  
   155  		flag.NewStringFlag(installUpgradeFlags, "identity-trust-anchors-file", "",
   156  			"A path to a PEM-encoded file containing Linkerd Identity trust anchors (generated by default)",
   157  			func(values *l5dcharts.Values, value string) error {
   158  				if value != "" {
   159  					data, err := os.ReadFile(filepath.Clean(value))
   160  					if err != nil {
   161  						return err
   162  					}
   163  					values.IdentityTrustAnchorsPEM = string(data)
   164  				}
   165  				return nil
   166  			}),
   167  
   168  		flag.NewBoolFlag(installUpgradeFlags, "enable-endpoint-slices", defaults.EnableEndpointSlices,
   169  			"Enables the usage of EndpointSlice informers and resources for destination service",
   170  			func(values *l5dcharts.Values, value bool) error {
   171  				values.EnableEndpointSlices = value
   172  				return nil
   173  			}),
   174  	}
   175  
   176  	// Hide developer focused flags in release builds.
   177  	release, err := version.IsReleaseChannel(version.Version)
   178  	if err != nil {
   179  		log.Errorf("Unable to parse version: %s", version.Version)
   180  	}
   181  	if release {
   182  		installUpgradeFlags.MarkHidden("control-plane-version")
   183  	}
   184  	installUpgradeFlags.MarkHidden("control-plane-tracing")
   185  	installUpgradeFlags.MarkHidden("control-plane-tracing-namespace")
   186  
   187  	return flags, installUpgradeFlags, nil
   188  }
   189  
   190  func loadCrtPEM(path string) (string, error) {
   191  	data, err := os.ReadFile(filepath.Clean(path))
   192  	if err != nil {
   193  		return "", err
   194  	}
   195  
   196  	crt, err := tls.DecodePEMCrt(string(data))
   197  	if err != nil {
   198  		return "", err
   199  	}
   200  	return crt.EncodeCertificatePEM(), nil
   201  }
   202  
   203  func loadKeyPEM(path string) (string, error) {
   204  	data, err := os.ReadFile(filepath.Clean(path))
   205  	if err != nil {
   206  		return "", err
   207  	}
   208  
   209  	key, err := tls.DecodePEMKey(string(data))
   210  	if err != nil {
   211  		return "", err
   212  	}
   213  	cred := tls.Cred{PrivateKey: key}
   214  	return cred.EncodePrivateKeyPEM(), nil
   215  }
   216  
   217  // makeInstallFlags builds the set of flags which are used by install.  These
   218  // flags should not be changed during an upgrade and are not available to the
   219  // upgrade command.
   220  func makeInstallFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
   221  
   222  	installOnlyFlags := pflag.NewFlagSet("install-only", pflag.ExitOnError)
   223  
   224  	flags := []flag.Flag{
   225  		flag.NewStringFlag(installOnlyFlags, "cluster-domain", defaults.ClusterDomain,
   226  			"Set custom cluster domain", func(values *l5dcharts.Values, value string) error {
   227  				values.ClusterDomain = value
   228  				return nil
   229  			}),
   230  
   231  		flag.NewStringFlag(installOnlyFlags, "identity-trust-domain", defaults.IdentityTrustDomain,
   232  			"Configures the name suffix used for identities.", func(values *l5dcharts.Values, value string) error {
   233  				values.IdentityTrustDomain = value
   234  				return nil
   235  			}),
   236  
   237  		flag.NewBoolFlag(installOnlyFlags, "identity-external-issuer", false,
   238  			"Whether to use an external identity issuer (default false)", func(values *l5dcharts.Values, value bool) error {
   239  				if value {
   240  					values.Identity.Issuer.Scheme = string(corev1.SecretTypeTLS)
   241  				} else {
   242  					values.Identity.Issuer.Scheme = k8s.IdentityIssuerSchemeLinkerd
   243  				}
   244  				return nil
   245  			}),
   246  
   247  		flag.NewBoolFlag(installOnlyFlags, "identity-external-ca", false,
   248  			"Whether to use an external CA provider (default false)", func(values *l5dcharts.Values, value bool) error {
   249  				values.Identity.ExternalCA = value
   250  				return nil
   251  			}),
   252  	}
   253  
   254  	return flags, installOnlyFlags
   255  }
   256  
   257  // makeProxyFlags builds the set of flags which affect how the proxy is
   258  // configured.  These flags are available to the inject command and to the
   259  // install and upgrade commands.
   260  func makeProxyFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
   261  	proxyFlags := pflag.NewFlagSet("proxy", pflag.ExitOnError)
   262  
   263  	flags := []flag.Flag{
   264  		flag.NewStringFlag(proxyFlags, "proxy-image", defaults.Proxy.Image.Name, "Linkerd proxy container image name",
   265  			func(values *l5dcharts.Values, value string) error {
   266  				values.Proxy.Image.Name = value
   267  				return nil
   268  			}),
   269  
   270  		flag.NewStringFlag(proxyFlags, "init-image", defaults.ProxyInit.Image.Name, "Linkerd init container image name",
   271  			func(values *l5dcharts.Values, value string) error {
   272  				values.ProxyInit.Image.Name = value
   273  				return nil
   274  			}),
   275  
   276  		flag.NewStringFlag(proxyFlags, "init-image-version", defaults.ProxyInit.Image.Version,
   277  			"Linkerd init container image version", func(values *l5dcharts.Values, value string) error {
   278  				values.ProxyInit.Image.Version = value
   279  				return nil
   280  			}),
   281  
   282  		flag.NewStringFlag(proxyFlags, "image-pull-policy", defaults.ImagePullPolicy,
   283  			"Docker image pull policy", func(values *l5dcharts.Values, value string) error {
   284  				values.ImagePullPolicy = value
   285  				values.Proxy.Image.PullPolicy = value
   286  				values.ProxyInit.Image.PullPolicy = value
   287  				values.DebugContainer.Image.PullPolicy = value
   288  				return nil
   289  			}),
   290  
   291  		flag.NewUintFlag(proxyFlags, "inbound-port", uint(defaults.Proxy.Ports.Inbound),
   292  			"Proxy port to use for inbound traffic", func(values *l5dcharts.Values, value uint) error {
   293  				values.Proxy.Ports.Inbound = int32(value)
   294  				return nil
   295  			}),
   296  
   297  		flag.NewUintFlag(proxyFlags, "outbound-port", uint(defaults.Proxy.Ports.Outbound),
   298  			"Proxy port to use for outbound traffic", func(values *l5dcharts.Values, value uint) error {
   299  				values.Proxy.Ports.Outbound = int32(value)
   300  				return nil
   301  			}),
   302  
   303  		flag.NewStringSliceFlag(proxyFlags, "skip-inbound-ports", nil, "Ports and/or port ranges (inclusive) that should skip the proxy and send directly to the application",
   304  			func(values *l5dcharts.Values, value []string) error {
   305  				values.ProxyInit.IgnoreInboundPorts = strings.Join(value, ",")
   306  				return nil
   307  			}),
   308  
   309  		flag.NewStringSliceFlag(proxyFlags, "skip-outbound-ports", nil, "Outbound ports and/or port ranges (inclusive) that should skip the proxy",
   310  			func(values *l5dcharts.Values, value []string) error {
   311  				values.ProxyInit.IgnoreOutboundPorts = strings.Join(value, ",")
   312  				return nil
   313  			}),
   314  
   315  		flag.NewInt64Flag(proxyFlags, "proxy-uid", defaults.Proxy.UID, "Run the proxy under this user ID",
   316  			func(values *l5dcharts.Values, value int64) error {
   317  				values.Proxy.UID = value
   318  				return nil
   319  			}),
   320  
   321  		flag.NewInt64Flag(proxyFlags, "proxy-gid", defaults.Proxy.GID, "Run the proxy under this group ID",
   322  			func(values *l5dcharts.Values, value int64) error {
   323  				values.Proxy.GID = value
   324  				return nil
   325  			}),
   326  
   327  		flag.NewStringFlag(proxyFlags, "proxy-log-level", defaults.Proxy.LogLevel, "Log level for the proxy",
   328  			func(values *l5dcharts.Values, value string) error {
   329  				values.Proxy.LogLevel = value
   330  				return nil
   331  			}),
   332  
   333  		flag.NewUintFlag(proxyFlags, "control-port", uint(defaults.Proxy.Ports.Control), "Proxy port to use for control",
   334  			func(values *l5dcharts.Values, value uint) error {
   335  				values.Proxy.Ports.Control = int32(value)
   336  				return nil
   337  			}),
   338  
   339  		flag.NewUintFlag(proxyFlags, "admin-port", uint(defaults.Proxy.Ports.Admin), "Proxy port to serve metrics on",
   340  			func(values *l5dcharts.Values, value uint) error {
   341  				values.Proxy.Ports.Admin = int32(value)
   342  				return nil
   343  			}),
   344  
   345  		flag.NewStringFlag(proxyFlags, "proxy-cpu-request", defaults.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
   346  			func(values *l5dcharts.Values, value string) error {
   347  				values.Proxy.Resources.CPU.Request = value
   348  				return nil
   349  			}),
   350  
   351  		flag.NewStringFlag(proxyFlags, "proxy-memory-request", defaults.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
   352  			func(values *l5dcharts.Values, value string) error {
   353  				values.Proxy.Resources.Memory.Request = value
   354  				return nil
   355  			}),
   356  
   357  		flag.NewStringFlag(proxyFlags, "proxy-cpu-limit", defaults.Proxy.Resources.CPU.Limit, "Maximum amount of CPU units that the proxy sidecar can use",
   358  			func(values *l5dcharts.Values, value string) error {
   359  				q, err := k8sResource.ParseQuantity(value)
   360  				if err != nil {
   361  					return err
   362  				}
   363  				c, err := inject.ToWholeCPUCores(q)
   364  				if err != nil {
   365  					return err
   366  				}
   367  				values.Proxy.Cores = c
   368  				values.Proxy.Resources.CPU.Limit = value
   369  				return nil
   370  			}),
   371  
   372  		flag.NewStringFlag(proxyFlags, "proxy-memory-limit", defaults.Proxy.Resources.Memory.Limit, "Maximum amount of Memory that the proxy sidecar can use",
   373  			func(values *l5dcharts.Values, value string) error {
   374  				values.Proxy.Resources.Memory.Limit = value
   375  				return nil
   376  			}),
   377  
   378  		flag.NewBoolFlag(proxyFlags, "enable-external-profiles", defaults.Proxy.EnableExternalProfiles, "Enable service profiles for non-Kubernetes services",
   379  			func(values *l5dcharts.Values, value bool) error {
   380  				values.Proxy.EnableExternalProfiles = value
   381  				return nil
   382  			}),
   383  
   384  		flag.NewStringFlag(proxyFlags, "default-inbound-policy", defaults.Proxy.DefaultInboundPolicy, "Inbound policy to use to control inbound access to the proxy",
   385  			func(values *l5dcharts.Values, value string) error {
   386  				values.Proxy.DefaultInboundPolicy = value
   387  				return nil
   388  			}),
   389  
   390  		// Deprecated flags
   391  
   392  		flag.NewStringFlag(proxyFlags, "proxy-memory", defaults.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
   393  			func(values *l5dcharts.Values, value string) error {
   394  				values.Proxy.Resources.Memory.Request = value
   395  				return nil
   396  			}),
   397  
   398  		flag.NewStringFlag(proxyFlags, "proxy-cpu", defaults.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
   399  			func(values *l5dcharts.Values, value string) error {
   400  				values.Proxy.Resources.CPU.Request = value
   401  				return nil
   402  			}),
   403  
   404  		flag.NewStringFlagP(proxyFlags, "proxy-version", "v", defaults.Proxy.Image.Version, "Tag to be used for the Linkerd proxy images",
   405  			func(values *l5dcharts.Values, value string) error {
   406  				values.Proxy.Image.Version = value
   407  				return nil
   408  			}),
   409  	}
   410  
   411  	registryFlag := flag.NewStringFlag(proxyFlags, "registry", cmd.DefaultDockerRegistry,
   412  		fmt.Sprintf("Docker registry to pull images from ($%s)", flagspkg.EnvOverrideDockerRegistry),
   413  		func(values *l5dcharts.Values, value string) error {
   414  			values.ControllerImage = cmd.RegistryOverride(values.ControllerImage, value)
   415  			values.PolicyController.Image.Name = cmd.RegistryOverride(values.PolicyController.Image.Name, value)
   416  			values.DebugContainer.Image.Name = cmd.RegistryOverride(values.DebugContainer.Image.Name, value)
   417  			values.Proxy.Image.Name = cmd.RegistryOverride(values.Proxy.Image.Name, value)
   418  			values.ProxyInit.Image.Name = cmd.RegistryOverride(values.ProxyInit.Image.Name, value)
   419  			return nil
   420  		})
   421  	if reg := os.Getenv(flagspkg.EnvOverrideDockerRegistry); reg != "" {
   422  		registryFlag.Set(reg)
   423  	}
   424  	flags = append(flags, registryFlag)
   425  
   426  	proxyFlags.MarkDeprecated("proxy-memory", "use --proxy-memory-request instead")
   427  	proxyFlags.MarkDeprecated("proxy-cpu", "use --proxy-cpu-request instead")
   428  	proxyFlags.MarkDeprecated("proxy-version", "use --set proxy.image.version=<version>")
   429  
   430  	// Hide developer focused flags in release builds.
   431  	release, err := version.IsReleaseChannel(version.Version)
   432  	if err != nil {
   433  		log.Errorf("Unable to parse version: %s", version.Version)
   434  	}
   435  	if release {
   436  		proxyFlags.MarkHidden("proxy-image")
   437  		proxyFlags.MarkHidden("proxy-version")
   438  		proxyFlags.MarkHidden("image-pull-policy")
   439  		proxyFlags.MarkHidden("init-image")
   440  		proxyFlags.MarkHidden("init-image-version")
   441  	}
   442  
   443  	return flags, proxyFlags
   444  }
   445  
   446  // makeInjectFlags builds the set of flags which are exclusive to the inject
   447  // command.  These flags configure the proxy but are not available to the
   448  // install and upgrade commands.  This is generally for proxy configuration
   449  // which is intended to be set on individual workloads rather than being
   450  // cluster wide.
   451  func makeInjectFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
   452  	injectFlags := pflag.NewFlagSet("inject", pflag.ExitOnError)
   453  
   454  	flags := []flag.Flag{
   455  		flag.NewBoolFlag(injectFlags, "native-sidecar", false, "Enable native sidecar",
   456  			func(values *l5dcharts.Values, value bool) error {
   457  				values.Proxy.NativeSidecar = value
   458  				return nil
   459  			}),
   460  
   461  		flag.NewInt64Flag(injectFlags, "wait-before-exit-seconds", int64(defaults.Proxy.WaitBeforeExitSeconds),
   462  			"The period during which the proxy sidecar must stay alive while its pod is terminating. "+
   463  				"Must be smaller than terminationGracePeriodSeconds for the pod (default 0)",
   464  			func(values *l5dcharts.Values, value int64) error {
   465  				values.Proxy.WaitBeforeExitSeconds = uint64(value)
   466  				return nil
   467  			}),
   468  
   469  		flag.NewBoolFlag(injectFlags, "disable-identity", false,
   470  			"Disables resources from participating in TLS identity", func(values *l5dcharts.Values, value bool) error {
   471  				return errors.New("--disable-identity is no longer supported; identity is always required")
   472  			}),
   473  
   474  		flag.NewStringSliceFlag(injectFlags, "require-identity-on-inbound-ports", strings.Split(defaults.Proxy.RequireIdentityOnInboundPorts, ","),
   475  			"Inbound ports on which the proxy should require identity", func(values *l5dcharts.Values, value []string) error {
   476  				values.Proxy.RequireIdentityOnInboundPorts = strings.Join(value, ",")
   477  				return nil
   478  			}),
   479  
   480  		flag.NewBoolFlag(injectFlags, "ingress", defaults.Proxy.IsIngress, "Enable ingress mode in the linkerd proxy",
   481  			func(values *l5dcharts.Values, value bool) error {
   482  				values.Proxy.IsIngress = value
   483  				return nil
   484  			}),
   485  
   486  		flag.NewStringSliceFlag(injectFlags, "opaque-ports", strings.Split(defaults.Proxy.OpaquePorts, ","),
   487  			"Set opaque ports on the proxy", func(values *l5dcharts.Values, value []string) error {
   488  				values.Proxy.OpaquePorts = strings.Join(value, ",")
   489  				return nil
   490  			}),
   491  	}
   492  	injectFlags.MarkHidden("disable-identity")
   493  
   494  	return flags, injectFlags
   495  }
   496  
   497  /* Validation */
   498  
   499  func validateValues(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
   500  	if !alphaNumDashDot.MatchString(values.LinkerdVersion) {
   501  		return fmt.Errorf("%s is not a valid version", values.LinkerdVersion)
   502  	}
   503  
   504  	if _, err := log.ParseLevel(values.ControllerLogLevel); err != nil {
   505  		return fmt.Errorf("--controller-log-level must be one of: panic, fatal, error, warn, info, debug, trace")
   506  	}
   507  
   508  	if values.Proxy.LogLevel == "" {
   509  		return errors.New("--proxy-log-level must not be empty")
   510  	}
   511  
   512  	if values.EnableEndpointSlices && k != nil {
   513  		err := k8s.EndpointSliceAccess(ctx, k)
   514  		if err != nil {
   515  			return err
   516  		}
   517  	}
   518  
   519  	// Validate only if its not empty
   520  	if values.IdentityTrustDomain != "" {
   521  		if errs := validation.IsDNS1123Subdomain(values.IdentityTrustDomain); len(errs) > 0 {
   522  			return fmt.Errorf("invalid trust domain '%s': %s", values.IdentityTrustDomain, errs[0])
   523  		}
   524  	}
   525  
   526  	err := validateProxyValues(values)
   527  	if err != nil {
   528  		return err
   529  	}
   530  
   531  	if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
   532  		if values.Identity.Issuer.TLS.CrtPEM != "" {
   533  			return errors.New("--identity-issuer-certificate-file must not be specified if --identity-external-issuer=true")
   534  		}
   535  		if values.Identity.Issuer.TLS.KeyPEM != "" {
   536  			return errors.New("--identity-issuer-key-file must not be specified if --identity-external-issuer=true")
   537  		}
   538  	}
   539  
   540  	if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) && k != nil {
   541  		externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
   542  		if err != nil {
   543  			return err
   544  		}
   545  		_, err = externalIssuerData.VerifyAndBuildCreds()
   546  		if err != nil {
   547  			return fmt.Errorf("failed to validate issuer credentials: %w", err)
   548  		}
   549  	}
   550  
   551  	if values.Identity.Issuer.Scheme == k8s.IdentityIssuerSchemeLinkerd {
   552  		issuerData := issuercerts.IssuerCertData{
   553  			IssuerCrt:    values.Identity.Issuer.TLS.CrtPEM,
   554  			IssuerKey:    values.Identity.Issuer.TLS.KeyPEM,
   555  			TrustAnchors: values.IdentityTrustAnchorsPEM,
   556  		}
   557  		_, err := issuerData.VerifyAndBuildCreds()
   558  		if err != nil {
   559  			return fmt.Errorf("failed to validate issuer credentials: %w", err)
   560  		}
   561  	}
   562  
   563  	if err != nil {
   564  		return err
   565  	}
   566  
   567  	return nil
   568  }
   569  
   570  func validateProxyValues(values *l5dcharts.Values) error {
   571  	networks := strings.Split(values.ClusterNetworks, ",")
   572  	for _, network := range networks {
   573  		if _, _, err := net.ParseCIDR(network); err != nil {
   574  			return fmt.Errorf("cannot parse destination get networks: %w", err)
   575  		}
   576  	}
   577  
   578  	if values.Proxy.Image.Version != "" && !alphaNumDashDot.MatchString(values.Proxy.Image.Version) {
   579  		return fmt.Errorf("%s is not a valid version", values.Proxy.Image.Version)
   580  	}
   581  
   582  	if !alphaNumDashDot.MatchString(values.ProxyInit.Image.Version) {
   583  		return fmt.Errorf("%s is not a valid version", values.ProxyInit.Image.Version)
   584  	}
   585  
   586  	if values.ImagePullPolicy != "Always" && values.ImagePullPolicy != "IfNotPresent" && values.ImagePullPolicy != "Never" {
   587  		return fmt.Errorf("--image-pull-policy must be one of: Always, IfNotPresent, Never")
   588  	}
   589  
   590  	if values.Proxy.Resources.CPU.Request != "" {
   591  		if _, err := k8sResource.ParseQuantity(values.Proxy.Resources.CPU.Request); err != nil {
   592  			return fmt.Errorf("Invalid cpu request '%s' for --proxy-cpu-request flag", values.Proxy.Resources.CPU.Request)
   593  		}
   594  	}
   595  
   596  	if values.Proxy.Resources.Memory.Request != "" {
   597  		if _, err := k8sResource.ParseQuantity(values.Proxy.Resources.Memory.Request); err != nil {
   598  			return fmt.Errorf("Invalid memory request '%s' for --proxy-memory-request flag", values.Proxy.Resources.Memory.Request)
   599  		}
   600  	}
   601  
   602  	if values.Proxy.Resources.CPU.Limit != "" {
   603  		cpuLimit, err := k8sResource.ParseQuantity(values.Proxy.Resources.CPU.Limit)
   604  		if err != nil {
   605  			return fmt.Errorf("Invalid cpu limit '%s' for --proxy-cpu-limit flag", values.Proxy.Resources.CPU.Limit)
   606  		}
   607  		// Not checking for error because option proxyCPURequest was already validated
   608  		if cpuRequest, _ := k8sResource.ParseQuantity(values.Proxy.Resources.CPU.Request); cpuRequest.MilliValue() > cpuLimit.MilliValue() {
   609  			return fmt.Errorf("The cpu limit '%s' cannot be lower than the cpu request '%s'", values.Proxy.Resources.CPU.Limit, values.Proxy.Resources.CPU.Request)
   610  		}
   611  	}
   612  
   613  	if values.Proxy.Resources.Memory.Limit != "" {
   614  		memoryLimit, err := k8sResource.ParseQuantity(values.Proxy.Resources.Memory.Limit)
   615  		if err != nil {
   616  			return fmt.Errorf("Invalid memory limit '%s' for --proxy-memory-limit flag", values.Proxy.Resources.Memory.Limit)
   617  		}
   618  		// Not checking for error because option proxyMemoryRequest was already validated
   619  		if memoryRequest, _ := k8sResource.ParseQuantity(values.Proxy.Resources.Memory.Request); memoryRequest.Value() > memoryLimit.Value() {
   620  			return fmt.Errorf("The memory limit '%s' cannot be lower than the memory request '%s'", values.Proxy.Resources.Memory.Limit, values.Proxy.Resources.Memory.Request)
   621  		}
   622  	}
   623  
   624  	if !validProxyLogLevel.MatchString(values.Proxy.LogLevel) {
   625  		return fmt.Errorf("\"%s\" is not a valid proxy log level - for allowed syntax check https://docs.rs/env_logger/0.6.0/env_logger/#enabling-logging",
   626  			values.Proxy.LogLevel)
   627  	}
   628  
   629  	if values.ProxyInit.IgnoreInboundPorts != "" {
   630  		if err := validateRangeSlice(strings.Split(values.ProxyInit.IgnoreInboundPorts, ",")); err != nil {
   631  			return err
   632  		}
   633  	}
   634  
   635  	if values.ProxyInit.IgnoreOutboundPorts != "" {
   636  		if err := validateRangeSlice(strings.Split(values.ProxyInit.IgnoreOutboundPorts, ",")); err != nil {
   637  			return err
   638  		}
   639  	}
   640  
   641  	if err := validatePolicy(values.Proxy.DefaultInboundPolicy); err != nil {
   642  		return err
   643  	}
   644  
   645  	return nil
   646  }
   647  
   648  func validatePolicy(policy string) error {
   649  	validPolicies := []string{"all-authenticated", "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny"}
   650  	for _, p := range validPolicies {
   651  		if p == policy {
   652  			return nil
   653  		}
   654  	}
   655  	return fmt.Errorf("--default-inbound-policy must be one of: %s (got %s)", strings.Join(validPolicies, ", "), policy)
   656  }
   657  
   658  // initializeIssuerCredentials populates the identity issuer TLS credentials.
   659  // If we are using an externally managed issuer secret, all we need to do here
   660  // is copy the trust root from the issuer secret.  Otherwise, if no credentials
   661  // have already been supplied, we generate them.
   662  func initializeIssuerCredentials(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
   663  	if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
   664  		// Using externally managed issuer credentials.  We need to copy the
   665  		// trust root.
   666  		if k == nil {
   667  			return errors.New("--ignore-cluster is not supported when --identity-external-issuer=true")
   668  		}
   669  		externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
   670  		if err != nil {
   671  			return err
   672  		}
   673  		values.IdentityTrustAnchorsPEM = externalIssuerData.TrustAnchors
   674  	} else if values.Identity.Issuer.TLS.CrtPEM != "" || values.Identity.Issuer.TLS.KeyPEM != "" || values.IdentityTrustAnchorsPEM != "" {
   675  		// If any credentials have already been supplied, check that they are
   676  		// all present.
   677  		if values.IdentityTrustAnchorsPEM == "" {
   678  			return errors.New("a trust anchors file must be specified if other credentials are provided")
   679  		}
   680  		if values.Identity.Issuer.TLS.CrtPEM == "" {
   681  			return errors.New("a certificate file must be specified if other credentials are provided")
   682  		}
   683  		if values.Identity.Issuer.TLS.KeyPEM == "" {
   684  			return errors.New("a private key file must be specified if other credentials are provided")
   685  		}
   686  	} else {
   687  		// No credentials have been supplied so we will generate them.
   688  		root, err := tls.GenerateRootCAWithDefaults(issuerName(values.IdentityTrustDomain))
   689  		if err != nil {
   690  			return fmt.Errorf("failed to generate root certificate for identity: %w", err)
   691  		}
   692  		values.Identity.Issuer.TLS.KeyPEM = root.Cred.EncodePrivateKeyPEM()
   693  		values.Identity.Issuer.TLS.CrtPEM = root.Cred.Crt.EncodeCertificatePEM()
   694  		values.IdentityTrustAnchorsPEM = root.Cred.Crt.EncodeCertificatePEM()
   695  	}
   696  	return nil
   697  }
   698  
   699  func issuerName(trustDomain string) string {
   700  	return fmt.Sprintf("identity.%s.%s", controlPlaneNamespace, trustDomain)
   701  }
   702  

View as plain text