mirror of https://github.com/linkerd/linkerd2.git
701 lines
28 KiB
Go
701 lines
28 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/linkerd/linkerd2/cli/flag"
|
|
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
|
|
"github.com/linkerd/linkerd2/pkg/issuercerts"
|
|
"github.com/linkerd/linkerd2/pkg/k8s"
|
|
consts "github.com/linkerd/linkerd2/pkg/k8s"
|
|
"github.com/linkerd/linkerd2/pkg/tls"
|
|
"github.com/linkerd/linkerd2/pkg/version"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/spf13/pflag"
|
|
corev1 "k8s.io/api/core/v1"
|
|
k8sResource "k8s.io/apimachinery/pkg/api/resource"
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
// makeInstallUpgradeFlags builds the set of flags which are used during the
|
|
// "control-plane" stage of install and upgrade. These flags control the
|
|
// majority of how the control plane is configured.
|
|
func makeInstallUpgradeFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet, error) {
|
|
installUpgradeFlags := pflag.NewFlagSet("install", pflag.ExitOnError)
|
|
|
|
issuanceLifetime, err := time.ParseDuration(defaults.Identity.Issuer.IssuanceLifetime)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
clockSkewAllowance, err := time.ParseDuration(defaults.Identity.Issuer.ClockSkewAllowance)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
flags := []flag.Flag{
|
|
flag.NewUintFlag(installUpgradeFlags, "controller-replicas", defaults.ControllerReplicas,
|
|
"Replicas of the controller to deploy", func(values *l5dcharts.Values, value uint) error {
|
|
values.ControllerReplicas = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installUpgradeFlags, "controller-log-level", defaults.Global.ControllerLogLevel,
|
|
"Log level for the controller and web components", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.ControllerLogLevel = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "ha", false, "Enable HA deployment config for the control plane (default false)",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.HighAvailability = value
|
|
if value {
|
|
haValues, err := l5dcharts.NewValues(true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// The HA flag must be processed before flags that set these values individually so that the
|
|
// individual flags can override the HA defaults. This means that the HA flag must appear
|
|
// before the individual flags in the slice passed to flags.ApplyIfSet.
|
|
values.ControllerReplicas = haValues.ControllerReplicas
|
|
values.Global.Proxy.Resources.CPU.Request = haValues.Global.Proxy.Resources.CPU.Request
|
|
values.Global.Proxy.Resources.Memory.Request = haValues.Global.Proxy.Resources.Memory.Request
|
|
values.Global.Proxy.Resources.CPU.Limit = haValues.Global.Proxy.Resources.CPU.Limit
|
|
values.Global.Proxy.Resources.Memory.Limit = haValues.Global.Proxy.Resources.Memory.Limit
|
|
}
|
|
return nil
|
|
}),
|
|
|
|
flag.NewInt64Flag(installUpgradeFlags, "controller-uid", defaults.ControllerUID,
|
|
"Run the control plane components under this user ID", func(values *l5dcharts.Values, value int64) error {
|
|
values.ControllerUID = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "disable-h2-upgrade", !defaults.EnableH2Upgrade,
|
|
"Prevents the controller from instructing proxies to perform transparent HTTP/2 upgrading (default false)",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.EnableH2Upgrade = !value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "disable-heartbeat", defaults.DisableHeartBeat,
|
|
"Disables the heartbeat cronjob (default false)", func(values *l5dcharts.Values, value bool) error {
|
|
values.DisableHeartBeat = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewDurationFlag(installUpgradeFlags, "identity-issuance-lifetime", issuanceLifetime,
|
|
"The amount of time for which the Identity issuer should certify identity",
|
|
func(values *l5dcharts.Values, value time.Duration) error {
|
|
values.Identity.Issuer.IssuanceLifetime = value.String()
|
|
return nil
|
|
}),
|
|
|
|
flag.NewDurationFlag(installUpgradeFlags, "identity-clock-skew-allowance", clockSkewAllowance,
|
|
"The amount of time to allow for clock skew within a Linkerd cluster",
|
|
func(values *l5dcharts.Values, value time.Duration) error {
|
|
values.Identity.Issuer.ClockSkewAllowance = value.String()
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "omit-webhook-side-effects", defaults.OmitWebhookSideEffects,
|
|
"Omit the sideEffects flag in the webhook manifests, This flag must be provided during install or upgrade for Kubernetes versions pre 1.12",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.OmitWebhookSideEffects = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "control-plane-tracing", defaults.Global.ControlPlaneTracing,
|
|
"Enables Control Plane Tracing with the defaults", func(values *l5dcharts.Values, value bool) error {
|
|
defaults.Global.ControlPlaneTracing = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installUpgradeFlags, "identity-issuer-certificate-file", "",
|
|
"A path to a PEM-encoded file containing the Linkerd Identity issuer certificate (generated by default)",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
if value != "" {
|
|
crt, err := loadCrtPEM(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values.Identity.Issuer.TLS.CrtPEM = crt
|
|
}
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installUpgradeFlags, "identity-issuer-key-file", "",
|
|
"A path to a PEM-encoded file containing the Linkerd Identity issuer private key (generated by default)",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
if value != "" {
|
|
key, err := loadKeyPEM(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values.Identity.Issuer.TLS.KeyPEM = key
|
|
}
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installUpgradeFlags, "identity-trust-anchors-file", "",
|
|
"A path to a PEM-encoded file containing Linkerd Identity trust anchors (generated by default)",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
if value != "" {
|
|
data, err := ioutil.ReadFile(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values.Global.IdentityTrustAnchorsPEM = string(data)
|
|
}
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installUpgradeFlags, "enable-endpoint-slices", defaults.Global.EnableEndpointSlices,
|
|
"Enables the usage of EndpointSlice informers and resources for destination service",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.EnableEndpointSlices = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installUpgradeFlags, "control-plane-version", defaults.ControllerImageVersion,
|
|
"Tag to be used for the control plane component images",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.ControllerImageVersion = value
|
|
values.Global.ControllerImageVersion = value
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
// Hide developer focused flags in release builds.
|
|
release, err := version.IsReleaseChannel(version.Version)
|
|
if err != nil {
|
|
log.Errorf("Unable to parse version: %s", version.Version)
|
|
}
|
|
if release {
|
|
installUpgradeFlags.MarkHidden("control-plane-version")
|
|
}
|
|
installUpgradeFlags.MarkHidden("control-plane-tracing")
|
|
|
|
return flags, installUpgradeFlags, nil
|
|
}
|
|
|
|
func loadCrtPEM(path string) (string, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
crt, err := tls.DecodePEMCrt(string(data))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return crt.EncodeCertificatePEM(), nil
|
|
}
|
|
|
|
func loadKeyPEM(path string) (string, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
key, err := tls.DecodePEMKey(string(data))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
cred := tls.Cred{PrivateKey: key}
|
|
return cred.EncodePrivateKeyPEM(), nil
|
|
}
|
|
|
|
// makeAllStageFlags builds the set of flags which are used during all stages
|
|
// of install and upgrade. These flags influence cluster level configuration
|
|
// and therefore are available during the "config" stage.
|
|
func makeAllStageFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
|
|
|
|
allStageFlags := pflag.NewFlagSet("all-stage", pflag.ExitOnError)
|
|
|
|
flags := []flag.Flag{
|
|
flag.NewBoolFlag(allStageFlags, "linkerd-cni-enabled", defaults.Global.CNIEnabled,
|
|
"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",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.CNIEnabled = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(allStageFlags, "restrict-dashboard-privileges", defaults.RestrictDashboardPrivileges,
|
|
"Restrict the Linkerd Dashboard's default privileges to disallow Tap and Check",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.RestrictDashboardPrivileges = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(allStageFlags, "config", "",
|
|
"A path to a yaml configuration file. The fields in this file will override the values used to install or upgrade Linkerd.",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
if value != "" {
|
|
data, err := ioutil.ReadFile(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = yaml.Unmarshal(data, &values)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
return flags, allStageFlags
|
|
}
|
|
|
|
// makeInstallFlags builds the set of flags which are used during the
|
|
// "control-plane" stage of install. These flags should not be changed during
|
|
// an upgrade and are not available to the upgrade command.
|
|
func makeInstallFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
|
|
|
|
installOnlyFlags := pflag.NewFlagSet("install-only", pflag.ExitOnError)
|
|
|
|
flags := []flag.Flag{
|
|
flag.NewStringFlag(installOnlyFlags, "cluster-domain", defaults.Global.ClusterDomain,
|
|
"Set custom cluster domain", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.ClusterDomain = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(installOnlyFlags, "identity-trust-domain", defaults.Global.IdentityTrustDomain,
|
|
"Configures the name suffix used for identities.", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.IdentityTrustDomain = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(installOnlyFlags, "identity-external-issuer", false,
|
|
"Whether to use an external identity issuer (default false)", func(values *l5dcharts.Values, value bool) error {
|
|
if value {
|
|
values.Identity.Issuer.Scheme = string(corev1.SecretTypeTLS)
|
|
} else {
|
|
values.Identity.Issuer.Scheme = consts.IdentityIssuerSchemeLinkerd
|
|
}
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
return flags, installOnlyFlags
|
|
}
|
|
|
|
// makeProxyFlags builds the set of flags which affect how the proxy is
|
|
// configured. These flags are available to the inject command and to the
|
|
// install and upgrade commands in the "control-plane" stage.
|
|
func makeProxyFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
|
|
|
|
proxyFlags := pflag.NewFlagSet("proxy", pflag.ExitOnError)
|
|
|
|
flags := []flag.Flag{
|
|
flag.NewStringFlagP(proxyFlags, "proxy-version", "v", defaults.Global.Proxy.Image.Version, "Tag to be used for the Linkerd proxy images",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Image.Version = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-image", defaults.Global.Proxy.Image.Name, "Linkerd proxy container image name",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Image.Name = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "init-image", defaults.Global.ProxyInit.Image.Name, "Linkerd init container image name",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.ProxyInit.Image.Name = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "init-image-version", defaults.Global.ProxyInit.Image.Version,
|
|
"Linkerd init container image version", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.ProxyInit.Image.Version = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "registry", defaultDockerRegistry, "Docker registry to pull images from",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.WebImage = registryOverride(values.WebImage, value)
|
|
values.ControllerImage = registryOverride(values.ControllerImage, value)
|
|
values.DebugContainer.Image.Name = registryOverride(values.DebugContainer.Image.Name, value)
|
|
values.Global.Proxy.Image.Name = registryOverride(values.Global.Proxy.Image.Name, value)
|
|
values.Global.ProxyInit.Image.Name = registryOverride(values.Global.ProxyInit.Image.Name, value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "image-pull-policy", defaults.Global.ImagePullPolicy,
|
|
"Docker image pull policy", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.ImagePullPolicy = value
|
|
values.Global.Proxy.Image.PullPolicy = value
|
|
values.Global.ProxyInit.Image.PullPolicy = value
|
|
values.DebugContainer.Image.PullPolicy = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewUintFlag(proxyFlags, "inbound-port", uint(defaults.Global.Proxy.Ports.Inbound),
|
|
"Proxy port to use for inbound traffic", func(values *l5dcharts.Values, value uint) error {
|
|
values.Global.Proxy.Ports.Inbound = int32(value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewUintFlag(proxyFlags, "outbound-port", uint(defaults.Global.Proxy.Ports.Outbound),
|
|
"Proxy port to use for outbound traffic", func(values *l5dcharts.Values, value uint) error {
|
|
values.Global.Proxy.Ports.Outbound = int32(value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringSliceFlag(proxyFlags, "skip-inbound-ports", nil, "Ports and/or port ranges (inclusive) that should skip the proxy and send directly to the application",
|
|
func(values *l5dcharts.Values, value []string) error {
|
|
values.Global.ProxyInit.IgnoreInboundPorts = strings.Join(value, ",")
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringSliceFlag(proxyFlags, "skip-outbound-ports", nil, "Outbound ports and/or port ranges (inclusive) that should skip the proxy",
|
|
func(values *l5dcharts.Values, value []string) error {
|
|
values.Global.ProxyInit.IgnoreOutboundPorts = strings.Join(value, ",")
|
|
return nil
|
|
}),
|
|
|
|
flag.NewInt64Flag(proxyFlags, "proxy-uid", defaults.Global.Proxy.UID, "Run the proxy under this user ID",
|
|
func(values *l5dcharts.Values, value int64) error {
|
|
values.Global.Proxy.UID = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-log-level", defaults.Global.Proxy.LogLevel, "Log level for the proxy",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.LogLevel = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewUintFlag(proxyFlags, "control-port", uint(defaults.Global.Proxy.Ports.Control), "Proxy port to use for control",
|
|
func(values *l5dcharts.Values, value uint) error {
|
|
values.Global.Proxy.Ports.Control = int32(value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewUintFlag(proxyFlags, "admin-port", uint(defaults.Global.Proxy.Ports.Admin), "Proxy port to serve metrics on",
|
|
func(values *l5dcharts.Values, value uint) error {
|
|
values.Global.Proxy.Ports.Admin = int32(value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-cpu-request", defaults.Global.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.CPU.Request = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-memory-request", defaults.Global.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.Memory.Request = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-cpu-limit", defaults.Global.Proxy.Resources.CPU.Limit, "Maximum amount of CPU units that the proxy sidecar can use",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.CPU.Limit = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-memory-limit", defaults.Global.Proxy.Resources.Memory.Limit, "Maximum amount of Memory that the proxy sidecar can use",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.Memory.Limit = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(proxyFlags, "enable-external-profiles", defaults.Global.Proxy.EnableExternalProfiles, "Enable service profiles for non-Kubernetes services",
|
|
func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.Proxy.EnableExternalProfiles = value
|
|
return nil
|
|
}),
|
|
|
|
// Deprecated flags
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-memory", defaults.Global.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.Memory.Request = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(proxyFlags, "proxy-cpu", defaults.Global.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
|
|
func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Resources.CPU.Request = value
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
proxyFlags.MarkDeprecated("proxy-memory", "use --proxy-memory-request instead")
|
|
proxyFlags.MarkDeprecated("proxy-cpu", "use --proxy-cpu-request instead")
|
|
|
|
// Hide developer focused flags in release builds.
|
|
release, err := version.IsReleaseChannel(version.Version)
|
|
if err != nil {
|
|
log.Errorf("Unable to parse version: %s", version.Version)
|
|
}
|
|
if release {
|
|
proxyFlags.MarkHidden("proxy-image")
|
|
proxyFlags.MarkHidden("proxy-version")
|
|
proxyFlags.MarkHidden("image-pull-policy")
|
|
proxyFlags.MarkHidden("init-image")
|
|
proxyFlags.MarkHidden("init-image-version")
|
|
}
|
|
|
|
return flags, proxyFlags
|
|
}
|
|
|
|
// makeInjectFlags builds the set of flags which are exclusive to the inject
|
|
// command. These flags configure the proxy but are not available to the
|
|
// install and upgrade commands. This is generally for proxy configuration
|
|
// which is intended to be set on individual workloads rather than being
|
|
// cluster wide.
|
|
func makeInjectFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
|
|
injectFlags := pflag.NewFlagSet("inject", pflag.ExitOnError)
|
|
|
|
flags := []flag.Flag{
|
|
flag.NewInt64Flag(injectFlags, "wait-before-exit-seconds", int64(defaults.Global.Proxy.WaitBeforeExitSeconds),
|
|
"The period during which the proxy sidecar must stay alive while its pod is terminating. "+
|
|
"Must be smaller than terminationGracePeriodSeconds for the pod (default 0)",
|
|
func(values *l5dcharts.Values, value int64) error {
|
|
values.Global.Proxy.WaitBeforeExitSeconds = uint64(value)
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(injectFlags, "disable-identity", defaults.Global.Proxy.DisableIdentity,
|
|
"Disables resources from participating in TLS identity", func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.Proxy.DisableIdentity = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewBoolFlag(injectFlags, "disable-tap", defaults.Global.Proxy.DisableTap,
|
|
"Disables resources from being tapped", func(values *l5dcharts.Values, value bool) error {
|
|
values.Global.Proxy.DisableTap = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(injectFlags, "trace-collector", defaults.Global.Proxy.Trace.CollectorSvcAddr,
|
|
"Collector Service address for the proxies to send Trace Data", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Trace.CollectorSvcAddr = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringFlag(injectFlags, "trace-collector-svc-account", defaults.Global.Proxy.Trace.CollectorSvcAccount,
|
|
"Service account associated with the Trace collector instance", func(values *l5dcharts.Values, value string) error {
|
|
values.Global.Proxy.Trace.CollectorSvcAccount = value
|
|
return nil
|
|
}),
|
|
|
|
flag.NewStringSliceFlag(injectFlags, "require-identity-on-inbound-ports", strings.Split(defaults.Global.Proxy.RequireIdentityOnInboundPorts, ","),
|
|
"Inbound ports on which the proxy should require identity", func(values *l5dcharts.Values, value []string) error {
|
|
values.Global.Proxy.RequireIdentityOnInboundPorts = strings.Join(value, ",")
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
return flags, injectFlags
|
|
}
|
|
|
|
/* Validation */
|
|
|
|
func validateValues(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
|
|
if !alphaNumDashDot.MatchString(values.ControllerImageVersion) {
|
|
return fmt.Errorf("%s is not a valid version", values.ControllerImageVersion)
|
|
}
|
|
|
|
if _, err := log.ParseLevel(values.Global.ControllerLogLevel); err != nil {
|
|
return fmt.Errorf("--controller-log-level must be one of: panic, fatal, error, warn, info, debug")
|
|
}
|
|
|
|
if values.Global.Proxy.LogLevel == "" {
|
|
return errors.New("--proxy-log-level must not be empty")
|
|
}
|
|
|
|
if values.Global.EnableEndpointSlices && k != nil {
|
|
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = k8s.EndpointSliceAccess(ctx, k8sAPI)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if errs := validation.IsDNS1123Subdomain(values.Global.IdentityTrustDomain); len(errs) > 0 {
|
|
return fmt.Errorf("invalid trust domain '%s': %s", values.Global.IdentityTrustDomain, errs[0])
|
|
}
|
|
|
|
err := validateProxyValues(values)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
|
|
if values.Identity.Issuer.TLS.CrtPEM != "" {
|
|
return errors.New("--identity-issuer-certificate-file must not be specified if --identity-external-issuer=true")
|
|
}
|
|
if values.Identity.Issuer.TLS.KeyPEM != "" {
|
|
return errors.New("--identity-issuer-key-file must not be specified if --identity-external-issuer=true")
|
|
}
|
|
}
|
|
|
|
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) && k != nil {
|
|
externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = externalIssuerData.VerifyAndBuildCreds(issuerName(values.Global.IdentityTrustDomain))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to validate issuer credentials: %s", err)
|
|
}
|
|
}
|
|
|
|
if values.Identity.Issuer.Scheme == consts.IdentityIssuerSchemeLinkerd {
|
|
issuerData := issuercerts.IssuerCertData{
|
|
IssuerCrt: values.Identity.Issuer.TLS.CrtPEM,
|
|
IssuerKey: values.Identity.Issuer.TLS.KeyPEM,
|
|
TrustAnchors: values.Global.IdentityTrustAnchorsPEM,
|
|
}
|
|
_, err := issuerData.VerifyAndBuildCreds(issuerName(values.Global.IdentityTrustDomain))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to validate issuer credentials: %s", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateProxyValues(values *l5dcharts.Values) error {
|
|
networks := strings.Split(values.Global.ClusterNetworks, ",")
|
|
for _, network := range networks {
|
|
if _, _, err := net.ParseCIDR(network); err != nil {
|
|
return fmt.Errorf("cannot parse destination get networks: %s", err)
|
|
}
|
|
}
|
|
|
|
if values.Global.Proxy.DisableIdentity && len(values.Global.Proxy.RequireIdentityOnInboundPorts) > 0 {
|
|
return errors.New("Identity must be enabled when --require-identity-on-inbound-ports is specified")
|
|
}
|
|
|
|
if values.Global.Proxy.Image.Version != "" && !alphaNumDashDot.MatchString(values.Global.Proxy.Image.Version) {
|
|
return fmt.Errorf("%s is not a valid version", values.Global.Proxy.Image.Version)
|
|
}
|
|
|
|
if !alphaNumDashDot.MatchString(values.Global.ProxyInit.Image.Version) {
|
|
return fmt.Errorf("%s is not a valid version", values.Global.ProxyInit.Image.Version)
|
|
}
|
|
|
|
if values.Global.ImagePullPolicy != "Always" && values.Global.ImagePullPolicy != "IfNotPresent" && values.Global.ImagePullPolicy != "Never" {
|
|
return fmt.Errorf("--image-pull-policy must be one of: Always, IfNotPresent, Never")
|
|
}
|
|
|
|
if values.Global.Proxy.Resources.CPU.Request != "" {
|
|
if _, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Request); err != nil {
|
|
return fmt.Errorf("Invalid cpu request '%s' for --proxy-cpu-request flag", values.Global.Proxy.Resources.CPU.Request)
|
|
}
|
|
}
|
|
|
|
if values.Global.Proxy.Resources.Memory.Request != "" {
|
|
if _, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Request); err != nil {
|
|
return fmt.Errorf("Invalid memory request '%s' for --proxy-memory-request flag", values.Global.Proxy.Resources.Memory.Request)
|
|
}
|
|
}
|
|
|
|
if values.Global.Proxy.Resources.CPU.Limit != "" {
|
|
cpuLimit, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Limit)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid cpu limit '%s' for --proxy-cpu-limit flag", values.Global.Proxy.Resources.CPU.Limit)
|
|
}
|
|
// Not checking for error because option proxyCPURequest was already validated
|
|
if cpuRequest, _ := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Request); cpuRequest.MilliValue() > cpuLimit.MilliValue() {
|
|
return fmt.Errorf("The cpu limit '%s' cannot be lower than the cpu request '%s'", values.Global.Proxy.Resources.CPU.Limit, values.Global.Proxy.Resources.CPU.Request)
|
|
}
|
|
}
|
|
|
|
if values.Global.Proxy.Resources.Memory.Limit != "" {
|
|
memoryLimit, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Limit)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid memory limit '%s' for --proxy-memory-limit flag", values.Global.Proxy.Resources.Memory.Limit)
|
|
}
|
|
// Not checking for error because option proxyMemoryRequest was already validated
|
|
if memoryRequest, _ := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Request); memoryRequest.Value() > memoryLimit.Value() {
|
|
return fmt.Errorf("The memory limit '%s' cannot be lower than the memory request '%s'", values.Global.Proxy.Resources.Memory.Limit, values.Global.Proxy.Resources.Memory.Request)
|
|
}
|
|
}
|
|
|
|
if !validProxyLogLevel.MatchString(values.Global.Proxy.LogLevel) {
|
|
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",
|
|
values.Global.Proxy.LogLevel)
|
|
}
|
|
|
|
if values.Global.ProxyInit.IgnoreInboundPorts != "" {
|
|
if err := validateRangeSlice(strings.Split(values.Global.ProxyInit.IgnoreInboundPorts, ",")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if values.Global.ProxyInit.IgnoreOutboundPorts != "" {
|
|
if err := validateRangeSlice(strings.Split(values.Global.ProxyInit.IgnoreOutboundPorts, ",")); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// initializeIssuerCredentials populates the identity issuer TLS credentials.
|
|
// If we are using an externally managed issuer secret, all we need to do here
|
|
// is copy the trust root from the issuer secret. Otherwise, if no credentials
|
|
// have already been supplied, we generate them.
|
|
func initializeIssuerCredentials(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
|
|
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
|
|
// Using externally managed issuer credentials. We need to copy the
|
|
// trust root.
|
|
if k == nil {
|
|
return errors.New("--ignore-cluster is not supported when --identity-external-issuer=true")
|
|
}
|
|
externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values.Global.IdentityTrustAnchorsPEM = externalIssuerData.TrustAnchors
|
|
} else if values.Identity.Issuer.TLS.CrtPEM != "" || values.Identity.Issuer.TLS.KeyPEM != "" || values.Global.IdentityTrustAnchorsPEM != "" {
|
|
// If any credentials have already been supplied, check that they are
|
|
// all present.
|
|
if values.Global.IdentityTrustAnchorsPEM == "" {
|
|
return errors.New("a trust anchors file must be specified if other credentials are provided")
|
|
}
|
|
if values.Identity.Issuer.TLS.CrtPEM == "" {
|
|
return errors.New("a certificate file must be specified if other credentials are provided")
|
|
}
|
|
if values.Identity.Issuer.TLS.KeyPEM == "" {
|
|
return errors.New("a private key file must be specified if other credentials are provided")
|
|
}
|
|
} else {
|
|
// No credentials have been supplied so we will generate them.
|
|
root, err := tls.GenerateRootCAWithDefaults(issuerName(values.Global.IdentityTrustDomain))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate root certificate for identity: %s", err)
|
|
}
|
|
values.Identity.Issuer.CrtExpiry = root.Cred.Crt.Certificate.NotAfter
|
|
values.Identity.Issuer.TLS.KeyPEM = root.Cred.EncodePrivateKeyPEM()
|
|
values.Identity.Issuer.TLS.CrtPEM = root.Cred.Crt.EncodeCertificatePEM()
|
|
values.Global.IdentityTrustAnchorsPEM = root.Cred.Crt.EncodeCertificatePEM()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func issuerName(trustDomain string) string {
|
|
return fmt.Sprintf("identity.%s.%s", controlPlaneNamespace, trustDomain)
|
|
}
|