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
31
32
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
60
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
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
218
219
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
258
259
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
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
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
447
448
449
450
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
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
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
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
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
659
660
661
662 func initializeIssuerCredentials(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
663 if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
664
665
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
676
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
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